pax_global_header00006660000000000000000000000064146264046140014521gustar00rootroot0000000000000052 comment=4d16d8bd8367d92c1b5725d3dc5cfe78dce86285 pvpgn-1.99.7.2.1+dfsg/000077500000000000000000000000001462640461400142005ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/.gitignore000066400000000000000000000004631462640461400161730ustar00rootroot00000000000000################################################################################ # This .gitignore file was automatically created by Microsoft(R) Visual Studio. ################################################################################ /zlib /cmake/Modules /build /lua5.1 /.vs /CMakeSettings.json pvpgn-1.99.7.2.1+dfsg/.travis.yml000066400000000000000000000022711462640461400163130ustar00rootroot00000000000000sudo: required dist: trusty language: cpp os: linux compiler: gcc branches: only: - master - coverity_scan env: global: # COVERITY_SCAN_TOKEN - secure: "U50+BcF3DczR+srgLGoMJ53+IRU+tlg/eW0XfNL3dxCp0FzZqMaM7FpENEaiqIOc/YQ2/x40aAufcHBbevY8qeWms36YtryRlQ7t8kWhFNmGNH6qalwROQWaWd3BcyZds3JIEq3qdbZTsCZ5S0up3v5BAsBjRSThlom9/o4jnWA=" addons: apt: sources: - ubuntu-toolchain-r-test - sourceline: 'ppa:george-edison55/cmake-3.x' packages: - gcc-5 - g++-5 - zlib1g-dev - build-essential - libmysqlclient-dev - liblua5.1-0-dev - cmake coverity_scan: project: name: pvpgn/pvpgn-server version: 1.99.7.1.1 description: Player vs Player Gaming Network (PvPGN) build_command_prepend: mkdir build && cd build && cmake -D WITH_MYSQL=true -D WITH_LUA=true ../ build_command: make branch_pattern: coverity_scan before_install: - export CXX="g++-5" CC="gcc-5" before_script: - mkdir build - cd build - cmake -D WITH_MYSQL=true -D WITH_LUA=true ../ script: - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then make; fi after_script: - sudo make install - sudo make uninstall pvpgn-1.99.7.2.1+dfsg/CMakeLists.txt000066400000000000000000000057221462640461400167460ustar00rootroot00000000000000# Required cmake version cmake_minimum_required(VERSION 3.1.0) # Put the include dirs which are in the source or build tree # before all other include dirs, so the headers in the sources # are prefered over the already installed ones # since cmake 2.4.1 set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) project(pvpgn CXX) option(WITH_BNETD "compile the bnetd target" ON) option(WITH_D2CS "compile the d2cs target" ON) option(WITH_D2DBS "compile the d2dbs target" ON) option(WITH_LUA "enable Lua support" OFF) if(WIN32) option(WITH_WIN32_GUI "enable GUI building (default on)" ON) endif(WIN32) #storage backends flags option(WITH_MYSQL "include MySQL user accounts support" OFF) option(WITH_SQLITE3 "include SQLite3 user accounts support" OFF) option(WITH_PGSQL "include PostgreSQL user accounts support" OFF) option(WITH_ODBC "include ODBC user accounts support" OFF) include(ConfigureChecks.cmake) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # using Clang set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3 -march=native -mtune=native -pedantic -stdlib=libc++") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # using G++ if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1) message(FATAL_ERROR "G++ 5.1 or higher required") endif() set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3 -march=native -mtune=native -pedantic -Wno-variadic-macros" ) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # using Visual Studio if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0) message(FATAL_ERROR "Visual Studio 2015 or higher required") endif() # DEBUG compiler flags: # /Zi create debugging information PDB file # /Od disable optimizations # /Oy- do not suppress frame pointers (recommended for debugging) # /MTd use statically linked, thread-safe, debug CRT libs (Magic Builder set this flag when build) # # RELEASE compiler flags: # /MT use statically linked, thread-safe CRT libs (Magic Builder set this flag when build) # /GS- no Buffer Security Check # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zi /Od /Oy-") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") # Explaining of linker flags and why enable pdb with debug info for Release build is on: # https://www.wintellect.com/correctly-creating-native-c-release-build-pdbs set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") endif() subdirs(src conf man files) if(WITH_LUA) add_subdirectory(lua) endif(WITH_LUA) ENABLE_TESTING() # uninstall target configure_file( "${CMAKE_MODULE_PATH}/cmake_uninstall.cmake.in" "${CMAKE_MODULE_PATH}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_MODULE_PATH}/cmake_uninstall.cmake) # purge target configure_file( "${CMAKE_MODULE_PATH}/cmake_purge.cmake.in" "${CMAKE_MODULE_PATH}/cmake_purge.cmake" IMMEDIATE @ONLY) add_custom_target(purge COMMAND ${CMAKE_COMMAND} -P ${CMAKE_MODULE_PATH}/cmake_purge.cmake) pvpgn-1.99.7.2.1+dfsg/CREDITS000066400000000000000000000241361462640461400152260ustar00rootroot00000000000000PvPGN CREDITS (also please see the original BNETD credits bellow): -------------------------------------------------------------------- CREDITS This file contains the names of people who have contributed to the development of the PvPGN BNETD mod in some way. This file is not complete and we would like to thank everyone who helped us in any way. PROGRAMMING CONTRIBUTORS AmAdEuS a.k.a. amadeo WIN32GUI-mod, WIN32-Installer, WIN32 services code, initial IRC mods Andreas Pickart a.k.a. apic (apic@lehm.name) - initial War3 work, w3route and anongame support, initial MySQL storage support and others Dizzy Project release engineering, general code maintenance, storage development, fdwatch development, some documentation and lots others. Bryan Biedenkapp (gatekeep@gmail.com) Westwood Online (WOL) support. Lot's of IRC fixes and enhancements. pandaemonium a.k.a. aaron W3 ladders, binary ladders, commands aliases, channel topics, channel tmp ops/voices, anongame infos language support, SQL db creator, bnchat cleanup, WIN32 build files maintenance and lots others. Pelish (pelish@gmail.com) WOL/IRC rework and many bugfixes Soar Clans, friends codes optimization, D2 stuff, w3 gameinfo etc. TheUndying aka Undy (kainofchaos80@hotmail.com) PvPGN project starter, initial code maintainance and management, initial W3 support codes, etc. Zzzoom (servers@3dgames.com.ar) For persistent SQL connections patch, password brute force attacks protection, various bug reports and very usefull profile dumps. MISC Eggy (eggy@neostats.net) Win32 binary packager, QA testing/reporting, supporter (IRC, forums), misc documentation. U-238 (mark@darkterrorsdomain.cjb.net) Webmaster, Mac binary packager, Mac versioncheck maintainer, PvPGN fink package maintainer, supporter (IRC, forums), tester, documentation writer. David Wilson (dgwilson@paradise.net.nz) Documentation writer, supporter (IRC, forums). Lots of other people who do not wish to be mentioned here. BINARY PACKAGES: - Birzan George Cristian (original Debian packager) - Dorin Lazar (RPMs) - Mihai Maties (RPMs & src.rpm snapshot script) - Radu Radoveneanu (Slackware packages) - Robert Millan (official Debian package maintainer) - Max E. Kuznecov (FreeBSD port entry maintainer) ORIGINAL BNETD CREDITS: ------------------------------- CREDITS This file contains the names of people who have contributed to the development of the BNETD server in some way. This project has made an enormous amount of progress since the development has become public, and this is one place where members of the community can be thanked. PROGRAMMING CONTRIBUTORS The following people have contributed code, code ideas, bug fixes, and debugging work: Mark Baysinger (mbaysing@ucsd.edu) For the original idea, original implementation, documentation of the protocol, starhack.ml.org, the tracking server, etc. Ross Combs (rocombs@cs.nmsu.edu) Various cleanups, release coordination, / commands, config files, etc. Rob Crittenden (rcrit@greyoak.com) For many bug fixes, protocol work, file downloads, man pages, user icons, better channel handling, autoconf, ladder, etc. as well as working on the FAQ and user support. Descolada (dyn1-tnt9-237.chicago.il.ameritech.net) For the hashing algorithm complete with a sample C implementation. Gediminas (gediminas_lt@mailexcite.com) For work on the client command "readline" code, the initial help system, and a better /conn command. Oleg Drokin (green@ccssu.crimea.ua) For patches to fix bnchat and bnstat. Damien Clermonte (clermond@esiee.fr) For portability (to cygnus B20.1 on win32) work, testing, and helpful observations, and the TCP keepalive patch. Denis Moreaux (vapula@endor.be) For working out the correct ladder equations. Philippe Dubois (pdubois@matrox.com) For working on the Win32 port, UDP stuff, auto-update, and the bnettime code. Typhoon [Marco Ziech] (mmz@gmx.net) For lots of good work on the BNI utilities, BITS (server inter- connectivity) support, and his bnetmasq.sh script. He also answers a lot of user questions and put together most of the web site. Dizzy For help with NAT issues, a much improved help system, channel flooding prevention, new /-commands, a mail system, and more. Razvan Jianu (roger@hell.gameover.ro) For many useful coredumps :) Also for the /addacct command. Qlex (qlex@eoliss.com) For kick starting the Diablo II stuff, testing thousands quick changes, moral support, and digging up all the information. Kenshin (kenshin_@hotmail.com) For helping Qlex with the character stuff, fixing some major bugs in the Diablo II code, and starting work on saving characters. Hakan Tandogan (hakan@gurkensalat.com) For always making RPMs for bnetd and the tracker and some patches to correct startup/shutdown issues. Also for continuing Diablo II work. Jung-woo Sohn (logout@aeroguy.snu.ac.kr) For /watch feedback, dobae flooding disconnect, better disconnect game result handling, and generally for lots of good ideas. And also for spotting the failed account creation crashing bug. YobGuls (yobguls@yobguls.2ndmail.com) Actually contributed to the DSBot code, but see next entry. Eurijk! (wc2@zefga.net) For pointing out how the new cdkey hashing works and also how version checking is performed. This was some really ugly but important stuff! He figured this out working on DSBot but kindly explained it to us and showed us example code. He's also working on the Prolix bot project and has provided lots of useful bug reports and other commentary. Fred Wittekind (rom@twister.dyndns.org) For autoupdate version range support. For pointing out all the update MPQ files we didn't know about. Also for the message force-command patch. Mathieu Legare (Mathieu_Legare@uqtr.uquebec.ca) For picking up bnetdStats and making it report all kinds of statistics. Onlyer (onlyer@users.sourceforge.net) For new commands, Diablo II protocol work, Diablo II closed server work, FSGS account conversion, etc. faster (lqx@cic.tsinghua.edu.cn) For Diablo II closed communication (S2S) and for finding that check_alloc bug. sousou (liupeng.cs@263.net) For Diablo II closed database server and ladder. esper (esperpale@263.net) Diablo II closed server gui and ladder. sherpya (sherpya@netfarm.it) For lots of help with adbanners, the d2close merge, reporting bugs (like the 100% poll() CPU usage bug), and lots of good suggestions. Also for work on channel list reloads. Yoss (bartek@milc.com.pl) For helpful suggestions for the mail code and for patching the emote flooding hole. Also for the /admin command and for the patch to fix the "operator can ban admins" bug. A big thanks for much improved kill and ban commands and the new dynamic ipban with better address matching and timeouts. Roland Stahn (blue@cs.tu-berlin.de) For all the work tracking down the versioncheck information for revision of the STAR and SEXP clients. For information about Blizzard's numbering scheme. For helpful packet information. Also for the bug reports and simple patches which probably should have been fixed a long time ago but he was the only person with enough patience to figure out what was happening. Specifically, the "all countries in one channel" feature, the empty game pass bug, the multiple game with same name bug, and the memory corruption after autoupdates. For updated version information. For diagnosing the autoupdate crash (bug in server.c handling closed sockets). KWS (vizi@csucs.net) For the nice bug reports on gameinfo parsing on game join, win32 socket and file portability problems. Also for patches to update versioncheck info, implement IP storage, the patch for better eventlogs, and Makefile improvements. Jack (jack@takeovertheworld.org) For Diablo II open game difficulty patch. forester (laterk@inbox.lv) Working on the Win32 port, bnetd GUI for Win32, small patches. TESTING AND DEBUGGING CONTRIBUTORS The following people have sent in ideas, bug reports, and minor patches. 187 (187@infoceptor.com) For bug reports and the idea for the /news command. Shin DongJun (djshin@oslab.snu.ac.kr) For some bug reports and patches. Jeffery Jones (jeffery_jones@mtlusa.com) For the game address translation idea. Maxim Tereshenkov (max@ostin.ru) For the stack trace which found the 8-bit account.c bug. Yanoch (yanoch@loginnovation.com) For suggesting the /kill improvement. stacker (stacker@mail.ee.ntou.edu.tw) For lots of bug reports and also the sbnetd front end. Mike Hindman (mhindman@bnetd.nmsu.edu) David Fitzpatrick (dfitzpat@bnetd.nmsu.edu) For lots of ideas and all the help with Diablo. Homer (homer@ogn.de) For the helpful info on game info strings and ladder maps. Alvin Sim (elflord@faerie.cx) For lots of testing and figuring out that Diablo II open gameplay was actually not too hard to implement. AsmodeusB For finding annoying the gameinfo bug in connection.c :) forester (http://forester.yo.lv) For pointing out what gameresult 5 means. jerome (imeepmeep@mac.com) For the MacOS X package. acbcdde (acbcdde@hotmail.com) For Win32 Makefile and config.h fixes. mojohead ( Andrey Ilyin ) For d2dbs/d2ladder.c fixes. OTHER CONTRIBUTORS The following people have contributed their time, talent, and other resources to the project: Josh (josh@bnetd.org) For a wonderful web site, quick mirroring of new releases, testing, organization, documentation, and user support. Tim Jung (tjung@igateway.net) For kindly hosting our mailing lists, domain, web pages, etc. as well as regular feedback and testing. Allan Clark (allanc@sco.com) For putting together some nice RPMs. NOTE This list is not complete. I have lost track of the names of some people who have contributed, and likely forgotten about others. If you think you should be listed here but aren't, please contact the bnetd-dev mailing list and we will take care of it right away. pvpgn-1.99.7.2.1+dfsg/ConfigureChecks.cmake000066400000000000000000000165271462640461400202570ustar00rootroot00000000000000# put in this file everything that needs to be setup depending # on the target architecture # our own modules set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules ) # include used modules include(DefineInstallationPaths) include(CheckIncludeFileCXX) include(CheckFunctionExists) include(CheckSymbolExists) include(CheckLibraryExists) include(CheckCXXCompilerFlag) include(CheckCXXSourceCompiles) include(CheckMkdirArgs) include(CheckIncludeFiles) # setup short variable path names set(BINDIR ${BIN_INSTALL_DIR}) set(SBINDIR ${SBIN_INSTALL_DIR}) set(SYSCONFDIR ${SYSCONF_INSTALL_DIR}) set(LOCALSTATEDIR ${LOCALSTATE_INSTALL_DIR}) set(MANDIR ${MAN_INSTALL_DIR}) # set default hardcoded config paths if(WIN32) set(BNETD_DEFAULT_CONF_FILE "conf/bnetd.conf") set(D2CS_DEFAULT_CONF_FILE "conf/d2cs.conf") set(D2DBS_DEFAULT_CONF_FILE "conf/d2dbs.conf") else(WIN32) set(BNETD_DEFAULT_CONF_FILE "${SYSCONFDIR}/bnetd.conf") set(D2CS_DEFAULT_CONF_FILE "${SYSCONFDIR}/d2cs.conf") set(D2DBS_DEFAULT_CONF_FILE "${SYSCONFDIR}/d2dbs.conf") endif(WIN32) enable_language(C) # library checks find_package(ZLIB REQUIRED) check_library_exists(nsl gethostbyname "" HAVE_LIBNSL) check_library_exists(socket socket "" HAVE_LIBSOCKET) check_library_exists(resolv inet_aton "" HAVE_LIBRESOLV) check_library_exists(bind __inet_aton "" HAVE_LIBBIND) if(WITH_LUA) find_package(Lua REQUIRED) endif(WITH_LUA) # storage module checks if(WITH_ODBC) find_package(ODBC REQUIRED) endif(WITH_ODBC) if(WITH_MYSQL) find_package(MySQL REQUIRED) endif(WITH_MYSQL) if(WITH_SQLITE3) find_package(SQLite3 REQUIRED) endif(WITH_SQLITE3) if(WITH_PGSQL) find_package(PostgreSQL REQUIRED) endif(WITH_PGSQL) # if any of nsl or socket exists we need to make sure the following tests # use them otherwise some functions may not be found if(HAVE_LIBNSL) SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} nsl) SET(NETWORK_LIBRARIES ${NETWORK_LIBRARIES} nsl) endif(HAVE_LIBNSL) if(HAVE_LIBSOCKET) SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} socket) SET(NETWORK_LIBRARIES ${NETWORK_LIBRARIES} socket) endif(HAVE_LIBSOCKET) if(HAVE_LIBRESOLV) SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} resolv) SET(NETWORK_LIBRARIES ${NETWORK_LIBRARIES} resolv) endif(HAVE_LIBRESOLV) if(HAVE_LIBBIND) # this is used for BeOS BONE, when someone will want # to test pvpgn with cmake on BeOS please contact us SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} bind) SET(NETWORK_LIBRARIES ${NETWORK_LIBRARIES} bind) endif(HAVE_LIBBIND) # for win32 unconditionally add network library linking to "ws2_32" if(WIN32) SET(NETWORK_LIBRARIES ${NETWORK_LIBRARIES} ws2_32) endif(WIN32) message(STATUS "Checking POSIX headers") check_include_file_cxx(arpa/inet.h HAVE_ARPA_INET_H) check_include_file_cxx(dirent.h HAVE_DIRENT_H) check_include_file_cxx(grp.h HAVE_GRP_H) check_include_file_cxx(fcntl.h HAVE_FCNTL_H) check_include_file_cxx(netdb.h HAVE_NETDB_H) check_include_file_cxx(netinet/in.h HAVE_NETINET_IN_H) check_include_file_cxx(poll.h HAVE_POLL_H) check_include_file_cxx(pwd.h HAVE_PWD_H) check_include_file_cxx(sys/mman.h HAVE_SYS_MMAN_H) check_include_file_cxx(sys/resource.h HAVE_SYS_RESOURCE_H) check_include_file_cxx(sys/select.h HAVE_SYS_SELECT_H) check_include_file_cxx(sys/socket.h HAVE_SYS_SOCKET_H) check_include_file_cxx(sys/stat.h HAVE_SYS_STAT_H) check_include_file_cxx(sys/time.h HAVE_SYS_TIME_H) check_include_file_cxx(sys/types.h HAVE_SYS_TYPES_H) check_include_file_cxx(sys/utsname.h HAVE_SYS_UTSNAME_H) check_include_file_cxx(sys/wait.h HAVE_SYS_WAIT_H) check_include_file_cxx(termios.h HAVE_TERMIOS_H) check_include_file_cxx(unistd.h HAVE_UNISTD_H) message(STATUS "Checking optional POSIX/required SUS headers") check_include_file_cxx(sys/timeb.h HAVE_SYS_TIMEB_H) message(STATUS "Checking FreeBSD-based headers") check_include_files("sys/types.h;sys/event.h" HAVE_SYS_EVENT_H) check_include_file_cxx(sys/param.h HAVE_SYS_PARAM_H) message(STATUS "Checking BSD headers") check_include_file_cxx(sys/file.h HAVE_SYS_FILE_H) message(STATUS "Checking Linux headers") check_include_file_cxx(sys/epoll.h HAVE_SYS_EPOLL_H) message(STATUS "Checking Win32 headers") check_include_file_cxx(windows.h HAVE_WINDOWS_H) check_include_file_cxx(winsock2.h HAVE_WINSOCK2_H) check_include_file_cxx(ws2tcpip.h HAVE_WS2TCPIP_H) check_include_file_cxx(process.h HAVE_PROCESS_H) message(STATUS "Checking other headers") check_include_file_cxx(dir.h HAVE_DIR_H) check_include_file_cxx(direct.h HAVE_DIRECT_H) check_include_file_cxx(ndir.h HAVE_NDIR_H) check_include_file_cxx(sys/dir.h HAVE_SYS_DIR_H) check_include_file_cxx(sys/ndir.h HAVE_SYS_NDIR_H) check_include_file_cxx(sys/poll.h HAVE_SYS_POLL_H) check_function_exists(chdir HAVE_CHDIR) check_function_exists(epoll_create HAVE_EPOLL_CREATE) check_function_exists(fork HAVE_FORK) check_function_exists(ftime HAVE_FTIME) check_function_exists(getgid HAVE_GETGID) check_function_exists(getgrnam HAVE_GETGRNAM) check_function_exists(getlogin HAVE_GETLOGIN) check_function_exists(getopt HAVE_GETOPT) check_function_exists(getpid HAVE_GETPID) check_function_exists(getpwnam HAVE_GETPWNAME) check_function_exists(getrlimit HAVE_GETRLIMIT) check_function_exists(gettimeofday HAVE_GETTIMEOFDAY) check_function_exists(getuid HAVE_GETUID) check_function_exists(ioctl HAVE_IOCTL) check_function_exists(kqueue HAVE_KQUEUE) check_function_exists(_mkdir HAVE__MKDIR) check_function_exists(mkdir HAVE_MKDIR) check_function_exists(mmap HAVE_MMAP) check_function_exists(pipe HAVE_PIPE) check_function_exists(poll HAVE_POLL) check_function_exists(setitimer HAVE_SETITIMER) check_function_exists(setpgid HAVE_SETPGID) check_function_exists(setpgrp HAVE_SETPGRP) check_function_exists(setsid HAVE_SETSID) check_function_exists(setuid HAVE_SETUID) check_function_exists(sigaction HAVE_SIGACTION) check_function_exists(sigaddset HAVE_SIGADDSET) check_function_exists(sigprocmask HAVE_SIGPROCMASK) check_function_exists(strcasecmp HAVE_STRCASECMP) check_function_exists(strdup HAVE_STRDUP) check_function_exists(stricmp HAVE_STRICMP) check_function_exists(strncasecmp HAVE_STRNCASECMP) check_function_exists(strnicmp HAVE_STRNICMP) check_function_exists(strsep HAVE_STRSEP) check_function_exists(uname HAVE_UNAME) check_function_exists(wait HAVE_WAIT) check_function_exists(waitpid HAVE_WAITPID) CHECK_CXX_SOURCE_COMPILES( " #include int main() { auto foo = std::make_unique(1); return 0; } " HAVE_MAKE_UNIQUE) # winsock2.h and ws2_32 should provide these if(HAVE_WINSOCK2_H) set(HAVE_GETHOSTNAME ON) set(HAVE_SELECT ON) set(HAVE_SOCKET ON) set(HAVE_RECV ON) set(HAVE_SEND ON) set(HAVE_RECVFROM ON) set(HAVE_SENDTO ON) set(HAVE_GETHOSTBYNAME ON) set(HAVE_GETSERVBYNAME ON) else(HAVE_WINSOCK2_H) check_function_exists(gethostname HAVE_GETHOSTNAME) check_function_exists(select HAVE_SELECT) check_function_exists(socket HAVE_SOCKET) check_function_exists(recv HAVE_RECV) check_function_exists(send HAVE_SEND) check_function_exists(recvfrom HAVE_RECVFROM) check_function_exists(sendto HAVE_SENDTO) check_function_exists(gethostbyname HAVE_GETHOSTBYNAME) check_function_exists(getservbyname HAVE_GETSERVBYNAME) endif(HAVE_WINSOCK2_H) check_mkdir_args(MKDIR_TAKES_ONE_ARG) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # using Visual Studio add_definitions( -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DUNICODE -D_UNICODE ) endif() pvpgn-1.99.7.2.1+dfsg/LICENSE000066400000000000000000000431101462640461400152040ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pvpgn-1.99.7.2.1+dfsg/NEWS000066400000000000000000000113331462640461400147000ustar00rootroot00000000000000 PvPGN 1.7.x Major Changes And New Features ============================================== o Westwood Online (WOL) support This is a historic moment in the life of the PvPGN project. For the first time a different gaming network than Battle.net is supported with PvPGN. Current supports includes almost complete support for Tiberian Sun (TS) and basic support for Red Alert 2 (RA2) and Yuri's Revenge (YR). o WIN32 GUI for d2cs and d2dbs d2cs and d2bs now feature a new slick GUI if build under WIN32. o Instant startup (partial) PvPGN now can startup in a lot lesser time than before, this is due to code changes which make it not load all accounts at startup. This improvement should be mostly noticed on very high number of accounts servers. Note that currently this happens only to SQL storage types (the file based storage types still load all accounts at startup). o Optimized account loading/saving codes Account loading/saving codes have been rewritten allowing PvPGN to scale a lot better with a high number of online users (ie lower CPU usage). o Better game result calculation Game result decision has been completly changed to use a democratic ellection scheme (ie it bases the decision on the oppinions of the most results). This should solve problems with game results beeing wrong under some situations (like team games). o New WIN32 compiler/enviroment support Now you can compile PvPGN under WIN32 using complete GPL/free build tools by using Dev-C++ project and additional tools (mingw, etc). o Optimized handling of D2 clients Replaced a O(n) search algorithm through the client connection list used mostly by D2 handling functions with a O(1) algorithm. o Optimized timers codes Timers codes have been rewritten so the most used functions complete a lot faster than before. This improves PvPGN CPU usage a lot because some timers functions are used very often inside PvPGN. o Memory management changes and optimizations There has been added some memory management wrappers functions which never fail and thus the calling codes were simplified a lot by removing checks to memory allocation failures. If memory allocation fails PvPGN tries to recover by programming an immediate shutdown and cleaning up some safe memory. o Changed codes to use logged in username instead of account username This change makes PvPGN use the username introduced at login (with the exact case as in login) instead of the account name. This solves some bugs (like could not edit profile in D2 if logged in with different case) and also gets us more close to Battle.Net. o Switched from string based tags to integer based tags Codes have been dramatically changed to use integers for storing of tags (client tags, architecture tags, etc) and thus when comparing tags we use plain integer comparation instead of string comparation. This should reduce CPU usage a lot. o Optimized list handling codes The list API has been changed and the codes have been optimized so it will remove elements from the list upon request and not later, thus eliminating a O(n) search and thus making PvPGN a lot faster. o Unified and extended address translation system We have unified all address translation files and codes to use a single address_translation.conf file for this job. Also the file syntax has been completly changed to be more flexible and allow configuration of almost any thinkable NAT setup. o IRC support reworked IRC client support has been bugfixed, optimized and made to work with a lot more IRC clients. o Unified file based storage File based storage (plain and cdb) have been unified into a single storage backend (called "file") and the mode (plain/cdb) is just a parameter. o Rewritten low level socket multiplexing codes (fdwatch2) The codes which deal with how PvPGN knows which sockets are available for reading/writing have been rewritten with a new interface which does not presume (like the old codes) that the underlying OS allocates socket values "close" (ex. WIN32 systems do not while most Unix based systems do so). The new codes eliminates this presumtion and thus allows WIN32 hosts to support a large number of connections as PvPGN supported Unix based platforms. o Added more storage types: SQLite3 and ODBC With 1.7.x PvPGN now supports besides the MySQL/PostgreSQL SQL backends also SQLite3 (www.sqlite.org) and ... ODBC SQL engine !! This means that theoretically now you should be able to use PvPGN with almost any SQL engine as long as an ODBC driver exists for it. pvpgn-1.99.7.2.1+dfsg/README.DEV000066400000000000000000000421271462640461400155030ustar00rootroot00000000000000 PVPGN 2.x DEVELOPERS GUIDE Please read this file before asking any questions ! 0. About this document ======================= This document is intended to be read by all of you out there wanting to do development or testing for/on PvPGN. 1. Why ? ========= You want to start coding for PvPGN ? Why ? What do you expect to get from it ? Answer yourself this questions and then go to the next paragraph. 2. History =========== PvPGN has started as a game server emulation project, taking the excelent's bnetd project source and working on it. While initially it has started as a War3 emulation patch over bnetd it become a lot more than that (lots of new features, lots of code changes organizing). Because of the code roots you will notice a lot of things still carrying the "bnetd" word (most notable examples are the main servers program file called "bnetd" or the main server configuration file called bnetd.conf). We considered that as a sign of respect from us to the bnetd coders we keep their names on the code they written (but on the new code of course we may name them different). Arround the time of pvpgn 1.7.9 release we have branched the codes and started to work on pvpgn 2.x, the next big version, a C++ based version. 3. Objective ============= PvPGN's main objective is to support many gaming network protocols so that users can setup LAN parties and Internet gaming communities. As such it focuses mainly on those protocols that are both popular and where the client vendor (game maker) has NOT released any kind of server software that you can run to achieve the goal above (like Battle.Net, Westwood Online, etc). The current status is almost complete support for Battle.Net (all games) and limited Westwood Online support. We also offer the possibility to connect to PvPGN using non-gaming related protocols like IRC and telnet for better interoperability regarding the chat feature of the server (one of the main features as a gaming server). 4. Layout of files =================== Note: Starting here on you may find lots of terms and wors which may sound "strange" to you and for this reason we have included a glossary of terms in the end of this file. The PvPGN project consists of a main server (called "bnetd") and various other programs and (little) servers (ex. bnchat, d2cs, d2dbs etc...). PvPGN follows the bnetd's layout of files: ./bin -> used to store binaries (after compilation) ./conf -> configuration files (many of them templates) ./files -> various files needed for clients connecting ./man -> outdated man pages :( ./sbin -> same as ./bin ./scripts -> various scripts for testing/managing pvpgn ./src -> the source main directory ./src/bnetd -> source files used only by the main server ./src/common -> source files used in common by different programs ./src/compat -> source files concerning portability of code ... PvPGN uses automake/autoconf build and platform portability system. This is the main build method for UNIX/POSIX builds. For Win32 we offer the option to build using mingw tools. 5. Coding Style ================ a. General Because PvPGN is developed mainly on POSIX/Linux systems there are some things specific to this type of platform (like the line ending of all text/source files to be "Unix-style"). One thing which is overlooked by newbie coders is the "esthetical" side of the code. It may not be so important to many people (which code on the idea "if it works then its good") but for us, coding on PvPGN is VERY important. When you are coding for PvPGN PLEASE try to make your code look similar to already written code (this includes identing, identificator names, etc...). Keeping the code look "the same" makes its reading a lot more easier so, finding bugs easier so coding better. Other overlooked aspect for newbie coders is code replication. Please DONT copy and paste code arround !!! If you need to copy common functionality from some place, think about making some API of that functionalilty, put it in some classes and use it from both places. I repeat, DONT replicate code. When allocating memory (or some other kind of resource like a file, etc) inside a function always free it (release the resource) in the same function before its exit (exceptions: the function returns the allocated memory/resource in which case the calling function should take care of the allocated memory; or the allocated memory is cached/stored somewhere to be used later, in which case you must make sure it will be free()d when not needed anymore). In the startup code of any "external" function (function which may be called from other modules then the one containing it or a public method ) please check ALL the input parameters (you will notice how PvPGN already does that in such functions). In case of errors detected in such cases (and not only) code should "throw" an exception (see section 5.d). When developing code you should compile always with "all warnings" enabled and try to fix them (some warnings uncover real bugs), like for gcc use "-Wall". When running/testing the codes you should use a memory debugger like valgrind. For more details about how to run pvpgn check Appendix B. To allocate/free memory use ONLY new/delete calls (this makes sure that out of memory condition is detected and nicely handled because in that case "new" automatically "throws" std::bad_alloc exception). b. Indenting PvPGN indentation should be done ONLY with "tabs" (exception see below), the tab size is 8 "spaces". We indent a level after if, while, do-while, for, case (but NOT from switch), class/struct/union (but NOT the access modifier public/protected/private) and a starting brace. The position of the starting brace relative to the keywords from above is at the same level as the keyword. We except to indent after switch, namespace braces and the access modifiers because otherwise things will look "too indented" and make codes less readable. c. Functions (methods/constructors) and their parameters Function definitions should have the type returned by the function on it's own line than the function name and it's parameter list. Like in: const std::string& MyClass::getString() { } Function parameters should be passed by reference for "aggregate" types (like class/struct/union). This reference usually is const, ie "const Type& param". Accessors should return references to aggregate members (this is safe because we presume the object's life time exceeds the reference cacher's use time, if not the calling codes can just cache a copy of the object). Functions should never have their bodies in the .h file (exception, class and function templates that NEED to be in the .h file completely). d. Exceptions and error handling "Errors are not always exceptions". Keep this in mind! But generally, errors are exceptions. In the common sense and in turn, in the codes. In case of errors (that is errors that shouldnt happen too often, not common case errors) the codes should throw an exception. Exceptions allows for a very clean and versatile way to detach the place of error detection to the place of error handling (in C you detect an error in some low-level codes and you report it in the return value and this is checked and propagated by checks to the higher level codes that will probably handle the error). Also with C++ exceptions the "normal" code flow it's NOT disrupted and polluted by trying to handle errors everywhere you might hit one. Exceptions basically offer an alternative SLOW code path. It is slow (much slower than normal code flow) and this is why it should be used to signal relatively "rare" events (thus called exceptions) and not something "common". But using exceptions you might be carefull with them to not "leak" resources. The problem is that if you allocate some resource in some codes and then you might get an exception (not even from your codes but indirectly from codes you call) you need to make sure the resources aquired are released BEFORE the exception propagates out of your code scope. This is _easily_ done with C++ and the RAII principle (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization). The rule is simple, ALL resource aquisitions should be done using some kind of wrappers that release the resource in their destructor. This also makes codes a lot more simple when exiting code scope on non-error cases because you do NOT need to explicitely release the "wrapped" resource as it will release automatically when leaving the code scope. You are allowed ONLY to throw objects, NEVER throw basic types. All the exceptions you throw HAVE to be derived from std::exception (thus providing the .what() method to get a C-string of the description of the error). It is best however, you derivate from std::runtime_error & co. You MAY create your own exception class (with the limitations from above) IF AND ONLY IF your exception represents an error that NEEDS to be handled differentely (ie catched separately from other exceptions). All such user defined exception classes should be defined NESTED in the class that throws them. Catching exceptions should ONLY be done using the "const reference" catching scheme like in: } catch (const std::exception& ex) { This allows for 2 important things to happen: - it allows proper polymorphic mechanisms for exceptions (so with the example above ANY exception derived from std::exception is caught and handled as it should be) - it behaves nicely on out of memory condition ie an ISO C++98 compliant compiler even when out of memory condition is reached has to provide ENOUGH memory to create a temporary object that (and this is what you do when throwing an exception to be caught with reference, you throw like throw MyException(); whcih means it creates a temporary object MyException() and that object reference is passed to the handlers) e. Namespaces All pvpgn codes will belong to the "pvpgn" namespace. Specific application codes will belong to a namespace inside "pvpgn" like "pvpgn::bnetd". Common codes need to still be just in "pvpgn" so all other codes will "see" them. Never ever use a "using" keyword inside a .h file. Inside .cpp files you _may_ use them. Some types, classes, etc are better to be declared inside some other class (like the exceptions, see the above section or smart pointers for the class). "static" keyword to declare "local" variables and functions it's not allowed. Instead of it, use unnamed namespaces. A ISO C++98 compliant compiler has to provide for each separate translation unit an unique name for the unnamed namespace that could be declared in that translation unit. As such, instead of ------------------------ static int myvar; static int myfunc(int); extern int externfunc() { } static int myfunc() { } ------------------------ Make it ------------------------ namespace { int myvar; int myfunc(int); } extern int externfunc() { } namespace { int myfunc(int) { } } ------------------------ f. Identifiers Class names should start with a capital and all words composing it should too (Java-like style, like in "MyStuff"). All methods should start with a lowercase letter and then all words composing it with uppercase. Accessor methods should start with get/set and then the name of the member accessed with the case rules already mentioned. g. Include order In .cpp files the first include should be "common/setup_before.h" followed by the coresponding header of the cpp file (ie in clan.cpp after setup_before.h you include clan.h). Then you include (this applies to headers too not only .cpp now) the headers of standard C++ lib, then the headers from C90 as offered by standard C++ (ex. etc), then the POSIX/win32/system dependant headers. h. Code Flow Appendix A. Glossary of terms =============================== * autoupdate: the feature of Battle.Net servers to send a (MPQ, see MPQ) file to the client which after downloading it, it is used to update the client * connection class: when a connection is established to a bnet listening address the client sends an initial byte which tells the server of what class of connection the following packets will be; classes of connections determine what packets can go through that connection. * MPQ: a format archive which is used by Blizzard clients and Battle.Net servers. This files are used for containing verious files (sound/graphics in clients, AI scripts, update patches etc...) * versioncheck: also know as vcheck and sometimes just vc ; a client verification system used by Battle.Net servers to identify client version and "purity". Based on this the server may accept/refuse connection or ask for upgrade (see autoupdate). Appendix B. How to run PvPGN for debugging =========================================== It is very helpfull in finding out memory coruption bugs as soon as possible so while developing codes or just when running a server it is good that you use some memory coruption run-time debuggers. I dont know about Win32 users but on Unix/Linux there are some good options. 1. valgrind (http://valgrind.kde.org) Valgrind is not very portable (only x86, Linux and very recently FreeBSD), also it slows down the debugged codes (it acts like a CPU emulator so it has to do that) but I have yet to find out a better debugging tool for what he does. Valgrind is so cool that recently many OSS projects use it for finding out bugs in their codes. For more information you can check out their web page. I will focus on valgrind with PvPGN. After you have compiled and installed valgrind (it's easy, ./configure, make, make install) you will use it by running PvPGN like this: $ valgrind --tool=memcheck --num-callers=10 /path/to/bnetd -f 2> valg.out "num-callers" makes valgrind record backtraces with 10 entries and is usually needed with PvPGN which has not very small backtrace path :) Another option you might want to use is "--leak-check=yes" and probably "--leak-resolution=high". This options make valgrind even slower but they will give memory leak information when PvPGN exits. I encourage EVERYONE to use it if available (that is if you run PvPGN on a supported platform). Only very big servers won't be able to do it because there is no hardware powerfull enough to run a big server with valgrind (but big means over 500 users online). You should test it with your server and if it does not make your bnetd go over 90% CPU then you should be fine. If you cannot run valgrind for any reason or if you are hunting for some bugs valgrind cannot find (yes, valgrind is superb but there is a class of bugs, especially overflows which valgrind can't help you with) you should then try the next debugging tool. 2. Deleaker (http://deleaker.com) For developers on Windows and uses Visual Studio, Deleaker is an extension for Visual Studio that helps you detect and localize resource leaks in memory, GDI and USER objects, handles. It integrates into Visual Studio's debugging functionality and displays useful information to trace leaks back to their source code. Deleaker also comes with a standalone program for developers using other compilers. Appendix C. How to generate and use "core" files ================================================= This appendix is for Unix users. I dont know if other platforms have similar features, that when the program crashes unexpectedly the OS would dump the contents of the memory of the crashing process into a disk file for later inspection. First make sure that PvPGN has been compiled with debugging ("-g" parameter to gcc) and with no optimizations ("-O0" parameter to gcc). PvPGN default build process puts "-g -O2" so you need to edit Makefile file before compile and change it to "-g -O0". Then something like "make clean; make". On Unix/Linux to be able to get core dumps you first need to make sure your core file size limit is set acordingly. Use "ulimit -c" for querying and setting this limit (I recommend setting it to "unlimited"). After that when you start PvPGN make sure you are in a directory where you have write access (so the OS when it wants to do the core dump it will be allowed to do so). The last thing to do is when starting PvPGN make sure it starts in FOREGROUND, example : /path/to/bnetd -f . If you did all this then when PvPGN will crash you will get a core dump. On linux this is a file called "core", on *BSD it's called .core (for bnetd that means it's called bnetd.core). Now that you got a core file it is time to use it to identify what happened wrong in the crashing process. We use gdb (the GNU debugger, should be available on all Unices) to do this. Run gdb like this: $ gdb /path/to/bnetd /path/to/corefile Then gdb should startup, print out a lot of messages and stop after printing a file and line number and quoting some C code where the crash has happened. You can find out a lot more information than this. Run gdb's command "bt full" and it will display a full backtrace of the moment of the crash. The backtrace will contain how the functions were called along the way (their parameters), and also any local variables. If you do not know what to do next from here contact a PvPGN developer and give him exactly that backtrace dump, he should know more. pvpgn-1.99.7.2.1+dfsg/README.md000066400000000000000000000206311462640461400154610ustar00rootroot00000000000000Player vs Player Gaming Network - PRO ===== ![](http://i.imgur.com/LfI3hXo.png) PvPGN is a free and open source cross-platform server software that supports Battle.net and and Westwood Online game clients. PvPGN-PRO is a fork of the official PvPGN project, whose development stopped in 2011, and aims to provide continued maintenance and additional features for PvPGN. [![License (GPL version 2)](https://img.shields.io/badge/license-GNU%20GPL%20version%202-blue.svg?style=flat-square)](http://opensource.org/licenses/GPL-2.0) ![Language (C++)](https://img.shields.io/badge/powered_by-C++-brightgreen.svg?style=flat-square) [![Language (Lua)](https://img.shields.io/badge/powered_by-Lua-red.svg?style=flat-square)](https://lua.org) [![Github Releases (by Release)](https://img.shields.io/github/downloads/pvpgn/pvpgn-server/1.99.7.2.1/total.svg?maxAge=2592000)]() [![Compiler (Microsoft Visual C++)](https://img.shields.io/badge/compiled_with-Microsoft%20Visual%20C++-yellow.svg?style=flat-square)](https://msdn.microsoft.com/en-us/vstudio/hh386302.aspx) [![Compiler (LLVM/Clang)](https://img.shields.io/badge/compiled_with-LLVM/Clang-lightgrey.svg?style=flat-square)](http://clang.llvm.org/) [![Compiler (GCC)](https://img.shields.io/badge/compiled_with-GCC-yellowgreen.svg?style=flat-square)](https://gcc.gnu.org/) [![Build Status](https://travis-ci.org/pvpgn/pvpgn-server.svg?branch=master)](https://travis-ci.org/pvpgn/pvpgn-server) [![Build status](https://ci.appveyor.com/api/projects/status/dqoj9lkvhfwthmn6)](https://ci.appveyor.com/project/HarpyWar/pvpgn) [Deleaker](http://www.deleaker.com/) helps us find memory leaks. ## Tracking By default, tracking is enabled and is only used for the purpose of sending informational data (e.g. server description, homepage, uptime, amount of users) to tracking servers. To disable tracking, set ````track = 0```` in ````conf/bnetd.conf````. ## Supported Clients - **WarCraft 2: Battle.net Edition**: 2.02a, 2.02b - **WarCraft 3: Reign of Chaos**\*: 1.13a, 1.13b, 1.14a, 1.14b, 1.15a, 1.16a, 1.17a, 1.18a, 1.19a, 1.19b, 1.20a, 1.20b, 1.20c, 1.20d, 1.20e, 1.21a, 1.21b, 1.22a, 1.23a, 1.24a, 1.24b, 1.24c, 1.24d, 1.24e, 1.25b, 1.26a, 1.27a, 1.27b, 1.28, 1.28.1, 1.28.2, 1.28.4, 1.28.5 - **WarCraft 3: The Frozen Throne**\*: 1.13a, 1.13b, 1.14a, 1.14b, 1.15a, 1.16a, 1.17a, 1.18a, 1.19a, 1.19b, 1.20a, 1.20b, 1.20c, 1.20d, 1.20e, 1.21a, 1.21b, 1.22a, 1.23a, 1.24a, 1.24b, 1.24c, 1.24d, 1.24e, 1.25b, 1.26a, 1.27a, 1.27b, 1.28, 1.28.1, 1.28.2, 1.28.4, 1.28.5 - **StarCraft**: 1.08, 1.08b, 1.09, 1.09b, 1.10, 1.11, 1.11b, 1.12, 1.12b, 1.13, 1.13b, 1.13c, 1.13d, 1.13e, 1.13f, 1.14, 1.15, 1.15.1, 1.15.2, 1.15.3, 1.16, 1.16.1, 1.17.0, 1.18.0 - **StarCraft: Brood War**: 1.08, 1.08b, 1.09, 1.09b, 1.10, 1.11, 1.11b, 1.12, 1.12b, 1.13, 1.13b, 1.13c, 1.13d, 1.13e, 1.13f, 1.14, 1.15, 1.15.1, 1.15.2, 1.15.3, 1.16, 1.16.1, 1.17.0, 1.18.0 - **Diablo**: 1.09, 1.09b - **Diablo 2**: 1.10, 1.11, 1.11b, 1.12a, 1.13c, 1.14a, 1.14b, 1.14c, 1.14d - **Diablo 2: Lord of Destruction**: 1.10, 1.11, 1.11b, 1.12a, 1.13c, 1.14a, 1.14b, 1.14c, 1.14d - **Westwood Chat Client**: 4.221 - **Command & Conquer**: Win95 1.04a (using Westwood Chat) - **Command & Conquer: Red Alert**: Win95 2.00 (using Westwood Chat), Win95 3.03 - **Command & Conquer: Red Alert 2**: 1.006 - **Command & Conquer: Tiberian Sun**: 2.03 ST-10 - **Command & Conquer: Tiberian Sun Firestorm**: 2.03 ST-10 - **Command & Conquer: Yuri's Revenge**: 1.001 - **Command & Conquer: Renegade**: 1.037 - **Nox**: 1.02b - **Nox Quest**: 1.02b - **Dune 2000**: 1.06 - **Emperor: Battle for Dune**: 1.09 \* WarCraft 3 clients are unable to connect to PvPGN servers without a client-side modification, through tools such as [W3L](https://github.com/w3lh/w3l), to disable server signature verification. \* StarCraft clients beginning with patch 1.18 will not be supported by PvPGN-PRO due to protocol changes. A 1.18.0 versioncheck entry is included for compatibility with bot software. ## Support [Create an issue](https://github.com/pvpgn/pvpgn-server/issues) if you have any questions, suggestions, or anything else to say about PvPGN-PRO. Please note that D2GS is not part of the PvPGN project and is therefore unsupported here. Set `loglevels = fatal,error,warn,info,debug,trace` in `bnetd.conf` before obtaining logs and posting them. ## Development Submit pull requests to contribute to this project. Utilize C++11 features and adhere to the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md) whenever possible. ## Building See [docs/ports.md](https://github.com/pvpgn/pvpgn-server/blob/master/docs/ports.md) for operating systems and compilers that have been confirmed to work with PvPGN. Any operating system that supports WinAPI or POSIX, and any C++11 compliant compiler should be able to build PvPGN. The CMake files have been hardcoded to reject compilers older than Visual Studio 2015 and GCC 5.1. #### Windows Use [Magic Builder](https://github.com/pvpgn/pvpgn-magic-builder). Alternatively, use cmake to generate the .sln project and build it from Visual Studio. ``` cmake -g "Visual Studio 14 2015" -H./ -B./build ``` This will generate .sln in `build` directory. #### Linux in general Do not blindly run these commands. The main problem with older distributions is installing CMake 3.2.x and GCC 5, so external repositories are used in the examples. ``` apt-get install git install cmake make build-essential zlib1g-dev apt-get install liblua5.1-0-dev #Lua support apt-get install mysql-server mysql-client libmysqlclient-dev #MySQL support cd /home git clone https://github.com/pvpgn/pvpgn-server.git cmake -D CMAKE_INSTALL_PREFIX=/usr/local/pvpgn -D WITH_MYSQL=true -D WITH_LUA=true ../ make make install ``` #### Ubuntu 16.04 ``` sudo apt-get -y install build-essential git cmake zlib1g-dev git clone https://github.com/pvpgn/pvpgn-server.git cd pvpgn-server && cmake -G "Unix Makefiles" -H./ -B./build cd build && make ``` #### Ubuntu 14.04 ``` sudo apt-get -y install build-essential zlib1g-dev git sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get -y update sudo apt-get -y install gcc-5 g++-5 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 60 --slave /usr/bin/g++ g++ /usr/bin/g++-5 sudo add-apt-repository -y ppa:george-edison55/cmake-3.x sudo apt-get update sudo apt-get -y install cmake git clone https://github.com/pvpgn/pvpgn-server.git cd pvpgn-server && cmake -G "Unix Makefiles" -H./ -B./build cd build && make ``` #### Debian 8 ``` sudo apt-get -y install build-essential zlib1g-dev clang libc++-dev git wget https://cmake.org/files/v3.7/cmake-3.7.1-Linux-x86_64.tar.gz tar xvfz cmake-3.7.1-Linux-x86_64.tar.gz git clone https://github.com/pvpgn/pvpgn-server.git cd pvpgn-server && CC=/usr/bin/clang CXX=/usr/bin/clang++ ../cmake-3.7.1-Linux-x86_64/bin/cmake -G "Unix Makefiles" -H./ -B./build cd build && make ``` #### CentOS 7 ``` sudo yum -y install epel-release centos-release-scl sudo yum -y install git zlib-devel cmake3 devtoolset-4-gcc* sudo ln -s /usr/bin/cmake3 /usr/bin/cmake git clone https://github.com/pvpgn/pvpgn-server.git cd pvpgn-server CC=/opt/rh/devtoolset-4/root/usr/bin/gcc CXX=/opt/rh/devtoolset-4/root/usr/bin/g++ cmake -G "Unix Makefiles" -H./ -B./build cd build && make ``` #### Fedora 25 ``` sudo dnf -y install gcc-c++ gcc make zlib-devel cmake git git clone https://github.com/pvpgn/pvpgn-server.git cd pvpgn-server cmake -G "Unix Makefiles" -H./ -B./build cd build && make ``` #### FreeBSD 11 ``` sudo pkg install -y git cmake git clone https://github.com/pvpgn/pvpgn-server.git cd pvpgn-server cmake -G "Unix Makefiles" -H./ -B./build cd build && make ``` Full instructions: [РуÑ�Ñ�кий](http://harpywar.com/?a=articles&b=2&c=1&d=74) | [English](http://harpywar.com/?a=articles&b=2&c=1&d=74&lang=en) ## License This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. pvpgn-1.99.7.2.1+dfsg/UPDATE000066400000000000000000000111401462640461400151020ustar00rootroot00000000000000========================== PVPGN UPDATE INFORMATION ========================== This file contains a summary of all changes from previous version you need to take care of when updating your server to the current version. It should contain: -- info's about all new/changed parameters in bnetd.conf -- all new/changed config files in the conf/ subfolder The path's specified are those of the default unix installation NOTE: If updating through several versions (example if updating from 1.7.1 to 1.7.5) you HAVE to go through ALL the update information listed for every version in between and including the last one (ex. you have to go through 1.7.1 to 1.7.2 update info, then 1.7.2 to 1.7.3, and so on until 1.7.4 to 1.7.5) Updating from 1.99.0-CVS ========================= Configuration files changed: - conf/d2cs.conf : added optional: "game_maxlevel" config option which which needs to be given in the case of Diablo MODs with a level > 255 Updating from 1.7.9 to 1.7.10 =============================== Configuration files changed: - conf/sql_DB_layout2.conf : added creation of two additional indices - conf/versioncheck.conf : added D2 / W3 entries + cleanups - conf/autoupdate.conf : general cleanup/update Updating from 1.7.8 to 1.7.9 ============================== Configuration files changed: - conf/d2cs.conf : added pid entry - conf/d2dbs.conf : added pid entry - conf/bnetd.conf : added examples for sql2 usage - conf/versioncheck.conf : added many SC/BW vchecks (yeah seems they have been busy lately) and W3 vchecks - conf/command_groups.conf : added entry for the new "/tos" command Configuration files added: - conf/sql_DB_layout2.conf : contains database structure initialization SQL statements for sql2 mode Updating from 1.7.7 to 1.7.8 ============================== Configuration files changed: - conf/sql_DB_layout.conf : includes changes needed for the new "username" SQL field used to fix a MAJOR bug that shows up with case sensitive SQL engines (almost ALL non-MySQL engines); if you update to 1.7.8 YOU MUST update this file too if you have your accounts on SQL - conf/bnetd.conf : added Westwood Online (WOL) configuration options; woladdr to specify the address to listen for WOL clients, woltimezone for the server timezone to send to clients, wollong for the server longitute, wollat for the server latitude - conf/bnetd.conf : changed "servaddrs" directive to not be totally commented and be as ":" by default in bnetd.conf (if "servaddrs" is commented this means "bnetd" will not listen for b.net services at all) - conf/channels.conf : added channels needed by WOL clients - conf/versioncheck.conf : updated with latest client versions (War3 1.18a, SC/BW 1.13 and 1.13b) - conf/bnmaps.conf : updated with the maps used now on b.net Updating from 1.7.5 to 1.7.6 ============================== Configuration files changed: - conf/bnetd.conf : moved the old "servername" parameter to "hostname" - conf/bnetd.conf : introduced "servername" parameter for server identification Updating from 1.7.3 to 1.7.4 ============================== - conf/bnetd.conf : added "ladder_prefix" parameter to allow only games with matching gamename to be counted as ladder (applies only to alternative ladder game types as specified by "ladder_games" directive) Updating from 1.7.1 to 1.7.2 ============================== Configuration files changed: - conf/bnetd.conf : added "sync_on_logoff" parameter to have account data saved to disk on user's logoff - conf/d2cs.conf : added "bak_charsavedir" & "bak_charinfodir" parameter so d2cs can delete the backup copy of charactars deleted by the client. these settings need to be set the same as the d2dbs.conf parameters of the same name Updating from 1.7.0 to 1.7.1 ============================== Configuration files changed: - conf/bnetd.conf : added "max_connections" parameter to set up the max allowed number of connections to the bnetd daemon - conf/d2cs.conf : added "max_connections" parameter to set up the max allowed number of connections to the d2cs daemon - conf/d2dbs.conf : added "difficulty_hack" parameter to activate the Diablo II level 100 workaround Updating from 1.6.x to 1.7.0 ============================== Because of the many changes in 1.7.0 we recommend to use a clean install and update the configuration files as necesarry. Things to note: 1. you now need GNU make to build PvPGN on POSIX systems (on Linux this is just "make" but on *BSD is "gmake") 2. d2trans,gametrans,w3trans have been unified under address_translation.conf file which has a complete new syntax so take care with that pvpgn-1.99.7.2.1+dfsg/appveyor.yml000066400000000000000000000046211462640461400165730ustar00rootroot00000000000000branches: only: - master - develop image: Visual Studio 2017 platform: - x86 environment: matrix: - db: plain db_id: 1 - db: mysql db_id: 2 - db: pgsql db_id: 3 - db: sqlite db_id: 4 - db: odbc db_id: 5 configuration: # - Debug - Release matrix: fast_finish: true clone_folder: c:\pvpgn install: - cmd: cd c:\ - cmd: appveyor DownloadFile https://github.com/HarpyWar/pvpgn-magic-builder/archive/master.zip - cmd: 7z.exe x master.zip >nul - cmd: ren pvpgn-magic-builder-master builder - cmd: mkdir c:\builder\source\ - cmd: xcopy /E /R /K /Y /Q "c:\pvpgn" "c:\builder\source\" - cmd: cd c:\builder\ - cmd: mkdir bin - cmd: mkdir pdb before_build: - cmd: set APPVEYOR_REPO_COMMIT_SHORT=%APPVEYOR_REPO_COMMIT:~0,7% # read version string from version.h to %PVPGN_VERSION% and append commit string inside that file - cmd: module\tools\pvpgn_version.bat %APPVEYOR_REPO_COMMIT_SHORT% - cmd: set COMPLETE_BUILD_VERSION=pvpgn-%APPVEYOR_REPO_BRANCH%-%PVPGN_VERSION%-%APPVEYOR_REPO_COMMIT_SHORT% - ps: $env:PVPGN_ZIP=$env:COMPLETE_BUILD_VERSION + "_" + $env:DB + $(If ($env:CONFIGURATION -ne "Release") {"_" + ($env:CONFIGURATION).ToLower()} Else {""}) + ".zip" - ps: $env:PVPGN_PDB_ZIP=$env:COMPLETE_BUILD_VERSION + "_" + $env:DB + $(If ($env:CONFIGURATION -ne "Release") {"_" + ($env:CONFIGURATION).ToLower()} Else {".pdb"}) + ".zip" build_script: # build gui and console for each storage type # build_pvpgn.bat params: # 1. automatic build flag # 2. visual studio version # 3. console or gui # 4. storage type # 5. with or without lua # 6. Release or Debug # gui - cmd: build_pvpgn.bat auto auto 2 %DB_ID% y %CONFIGURATION% - cmd: move /Y %CONFIGURATION%\*.pdb pdb\ # move pdb files - cmd: copy /Y %CONFIGURATION%\* bin\ # backup binary files # console - cmd: build_pvpgn.bat auto auto 1 %DB_ID% y %CONFIGURATION% - cmd: move /Y %CONFIGURATION%\*.pdb pdb\ # move pdb files - cmd: move /Y bin\* %CONFIGURATION% # move back binaries after build console version # create zip artifacts - cmd: 7z.exe a %PVPGN_ZIP% .\%CONFIGURATION%\* - cmd: 7z.exe a %PVPGN_PDB_ZIP% .\pdb\* deploy_script: - ps: | Push-AppveyorArtifact $env:PVPGN_ZIP Push-AppveyorArtifact $env:PVPGN_PDB_ZIP # check the latest build executable for exists test: assemblies: - '**\src\bnetd\%CONFIGURATION%\bnetd.exe' pvpgn-1.99.7.2.1+dfsg/cmake/000077500000000000000000000000001462640461400152605ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/cmake/Modules/000077500000000000000000000000001462640461400166705ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/cmake/Modules/CheckMkdirArgs.cmake000066400000000000000000000047311462640461400225200ustar00rootroot00000000000000# - Check if the files can be included # # CHECK_MKDIR_ARGS(VARIABLE) # # VARIABLE - variable to return if mkdir/_mkdir need a single argument # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories MACRO(CHECK_MKDIR_ARGS VARIABLE) IF("${VARIABLE}" MATCHES "^${VARIABLE}$") SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") IF(CMAKE_REQUIRED_INCLUDES) SET(CHECK_MKDIR_ARGS_INCLUDE_DIRS "-DINCLUDE_DIRECTORIES=${CMAKE_REQUIRED_INCLUDES}") ELSE(CMAKE_REQUIRED_INCLUDES) SET(CHECK_MKDIR_ARGS_INCLUDE_DIRS) ENDIF(CMAKE_REQUIRED_INCLUDES) SET(CHECK_MKDIR_ARGS_CONTENT "/* */\n") SET(MACRO_CHECK_MKDIR_ARGS_FLAGS ${CMAKE_REQUIRED_FLAGS}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n#include\nint main(){mkdir(\"\");}\n") CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckMkdirArgs.cxx" @ONLY IMMEDIATE) MESSAGE(STATUS "Checking for single argument mkdir ${VARIABLE}") TRY_COMPILE(${VARIABLE} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckMkdirArgs.cxx COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_MKDIR_ARGS_FLAGS} "${CHECK_MKDIR_ARGS_INCLUDE_DIRS}" OUTPUT_VARIABLE OUTPUT) IF(${VARIABLE}) MESSAGE(STATUS "Checking for single argument mkdir ${VARIABLE} - found") SET(${VARIABLE} 1 CACHE INTERNAL "Have include ${VARIABLE}") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if files ${INCLUDE} " "exist passed with the following output:\n" "${OUTPUT}\n\n") ELSE(${VARIABLE}) MESSAGE(STATUS "Checking for single argument mkdir ${VARIABLE} - not found.") SET(${VARIABLE} "" CACHE INTERNAL "Have includes ${VARIABLE}") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if files ${INCLUDE} " "exist failed with the following output:\n" "${OUTPUT}\nSource:\n${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ENDIF(${VARIABLE}) ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$") ENDMACRO(CHECK_MKDIR_ARGS) pvpgn-1.99.7.2.1+dfsg/cmake/Modules/DefineInstallationPaths.cmake000066400000000000000000000036141462640461400244520ustar00rootroot00000000000000IF (NOT APPLICATION_NAME) MESSAGE(STATUS "${PROJECT_NAME} is used as APPLICATION_NAME") SET(APPLICATION_NAME ${PROJECT_NAME}) ENDIF (NOT APPLICATION_NAME) SET(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries" FORCE ) SET(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Base directory for files which go to share/" FORCE ) SET(DATA_INSTALL_PREFIX "${SHARE_INSTALL_PREFIX}/${APPLICATION_NAME}" CACHE PATH "The parent directory where applications can install their data" FORCE ) SET(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The ${APPLICATION_NAME} binary install dir (default prefix/bin)" FORCE ) #***********************************# if(WIN32) SET(LOCALSTATE_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/var" CACHE PATH "The ${APPLICATION_NAME} local state install dir (default prefix/var)" FORCE ) else() SET(LOCALSTATE_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/var/${APPLICATION_NAME}" CACHE PATH "The ${APPLICATION_NAME} local state install dir (default prefix/var)" FORCE ) endif() SET(MAN_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/man" CACHE PATH "The ${APPLICATION_NAME} man install dir (default prefix/man)" FORCE ) if(WIN32) SET(SBIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}" CACHE PATH "The ${APPLICATION_NAME} sbin install dir (default prefix/sbin)" FORCE ) else() SET(SBIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/sbin" CACHE PATH "The ${APPLICATION_NAME} sbin install dir (default prefix/sbin)" FORCE ) endif() if(WIN32) SET(SYSCONF_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/conf" CACHE PATH "The ${APPLICATION_NAME} sysconfig install dir (default conf)" FORCE ) else() SET(SYSCONF_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/etc/${APPLICATION_NAME}" CACHE PATH "The ${APPLICATION_NAME} sysconfig install dir (default prefix/etc)" FORCE ) endif()pvpgn-1.99.7.2.1+dfsg/cmake/Modules/FindLua.cmake000066400000000000000000000034141462640461400212160ustar00rootroot00000000000000# Copyright (c) 2013 Martin Felis <martin@fysx.org> # License: Public Domain (Unlicense: http://unlicense.org/) # Modified by Edvin "Lego3" Linge for the CorsixTH project. # # Try to find Lua or LuaJIT depending on the variable WITH_LUAJIT. # Sets the following variables: # LUA_FOUND # LUA_INCLUDE_DIR # LUA_LIBRARY # # Use it in a CMakeLists.txt script as: # # OPTION (WITH_LUAJIT "Use LuaJIT instead of default Lua" OFF) # UNSET(LUA_FOUND CACHE) # UNSET(LUA_INCLUDE_DIR CACHE) # UNSET(LUA_LIBRARY CACHE) # FIND_PACKAGE (Lua REQUIRED) SET (LUA_FOUND FALSE) SET (LUA_LIBRARIES) SET (LUA_INTERPRETER_TYPE "Lua5.1") SET (LUA_LIBRARY_NAME lua5.1 lua51 lua lua-5.1) SET (LUA_INCLUDE_DIRS include/lua5.1 include/lua51 include/lua include/lua-5.1 include) FIND_PATH (LUA_INCLUDE_DIR lua.h HINTS ENV LUA_DIR PATH_SUFFIXES ${LUA_INCLUDE_DIRS} PATHS /opt/local /usr/local /usr /opt /sw ~/Library/Frameworks /Library/Frameworks ) FIND_LIBRARY (LUA_LIBRARY NAMES ${LUA_LIBRARY_NAME} HINTS ENV LUA_DIR PATH_SUFFIXES lib PATHS /usr /usr/local /opt/local /opt /sw ~/Library/Frameworks /Library/Frameworks ) IF (LUA_INCLUDE_DIR AND LUA_LIBRARY) SET (LUA_FOUND TRUE) SET (LUA_LIBRARIES ${LUA_LIBRARY}) ENDIF (LUA_INCLUDE_DIR AND LUA_LIBRARY) IF (LUA_FOUND) IF (NOT Lua_FIND_QUIETLY) MESSAGE(STATUS "Found ${LUA_INTERPRETER_TYPE} library: ${LUA_LIBRARY}") ENDIF (NOT Lua_FIND_QUIETLY) ELSE (LUA_FOUND) IF (Lua_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find ${LUA_INTERPRETER_TYPE}") ENDIF (Lua_FIND_REQUIRED) ENDIF (LUA_FOUND) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua DEFAULT_MSG LUA_LIBRARY LUA_INCLUDE_DIR) MARK_AS_ADVANCED ( LUA_INCLUDE_DIR LUA_LIBRARY)pvpgn-1.99.7.2.1+dfsg/cmake/Modules/FindMySQL.cmake000066400000000000000000000047121462640461400214440ustar00rootroot00000000000000# - Try to find MySQL. # Once done this will define: # MYSQL_FOUND - If false, do not try to use MySQL. # MYSQL_INCLUDE_DIRS - Where to find mysql.h, etc. # MYSQL_LIBRARIES - The libraries to link against. # MYSQL_VERSION_STRING - Version in a string of MySQL. # # Created by RenatoUtsch based on eAthena implementation. # # Please note that this module only supports Windows and Linux officially, but # should work on all UNIX-like operational systems too. # #============================================================================= # Copyright 2012 RenatoUtsch # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) if( WIN32 ) set(MYENV "PROGRAMFILES(X86)") find_path( MYSQL_INCLUDE_DIR NAMES "mysql.h" PATHS "$ENV{PROGRAMFILES}/MySQL/*/include" "$ENV{${MYENV}}/MySQL/*/include" "$ENV{SYSTEMDRIVE}/MySQL/*/include" ) find_library( MYSQL_LIBRARY NAMES "mysqlclient" "mysqlclient_r" "libmysql" PATHS "$ENV{PROGRAMFILES}/MySQL/*/lib" "$ENV{${MYENV}}/MySQL/*/lib" "$ENV{SYSTEMDRIVE}/MySQL/*/lib" ) else() find_path( MYSQL_INCLUDE_DIR NAMES "mysql.h" PATHS "/usr/include/mysql" "/usr/local/include/mysql" "/usr/mysql/include/mysql" ) find_library( MYSQL_LIBRARY NAMES "mysqlclient" "mysqlclient_r" PATHS "/lib/mysql" "/lib64/mysql" "/usr/lib/mysql" "/usr/lib64/mysql" "/usr/local/lib/mysql" "/usr/local/lib64/mysql" "/usr/mysql/lib/mysql" "/usr/mysql/lib64/mysql" ) endif() IF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) SET(MYSQL_FOUND TRUE) SET( MYSQL_LIBRARIES ${MYSQL_LIBRARY} ) ELSE (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) SET(MYSQL_FOUND FALSE) SET( MYSQL_LIBRARIES ) ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) IF (MYSQL_FOUND) MESSAGE(STATUS "Found MySQL: ${MYSQL_LIBRARY}") ELSE (MYSQL_FOUND) IF (MySQL_FIND_REQUIRED) MESSAGE(STATUS "Looked for MySQL libraries named ${MYSQL_NAMES}.") MESSAGE(FATAL_ERROR "Could NOT find MySQL library") ENDIF (MySQL_FIND_REQUIRED) ENDIF (MYSQL_FOUND) MARK_AS_ADVANCED( MYSQL_LIBRARY MYSQL_INCLUDE_DIR )pvpgn-1.99.7.2.1+dfsg/cmake/Modules/FindODBC.cmake000066400000000000000000000033561462640461400212110ustar00rootroot00000000000000# # Find the ODBC driver manager includes and library. # # ODBC is an open standard for connecting to different databases in a # semi-vendor-independent fashion. First you install the ODBC driver # manager. Then you need a driver for each separate database you want # to connect to (unless a generic one works). VTK includes neither # the driver manager nor the vendor-specific drivers: you have to find # those yourself. # # This module defines # ODBC_INCLUDE_DIRECTORIES, where to find sql.h # ODBC_LIBRARIES, the libraries to link against to use ODBC # ODBC_FOUND. If false, you cannot build anything that requires MySQL. # also defined, but not for general use is # ODBC_LIBRARY, where to find the ODBC driver manager library. set(ODBC_FOUND FALSE) find_path(ODBC_INCLUDE_DIR sql.h /usr/include /usr/include/odbc /usr/local/include /usr/local/include/odbc /usr/local/odbc/include "C:/Program Files/ODBC/include" "C:/Program Files/Microsoft SDKs/Windows/v7.0A/include" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/include" "C:/Program Files/Microsoft SDKs/Windows/v6.0a/include" "C:/ODBC/include" DOC "Specify the directory containing sql.h." ) find_library(ODBC_LIBRARY NAMES iodbc odbc odbcinst odbc32 PATHS /usr/lib /usr/lib/odbc /usr/local/lib /usr/local/lib/odbc /usr/local/odbc/lib "C:/Program Files/ODBC/lib" "C:/ODBC/lib/debug" "C:/Program Files/Microsoft SDKs/Windows/v7.0A/Lib" "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib" DOC "Specify the ODBC driver manager library here." ) if(ODBC_LIBRARY) if(ODBC_INCLUDE_DIR) set( ODBC_FOUND 1 ) endif() endif() set(ODBC_LIBRARIES ${ODBC_LIBRARY}) mark_as_advanced(ODBC_FOUND ODBC_LIBRARY ODBC_EXTRA_LIBRARIES ODBC_INCLUDE_DIR) pvpgn-1.99.7.2.1+dfsg/cmake/Modules/FindPostgreSQL.cmake000066400000000000000000000040511462640461400224760ustar00rootroot00000000000000# - Find libpq # Find the native PostgreSQL includes and library # # PGSQL_INCLUDE_DIR - where to find libpq-fe.h, etc. # PGSQL_LIBRARIES - List of libraries when using PGSQL. # PGSQL_FOUND - True if PGSQL found. IF (PGSQL_INCLUDE_DIR) # Already in cache, be silent SET(PostgreSQL_FIND_QUIETLY TRUE) ENDIF (PGSQL_INCLUDE_DIR) # the registry settings checked are in order: # - for pgInstaller 8.2.x postgresql version SET(PGSQL_WIN_BASE "[HKEY_LOCAL_MACHINE\\SOFTWARE\\PostgreSQL\\Installations\\{1F701DBD-1660-4108-B10A-FB435EA63BF0};Base Directory]") IF(PGSQL_WIN_BASE) IF(MSVC) SET(PGSQL_SEARCH_LIB_PATHS "${PGSQL_WIN_BASE}/lib/ms") ELSE(MSVC) SET(PGSQL_SEARCH_LIB_PATHS "${PGSQL_WIN_BASE}/lib") ENDIF(MSVC) ENDIF(PGSQL_WIN_BASE) FIND_PATH(PGSQL_INCLUDE_DIR libpq-fe.h "[HKEY_LOCAL_MACHINE\\SOFTWARE\\PostgreSQL\\Installations\\{1F701DBD-1660-4108-B10A-FB435EA63BF0};Base Directory]/include" /usr/local/pgsql/include /usr/local/postgresql/include /usr/local/include/pgsql /usr/local/include/postgresql /usr/local/include /usr/include/pgsql /usr/include/postgresql /usr/include /usr/pgsql/include /usr/postgresql/include ) SET(PGSQL_NAMES pq libpq) SET(PGSQL_SEARCH_LIB_PATHS ${PGSQL_SEARCH_LIB_PATHS} /usr/local/pgsql/lib /usr/local/lib /usr/lib ) FIND_LIBRARY(PGSQL_LIBRARY NAMES ${PGSQL_NAMES} PATHS ${PGSQL_SEARCH_LIB_PATHS} ) IF (PGSQL_INCLUDE_DIR AND PGSQL_LIBRARY) SET(PGSQL_FOUND TRUE) SET( PGSQL_LIBRARIES ${PGSQL_LIBRARY} ) ELSE (PGSQL_INCLUDE_DIR AND PGSQL_LIBRARY) SET(PGSQL_FOUND FALSE) SET( PGSQL_LIBRARIES ) ENDIF (PGSQL_INCLUDE_DIR AND PGSQL_LIBRARY) IF (PGSQL_FOUND) IF (NOT PostgreSQL_FIND_QUIETLY) MESSAGE(STATUS "Found PostgreSQL: ${PGSQL_LIBRARY}") ENDIF (NOT PostgreSQL_FIND_QUIETLY) ELSE (PGSQL_FOUND) IF (PostgreSQL_FIND_REQUIRED) MESSAGE(STATUS "Looked for PostgreSQL libraries named ${PGSQL_NAMES}.") MESSAGE(FATAL_ERROR "Could NOT find PostgreSQL library") ENDIF (PostgreSQL_FIND_REQUIRED) ENDIF (PGSQL_FOUND) MARK_AS_ADVANCED( PGSQL_LIBRARY PGSQL_INCLUDE_DIR ) pvpgn-1.99.7.2.1+dfsg/cmake/Modules/FindSQLite3.cmake000066400000000000000000000025211462640461400217170ustar00rootroot00000000000000# - Find sqlite3 # Find the native SQLite3 includes and library # # SQLITE3_INCLUDE_DIR - where to find sqlite3.h # SQLITE3_LIBRARIES - List of libraries when using SQLite3. # SQLITE3_FOUND - True if SQLite3 found. IF (SQLITE3_INCLUDE_DIR) # Already in cache, be silent SET(SQLite3_FIND_QUIETLY TRUE) ENDIF (SQLITE3_INCLUDE_DIR) FIND_PATH(SQLITE3_INCLUDE_DIR sqlite3.h /usr/local/include/sqlite3 /usr/local/include /usr/include/sqlite3 /usr/include ${CMAKE_SOURCE_DIR}/sqlite3 ) SET(SQLITE3_NAMES sqlite3) FIND_LIBRARY(SQLITE3_LIBRARY NAMES ${SQLITE3_NAMES} PATHS /usr/local/lib /usr/lib ${CMAKE_SOURCE_DIR}/sqlite3 ) IF (SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY) SET(SQLITE3_FOUND TRUE) SET( SQLITE3_LIBRARIES ${SQLITE3_LIBRARY} ) ELSE (SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY) SET(SQLITE3_FOUND FALSE) SET( SQLITE3_LIBRARIES ) ENDIF (SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY) IF (SQLITE3_FOUND) IF (NOT SQLite3_FIND_QUIETLY) MESSAGE(STATUS "Found SQLite3: ${SQLITE3_LIBRARY}") ENDIF (NOT SQLite3_FIND_QUIETLY) ELSE (SQLITE3_FOUND) IF (SQLite3_FIND_REQUIRED) MESSAGE(STATUS "Looked for SQLite3 libraries named ${SQLITE3_NAMES}.") MESSAGE(FATAL_ERROR "Could NOT find SQLite3 library") ENDIF (SQLite3_FIND_REQUIRED) ENDIF (SQLITE3_FOUND) MARK_AS_ADVANCED( SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR ) pvpgn-1.99.7.2.1+dfsg/cmake/Modules/FindZLIB.cmake000066400000000000000000000025111462640461400212320ustar00rootroot00000000000000# - Find zlib # Find the native ZLIB includes and library # # ZLIB_INCLUDE_DIR - where to find zlib.h, etc. # ZLIB_LIBRARIES - List of libraries when using zlib. # ZLIB_FOUND - True if zlib found. IF (ZLIB_INCLUDE_DIR) # Already in cache, be silent SET(ZLIB_FIND_QUIETLY TRUE) ENDIF (ZLIB_INCLUDE_DIR) FIND_PATH(ZLIB_INCLUDE_DIR zlib.h "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]/include" /usr/local/include /usr/include ${CMAKE_SOURCE_DIR}/zlib ) SET(ZLIB_NAMES z zlib zdll zlibwapi) FIND_LIBRARY(ZLIB_LIBRARY NAMES ${ZLIB_NAMES} PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]/lib" /usr/lib /usr/local/lib ${CMAKE_SOURCE_DIR}/zlib ) IF (ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY) SET(ZLIB_FOUND TRUE) SET( ZLIB_LIBRARIES ${ZLIB_LIBRARY} ) ELSE (ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY) SET(ZLIB_FOUND FALSE) SET( ZLIB_LIBRARIES ) ENDIF (ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY) IF (ZLIB_FOUND) IF (NOT ZLIB_FIND_QUIETLY) MESSAGE(STATUS "Found ZLIB: ${ZLIB_LIBRARY}") ENDIF (NOT ZLIB_FIND_QUIETLY) ELSE (ZLIB_FOUND) IF (ZLIB_FIND_REQUIRED) MESSAGE(STATUS "Looked for Z libraries named ${ZLIB_NAMES}.") MESSAGE(FATAL_ERROR "Could NOT find z library") ENDIF (ZLIB_FIND_REQUIRED) ENDIF (ZLIB_FOUND) MARK_AS_ADVANCED( ZLIB_LIBRARY ZLIB_INCLUDE_DIR ) pvpgn-1.99.7.2.1+dfsg/cmake/Modules/cmake_purge.cmake.in000066400000000000000000000041201462640461400225560ustar00rootroot00000000000000if(WIN32) message(STATUS "Uninstalling \"@EXEC_INSTALL_PREFIX@\"") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove_directory \"@EXEC_INSTALL_PREFIX@\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing \"@EXEC_INSTALL_PREFIX@\"") endif(NOT "${rm_retval}" STREQUAL 0) else(WIN32) if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) #remove directories message(STATUS "Uninstalling \"@SYSCONF_INSTALL_DIR@\"") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove_directory \"@SYSCONF_INSTALL_DIR@\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing \"@SYSCONF_INSTALL_DIR@\"") endif(NOT "${rm_retval}" STREQUAL 0) message(STATUS "Uninstalling \"@LOCALSTATE_INSTALL_DIR@\"") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove_directory \"@LOCALSTATE_INSTALL_DIR@\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing \"@LOCALSTATE_INSTALL_DIR@\"") endif(NOT "${rm_retval}" STREQUAL 0) endif(WIN32)pvpgn-1.99.7.2.1+dfsg/cmake/Modules/cmake_uninstall.cmake.in000066400000000000000000000020121462640461400234430ustar00rootroot00000000000000if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file)pvpgn-1.99.7.2.1+dfsg/conf/000077500000000000000000000000001462640461400151255ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/CMakeLists.txt000066400000000000000000000031071462640461400176660ustar00rootroot00000000000000# generate the configs with proper line endings set(OUTPUT_CONFS ad.json anongame_infos.conf address_translation.conf autoupdate.conf bnalias.conf bnban.conf bnetd_default_user.plain bnissue.txt bnmaps.conf bnxpcalc.conf bnxplevel.conf channel.conf command_groups.conf realm.conf sql_DB_layout.conf supportfile.conf topics.conf tournament.conf versioncheck.json icons.conf) foreach(CONF ${OUTPUT_CONFS}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${CONF}.in ${CMAKE_CURRENT_BINARY_DIR}/${CONF} @ONLY) endforeach(CONF) # generate the config files replacing the path variables configure_file(${CMAKE_CURRENT_SOURCE_DIR}/bnetd.conf.in ${CMAKE_CURRENT_BINARY_DIR}/bnetd.conf) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/d2cs.conf.in ${CMAKE_CURRENT_BINARY_DIR}/d2cs.conf) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/d2dbs.conf.in ${CMAKE_CURRENT_BINARY_DIR}/d2dbs.conf) if(WITH_BNETD) set(BNETD_CONFS bnetd.conf ad.json anongame_infos.conf address_translation.conf autoupdate.conf bnalias.conf bnban.conf bnetd_default_user.plain bnissue.txt bnmaps.conf bnxpcalc.conf bnxplevel.conf channel.conf command_groups.conf realm.conf sql_DB_layout.conf supportfile.conf topics.conf tournament.conf versioncheck.json icons.conf) endif(WITH_BNETD) if(WITH_D2CS) set(D2CS_CONFS d2cs.conf anongame_infos.conf) endif(WITH_D2CS) if(WITH_D2DBS) set(D2DBS_CONFS d2dbs.conf) endif(WITH_D2DBS) foreach(CONF_FILE ${BNETD_CONFS} ${D2CS_CONFS} ${D2DBS_CONFS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CONF_FILE} DESTINATION ${SYSCONFDIR}) endforeach(CONF_FILE) add_subdirectory(i18n)pvpgn-1.99.7.2.1+dfsg/conf/ad.json.in000066400000000000000000000006041462640461400170110ustar00rootroot00000000000000{ "ads": [ {"filename": "ad000001.png", "url": "http://pvpgn.pro", "client": "W3XP", "lang": "NULL"}, {"filename": "ad000002.mng", "url": "http://pvpgn.pro", "client": "W3XP", "lang": "NULL"}, {"filename": "ad000001.smk", "url": "http://pvpgn.pro", "client": "NULL", "lang": "NULL"}, {"filename": "ad000002.smk", "url": "http://pvpgn.pro", "client": "NULL", "lang": "NULL"} ] }pvpgn-1.99.7.2.1+dfsg/conf/address_translation.conf.in000066400000000000000000000154051462640461400224510ustar00rootroot00000000000000######################################################################################################## #------------------------------------------------------------------------------------------------------# # Address Translation table # #---------------------------------------------------------------------------- # # DO NOT, I REPEAT, DO NOT TOUCH THIS FILE UNLESS YOU RUN THE SERVER ON A # COMPUTER INSIDE A LAN AND YOU NEED TO HAVE PLAYERS FROM "OUTSIDE" YOUR LAN # # This is a list of ip:port addresses that get translated # EVERYTHING AFTER A "#" IS IGNORED (ie COMMENTED) # # The entries are broken into four parts: # # * the IP address w/port to be translated (what pvpgn wants to send initially) # * the output IP address w/port (what pvpgn WILL send after translation) # * comma delimited list of networks to be excluded from translation # * comma delimited list of networks to be included in translation # # Each line must contain all four parts or the entire entry will be rejected # There are no default ports, make sure input and output include the port number # # The entries are searched in the order listed in this file # Exclude networks are searched before include networks # Only the first matching line is used # Lines begining with '#' are ignored # # If no match is found, the input ip will be returned. # # Network specification can be either NONE, ANY or x.y.z.t/bitlen: # - NONE = no neworks (same as 0.0.0.0/32) # - ANY = All networks (same as 0.0.0.0/0) # x.y.z.t/bitlen : means matching the first bitlen bits from the x.y.z.t value # Example: if you have the network 192.168.0.x (where x is from 1 to 255) to # match any IP in it you use "192.168.0.0/24" meaning the first 24 bits of # 192.168.0.0 HAVE TO match (the first 24 bits means the first 3 digits) # ################################################################################# ################################################################################ # w3route server ip translation # # Here you can translate the IP/port of the w3route IP/port as sent by pvpgn # to the WAR3 clients. You need this ONLY if you want to have external W3 # players that will play PlayGame/ArrangedTeam games. # Set exclude to the range of clients you want to recieve the input address # instead of the output address # Set include to the range of clients you want to recieve the output address # # input (ip:port) output (ip:port) exclude (ip/netmask) include (ip/netmask) #----------------- ------------------ ---------------------- ---------------------- # Example, if you left w3route = 0.0.0.0:6200 as it is by default in bnetd.conf # AND you have the external IP 1.2.3.4 AND you want to exclude from translation # the internal W3 clients (those with IPs 192.168.0.x) AND you port forward # port 6200 TCP from your router to the pvpgn server port 6200 then here put: # 0.0.0.0:6200 1.2.3.4:6200 192.168.0.0/24 ANY ############################################################################### # Game Translations for clients/games (client data ports) # # You need this ONLY if you will have internal players that want to play with # external ones. # # This setting is NOT NEEDED for D2 closed realm clients (they connect to d2gs # to play games not to eachover). # # Set input address to the client to be translated # Set output address to the address to be sent to the other clients # Set exclude to the range of clients you want to recieve the input address # instead of the output address # Set include to the range of clients you want to recieve the output address # # input (ip:port) output (ip:port) exclude (ip/netmask) include (ip/netmask) #----------------- ------------------ ---------------------- ---------------------- # Example, if you have an internal client on IP 192.168.1.10 AND you have the # external IP 1.2.3.4 AND you want to exclude from translation the internal # clients (those with IPs 192.168.1.x and 10.x.y.z) AND you port forward # port 6118 TCP AND UDP from your router to the client IP and port 6112 then # here put: #192.168.1.10:6112 1.2.3.4:6118 192.168.1.0/24,10.0.0.0/8 ANY ################################################################################ # Diablo II Character Server translation (d2cs) # # You need this ONLY if you run a d2cs (ie ONLY if you will host a Diablo2 # closed realm). # # Set input address to the ip of d2cs (same as the ip in the realm.conf) # Set output address to the address to be sent to the clients # Set exclude to the range of clients you want to recieve the input address # instead of the output address # Set include to the range of client you want to recieve the output address # # input (ip:port) output (ip:port) exclude (ip/netmask) include (ip/netmask) #----------------- ------------------ ---------------------- ---------------------- # Example, if you run a d2cs on IP 192.168.1.10 port 6113 and you have put the # same IP/port in realm.conf AND you have the external IP 1.2.3.4 AND you want # to exclude from translation the internal clients (those with IPs 192.168.1.x) # AND you port forward port 6113 TCP from your router to the machine running # d2cs and port 6113 then here put: #192.168.1.10:6113 1.2.3.4:6113 192.168.1.0/24 ANY ################################################################################ # Diablo II Game Server Translation (d2gs) # # Set input address to the ip of d2gs (same as the gameservlist in d2cs.conf) # Set output address to the address to be sent to the clients # Set exclude to the range of clients you want to recieve the input address # instead of the output address # Set include to the range of client you want to recieve the output address # # input (ip:port) output (ip:port) exclude (ip/netmask) include (ip/netmask) #----------------- ------------------ ---------------------- ---------------------- # Example, if you run a d2gs on IP 192.168.1.100 (and port 4000, d2gs cannot # listen on other port, it will automatically listen on port 4000) and you have # put the same IP in d2cs.conf and d2dbs.conf gameservlist setting AND you have # the external IP 1.2.3.4 AND you dont want to exclude from translation anyone # AND you port forward port 4000 TCP from your router to the machine running # d2gs and port 4000 then here put: #192.168.1.100:4000 1.2.3.4:4000 NONE ANY ################################################################################ # Westwood Online Game Server Translation (wwol) # # # input (ip:port) output (ip:port) exclude (ip/netmask) include (ip/netmask) #----------------- ------------------ ---------------------- ---------------------- #192.168.1.10:4005 1.2.3.4:4005 192.168.1.0/24 ANY pvpgn-1.99.7.2.1+dfsg/conf/anongame_infos.conf.in000066400000000000000000000210051462640461400213620ustar00rootroot00000000000000# # this file shows a sample config of a Frozen Throne ladder URL config # replace www.your-ladder-page.com with your actual ladder stats URL # [URL] server_URL = "http://www.your-ladder-page.com" player_URL = "http://www.your-ladder-page.com/?game=W3XP&user=" tourney_URL = "http://www.your-tourney-page.com" clan_URL = "http://www.your-clan-page/?clan=" ladder_PG_1v1_URL = "http://www.your-ladder-page.com/?game=W3XP&type=solo" ladder_PG_ffa_URL = "http://www.your-ladder-page.com/?game=W3XP&type=team" ladder_PG_team_URL = "http://www.your-ladder-page.com/?game=W3XP&type=ffa" ladder_AT_2v2_URL = "http://www.your-ladder-page.com/?game=W3XP&type=at" ladder_AT_3v3_URL = "http://www.your-ladder-page.com/?game=W3XP&type=at" ladder_AT_4v4_URL = "http://www.your-ladder-page.com/?game=W3XP&type=at" ladder_clan_1v1_URL = "http://www.your-ladder-page.com/?game=W3XP&type=clan_1v1" ladder_clan_2v2_URL = "http://www.your-ladder-page.com/?game=W3XP&type=clan_2v2" ladder_clan_3v3_URL = "http://www.your-ladder-page.com/?game=W3XP&type=clan_3v3" ladder_clan_4v4_URL = "http://www.your-ladder-page.com/?game=W3XP&type=clan_4v4" [DEFAULT_DESC] ladder_PG_1v1_desc = "Solo Games" ladder_PG_ffa_desc = "Free For all Games" ladder_PG_team_desc = "Team Games" ladder_AT_2v2_desc = "2 Player Team" ladder_AT_3v3_desc = "3 Player Team" ladder_AT_4v4_desc = "4 Player Team" ladder_clan_1v1_desc = "Solo Games" ladder_clan_2v2_desc = "2 Player Team" ladder_clan_3v3_desc = "3 Player Team" ladder_clan_4v4_desc = "4 Player Team" gametype_1v1_short = "One vs. One" gametype_1v1_long = "Two players fight to the death" gametype_2v2_short = "Two vs. Two" gametype_2v2_long = "Two teams of two vie for dominance" gametype_3v3_short = "Three vs. Three" gametype_3v3_long = "Two teams of three face off on the battlefield" gametype_4v4_short = "Four vs. Four" gametype_4v4_long = "Two teams of four head to battle" gametype_5v5_short = "Five vs. Five" gametype_5v5_long = "Two teams of five - who will prevail?" gametype_6v6_short = "Six vs. Six" gametype_6v6_long = "Two teams of six - get ready to rumble!" gametype_sffa_short = "Small Free for All" gametype_sffa_long = "Can you defeat 3-5 opponents alone?" gametype_tffa_short = "Team Free for All" gametype_tffa_long = "Can your team defeat 1-2 others?" gametype_2v2v2_short = "Two vs. Two vs. Two" gametype_2v2v2_long = "Three teams of two, can you handle it?" gametype_3v3v3_short = "Three vs. Three vs. Three" gametype_3v3v3_long = "Three teams of three battle each other" gametype_4v4v4_short = "Four vs. Four vs. Four" gametype_4v4v4_long = "Three teams of four - things getting crowded?" gametype_2v2v2v2_short = "Two vs. Two vs. Two vs. Two" gametype_2v2v2v2_long = "Four teams of two, is this a challenge?" gametype_3v3v3v3_short = "Three vs. Three vs. Three vs. Three" gametype_3v3v3v3_long = "Four teams of three, the ultimate challenge!" [deDE] ladder_PG_1v1_desc = "Solo-Spiele" ladder_PG_ffa_desc = "J.g.J.-Spiele" ladder_PG_team_desc = "Team-Spiele" ladder_AT_2v2_desc = "2 Spieler Team" ladder_AT_3v3_desc = "3 Spieler Team" ladder_AT_4v4_desc = "4 Spieler Team" ladder_clan_1v1_desc = "Solo-Spiele" ladder_clan_2v2_desc = "2 Spieler-Spiele" ladder_clan_3v3_desc = "3 Spieler-Spiele" ladder_clan_4v4_desc = "4 Spieler-Spiele" gametype_1v1_short = "Einer gegen Einen" gametype_1v1_long = "Zwei Spieler kämpfen bis zum Tod." gametype_2v2_short = "Zwei gegen Zwei" gametype_2v2_long = "Zwei Zweier-Teams ringen um die Vorherrschaft." gametype_3v3_short = "Drei gegen Drei" gametype_3v3_long = "Zwei Dreier-Teams stehen sich auf dem Schlachfeld gegenüber." gametype_4v4_short = "Vier gegen Vier" gametype_4v4_long = "Zwei Vierer-Teams stürmen in die Schlacht." gametype_5v5_short = "Fünf gegen Fünf" gametype_5v5_long = "Zwei Fünfer-Teams, wer wird vorherschen?" gametype_6v6_short = "Sechs gegen Sechs" gametype_6v6_long = "Zwei Sechser-Teams, macht euch bereit zur Schlacht!" gametype_sffa_short = "Jeder gegen Jeden - klein" gametype_sffa_long = "Können Sie allein 3-5 Gegener besiegen?" gametype_tffa_short = "Jeder gegen Jeden - Team" gametype_tffa_long = "Kann dein Team 1-2 andere besiegen?" gametype_2v2v2_short = "Zwei gegen Zwei gegen Zwei" gametype_2v2v2_long = "Drei Zweier-Teams, können sie das schaffen?" gametype_3v3v3_short = "Drei gegen Drei gegen Drei" gametype_3v3v3_long = "Drei Dreier-Teams bekämpfen sich gegenseitig" gametype_4v4v4_short = "Vier gegen Vier gegen Vier" gametype_4v4v4_long = "Drei Vierer-Teams - wirds langsam eng?" gametype_2v2v2v2_short = "2 gegen 2 gegen 2 gegen 2" gametype_2v2v2v2_long = "Vier Zweier-Teams, ist das eine Herausforderung?" gametype_3v3v3v3_short = "3 gegen 3 gegen 3 gegen 3" gametype_3v3v3v3_long = "Vier Dreier-Teams, die ultimative Herausforderung!" [zhCN] ladder_PG_1v1_desc = "å�•挑" ladder_PG_ffa_desc = "混战" ladder_PG_team_desc = "群殴" ladder_AT_2v2_desc = "两人队ä¼�" ladder_AT_3v3_desc = "三人队ä¼�" ladder_AT_4v4_desc = "四人队ä¼�" gametype_1v1_short = "1对1" gametype_1v1_long = "两个玩家一定è¦�分出个胜负。" gametype_2v2_short = "2对2" gametype_2v2_long = "两个队ä¼�展开对决。" gametype_3v3_short = "3对3" gametype_3v3_long = "两个三人队ä¼�进行对决。" gametype_4v4_short = "4对4" gametype_4v4_long = "两个四人队ä¼�进行对决。" gametype_5v5_short = "5对5" gametype_5v5_long = "两个五人队ä¼�进行对决。" gametype_6v6_short = "6对6" gametype_6v6_long = "两个六人队ä¼�进行对决。" gametype_sffa_short = " å°�型的FFA" gametype_sffa_long = "你能å�•独击败3到5个对手么?" gametype_tffa_short = " 组队的FFA" gametype_tffa_long = "你能和队å�‹å�ˆä½œå‡»è´¥1到2个对手么?" gametype_2v2v2_short = "2对2对2" gametype_2v2v2_long = "三个队ä¼�进行对决。" gametype_3v3v3_short = "3对3对3" gametype_3v3v3_long = "三个三人队ä¼�进行对决。" gametype_4v4v4_short = "4对4对4" gametype_4v4v4_long = "三个四人队ä¼�进行对决。" gametype_2v2v2v2_short = "2对2对2对2" gametype_2v2v2v2_long = "四个二人队ä¼�进行对决。" gametype_3v3v3v3_short = "3对3对3对3" gametype_3v3v3v3_long = "四个三人队ä¼�进行对决。" [ruRU] ladder_PG_1v1_desc = "Игры без правил" ladder_PG_ffa_desc = "Без правил" ladder_PG_team_desc = "Клановые игры" ladder_AT_2v2_desc = "2 Player Team" ladder_AT_3v3_desc = "3 Player Team" ladder_AT_4v4_desc = "4 Player Team" gametype_1v1_short = "1x1" gametype_1v1_long = "ДуÑ�ль двух игроков." gametype_2v2_short = "2x2" gametype_2v2_long = "Два враждующих клана, по 2 игрока в каждом." gametype_3v3_short = "3x3" gametype_3v3_long = "Два враждующих клана, по 3 игрока в каждом." gametype_4v4_short = "4x4" gametype_4v4_long = "Два враждующих клана, по 4 игрока в каждом." gametype_5v5_short = "5x5" gametype_5v5_long = "Два враждующих клана, по 5 игроков в каждом." gametype_6v6_short = "6x6" gametype_6v6_long = "Два враждующих клана, по 6 игроков в каждом." gametype_sffa_short = "МалаÑ� игра БП" gametype_sffa_long = "СправишÑ�Ñ� в одиночку Ñ� 3-5 противниками?" gametype_tffa_short = "Team Free for All" gametype_tffa_long = "СправишÑ�Ñ� Ñ� 1-2 противниками?" gametype_2v2v2_short = "2x2x2" gametype_2v2v2_long = "Три враждующих клана, по 2 игрока в каждом." gametype_3v3v3_short = "3x3x3" gametype_3v3v3_long = "Три враждующих клана, по 3 игрока в каждом." gametype_4v4v4_short = "4x4x4" gametype_4v4v4_long = "Три враждующих клана, по 4 игрока в каждом." gametype_2v2v2v2_short = "2x2x2x2" gametype_2v2v2v2_long = "Четыре враждующих клана, по 2 игрока в каждом." gametype_3v3v3v3_short = "3x3x3x3" gametype_3v3v3v3_long = "Четыре враждующих клана, по 3 игрока в каждом." [THUMBS_DOWN_LIMIT] PG_1v1 = 3 PG_2v2 = 2 PG_3v3 = 1 PG_4v4 = 1 PG_5v5 = 1 PG_6v6 = 1 PG_ffa = 2 AT_2v2 = 3 AT_3v3 = 2 AT_4v4 = 1 AT_ffa = 0 PG_2v2v2 = 0 PG_3v3v3 = 0 PG_4v4v4 = 0 PG_2v2v2v2 = 0 PG_3v3v3v3 = 0 [ICON_REQUIRED_RACE_WINS_WAR3] Level1 = 25 Level2 = 250 Level3 = 500 Level4 = 1500 [ICON_REQUIRED_RACE_WINS_W3XP] Level1 = 25 Level2 = 150 Level3 = 350 Level4 = 750 Level5 = 1500 [ICON_REQUIRED_TOURNEY_WINS] Level1 = 10 Level2 = 75 Level3 = 150 Level4 = 250 Level5 = 500 pvpgn-1.99.7.2.1+dfsg/conf/autoupdate.conf.in000066400000000000000000000667501462640461400205720ustar00rootroot00000000000000############################################################################## # autoupdate.conf - Auto Update Configuration # #----------------------------------------------------------------------------# # # # This is a list of client tags and their equivalent autoupdate files and # # version information. # # # # This file will be ignored for certain clients if skip_versioncheck is # # enabled in the bnetd.conf file. # # # # Only the first matching line is used. If no lines match then no upgrade # # will be attempted and the client will remain at its current version. # # # # Note: if you want to use the autoupdate wildcard entries, you'll also need # # to uncomment the corresponding wildcard entries in versioncheck.conf. # # # # Do not include "/" in any of the filenames. # # # # The versiontag is used to identify a particular version as specified in # # the versioncheck configuration file. # # # # It works like this: versioncheck identifies a version by reported version # # number, checksum, clienttag, etc. The autoupdate then uses the arch tag, # # client tag and version tag to find the needed update file. # # # ############################################################################## # Battle.net related informations # #----------------------------------------------------------------------------# # War3 and War3x clients have different mpq files for the different game # # languages. This is accounted for by the server, and the client will get # # the correct file. Only one entry is needed per arch, client, and version # # tag. # # # # Example: With the following line: # # # # IX86 W3XP W3XP_107A W3XP_IX86_1xx_111A.mpq # # # # war3x english clients will receive the file "W3XP_IX86_1xx_111A_enUS.mpq" # # where russian clients will receive the file "W3XP_IX86_1xx_111A_ruRU.mpq" # # and german clients will receive the file "W3XP_IX86_1xx_111A_deDE.mpq" # # Make sure all the required mpq files are in the files directory or # # autoupdate will not work. # # # # The avaliable language files are: # # # # enUS csCZ deDE esES frFR itIT jaJA koKR plPL ruRU zhCN zhTW # # # # The format of this file is as follows: # # # # archtag clienttag versiontag -----update file----- # # # ############################################################################## # Westwood Online related informations # #----------------------------------------------------------------------------# # For WOL autoupdate is used standart FTP protocol which is not included in # # PvPGN. The default WOL autoupdate entryes are writed according to # # westwood's autoupdate FTP server settings (username, password, filepath). # # # # WOLv2 using diferent rtp files for the diferent SKUs. This is accounted # # for by the server, and the client will get the correct file. Only one # # entry is needed per arch, client, and version tag. # # # # Example: With the following line: # # IX86 TSUN 65536 131075_65536.rtp tibsun # # # # TibSun clients with SKU 4608 will receive the file "131075_65536_4608.rtp" # # TibSun clients with SKU 4610 will receive the file "131075_65536_4610.rtp" # # # # The avaliable clients, theirs tags and SKUs are: # # # # Client name CTag SKUs # # --------------------------------------------------- # # Renegade RNGD 3072 3074 3075 3078 3081 3082 # # Dune 2000 DN2K 3584 3586 3587 3589 3591 # # Nox NOXX 4096 4098 4099 4101 4102 4105 # # Tiberian Sun TSUN 4608 4610 4611 4615 # # Red Alert 1v3 RALT 5376 5378 5379 # # Lands of Lore 3 LOR3 6400 6401 6402 6403 6405 # # Tiberian Sun XP TSXP 7168 7170 7171 7175 7424 7426 7427 7431 # # Emperor: BFD EBFD 7936 7938 7939 7945 7946 # # Red Alert 2 RAL2 8448 8450 8451 8457 8458 8960 8962 8963 8969 8970 # # Nox Quest NOXQ 9472 9474 9475 9477 9478 9481 # # Yuri's Revenge YURI 10496 10498 10499 10505 10506 # # Renegade Server RFDS 12288 # # WOL API WWOL 32512 # # # # Note: WOL update entry must including file path according to FTP server. # # All WOL entries using archtag IX86. # # # # The format of this file is as follows: # # # # archtag clienttag versiontag -----update file----- file path # # # ############################################################################## ##### Intel (IX86) ########################################################### # =============== WarCraft III - FT (Expansion) =============== #IX86 W3XP W3XP_107A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_110A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_111A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_112A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_113A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_113B W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_114A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_114B W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_115A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_116A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_117A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_118A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_119A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_119B W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_120A W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_120B W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_120C W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_120D W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_120E W3XP_IX86_1xx_122a_2.mpq #IX86 W3XP W3XP_121A W3XP_IX86_1xx_122a_2.mpq # Incremental update from version 1.21b #IX86 W3XP W3XP_121B W3XP_IX86_121B_122a_2.mpq # The next one is a WarCraft III - FT wildcard entry. #IX86 W3XP W3XP_1xx W3XP_IX86_1xx_122a_2.mpq # ==================== WarCraft III - RoC ===================== #IX86 WAR3 WAR3_107A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_110A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_111A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_112A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_113A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_113B WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_114A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_114B WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_115A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_116A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_117A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_118A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_119A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_119B WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_120A WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_120B WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_120C WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_120D WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_120E WAR3_IX86_1xx_122a_2.mpq #IX86 WAR3 WAR3_121A WAR3_IX86_1xx_122a_2.mpq # Incremental update from version 1.21b #IX86 WAR3 WAR3_121B WAR3_IX86_121B_122a_2.mpq # The next one is a WarCraft III - RoC wildcard entry. #IX86 WAR3 WAR3_1xx WAR3_IX86_1xx_122a_2.mpq # ===================== Diablo II Expansion (LoD) =========================== # Note: the following assumes that you want to upgrade all Diablo II clients # to version 1.11b. If you are still running a 1.10 or 1.09d realm, you # will need to modify these entries accordingly. #IX86 D2XP D2XP_107 D2XP_IX86_1xx_112A.mpq #IX86 D2XP D2XP_108 D2XP_IX86_1xx_112A.mpq #IX86 D2XP D2XP_109 D2XP_IX86_1xx_112A.mpq #IX86 D2XP D2XP_109B D2XP_IX86_1xx_112A.mpq #IX86 D2XP D2XP_109C D2XP_IX86_1xx_112A.mpq #IX86 D2XP D2XP_109D D2XP_IX86_1xx_112A.mpq #IX86 D2XP D2XP_110 D2XP_IX86_1xx_112A.mpq #IX86 D2XP D2XP_111 D2XP_IX86_1xx_112A.mpq # Incremental update from version 1.11a --> 1.11b #IX86 D2XP D2XP_111B D2XP_IX86_111B_112A.mpq # The next one is a Diablo II LoD wildcard entry. #IX86 D2XP D2XP_1xx D2XP_IX86_1xx_112A.mpq # ========================== Diablo II (DV) ================================= # Note: the following assumes that you want to upgrade all Diablo II clients # to version 1.11b. If you are still running a 1.10 or 1.09d realm, you # will need to modify these entries accordingly. #IX86 D2DV D2DV_100 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_101 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_103 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_104B D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_104C D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_105 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_105B D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_106 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_106B D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_108 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_109 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_109B D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_109C D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_109D D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_110 D2DV_IX86_1xx_112A.mpq #IX86 D2DV D2DV_111 D2DV_IX86_1xx_112A.mpq # Incremental update from version 1.11a --> 1.11b #IX86 D2DV D2DV_111B D2DV_IX86_111B_112A.mpq # The next one is a Diablo II wildcard entry. #IX86 D2DV D2DV_1xx D2DV_IX86_1xx_112A.mpq # ================ StarCraft Expansion (Brood War) ========================== #IX86 SEXP SEXP_104 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_104_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_105 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_105_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_106 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_106_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_107 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_107_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_108 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_108_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_108B SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_108B_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_109 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_109_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_110 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_110_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_111 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_111_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_111B SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_111B_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_112 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_112_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_112B SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_112B_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_113 SEXP_IX86_1xx_1161.mpq # unfortunately we don't have a SEXP_113_STAR in versioncheck.conf #IX86 SEXP SEXP_113B SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_113B_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_113C SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_113C_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_113D SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_113D_STAR SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_113E SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_113E SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_114 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_114 SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_115 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_115 SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_1151 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_1151 SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_1152 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_1152 SEXP_IX86_1xx_1161.mpq #IX86 SEXP SEXP_1153 SEXP_IX86_1xx_1161.mpq #IX86 STAR SEXP_1153 SEXP_IX86_1xx_1161.mpq # Incremental update from version 1.15.3 #IX86 SEXP SEXP_116 SEXP_IX86_116_1161.mpq #IX86 STAR SEXP_116 SEXP_IX86_116_1161.mpq # The next one is a Brood War wildcard entry. #IX86 SEXP SEXP_1xx SEXP_IX86_1xx_1161.mpq # ========================= StarCraft (original) ============================ #IX86 STAR STAR_100 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_101 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_102 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_103 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_104 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_105 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_106 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_107 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_108 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_108B STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_109 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_109B STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_110 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_111 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_111B STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_112 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_112B STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_113 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_113B STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_113C STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_113D STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_113E STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_114 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_115 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_1151 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_1152 STAR_IX86_1xx_1161.mpq #IX86 STAR STAR_1153 SEXP_IX86_1xx_1161.mpq # Incremental update from version 1.15.3 #IX86 STAR STAR_116 SEXP_IX86_116_1161.mpq # The next one is a Starcraft wildcard entry. #IX86 STAR STAR_1xx STAR_IX86_1xx_1161.mpq # ========================= Diablo (original) ============================== # note: Blizzard doesn't update client from 109 to 109b. #IX86 DRTL DRTL_108 DRTL_IX86_108_109.mpq # The next one is a Diablo wildcard entry. #IX86 DRTL DRTL_1xx DRTL_IX86_100_109.mpq # ========================= Warcraft II BNE =============================== #IX86 W2BN W2BN_200 W2BN_IX86_200_202.mpq #IX86 W2BN W2BN_201 W2BN_IX86_201_202.mpq ##### Mac OS X (XMAC) ######################################################## # ===================== Diablo II Expansion (LoD) =========================== # Note: the following assumes that you want to upgrade all Diablo II clients # to version 1.11b. If you are still running a 1.10 or 1.09d realm, you # will need to modify these entries accordingly. #XMAC D2XP D2XP_109D D2XP_XMAC_1xx_111B.mpq #XMAC D2XP D2XP_110 D2XP_XMAC_1xx_111B.mpq # Incremental update from version 1.11a --> 1.11b #XMAC D2XP D2XP_111 D2XP_XMAC_111A_111B.mpq # The next one is a Diablo II LoD wildcard entry. #XMAC D2XP D2XP_1xx D2XP_XMAC_1xx_111B.mpq # ========================== Diablo II (DV) ================================= # Note: the following assumes that you want to upgrade all Diablo II clients # to version 1.11b. If you are still running a 1.10 or 1.09d realm, you # will need to modify these entries accordingly. #XMAC D2DV D2DV_109D D2DV_XMAC_1xx_111B.mpq #XMAC D2DV D2DV_110 D2DV_XMAC_1xx_111B.mpq # Incremental update from version 1.11a --> 1.11b #XMAC D2DV D2DV_111 D2DV_XMAC_111A_111B.mpq # The next one is a Diablo II LoD wildcard entry. #XMAC D2DV D2DV_1xx D2DV_XMAC_1xx_111B.mpq # ================ StarCraft Expansion (Brood War) ========================== #XMAC SEXP SEXP_109B SEXP_XMAC_1xx_113E.mpq #XMAC SEXP SEXP_110 SEXP_XMAC_1xx_113E.mpq #XMAC STAR SEXP_110_STAR SEXP_XMAC_1xx_113E.mpq #XMAC SEXP SEXP_111 SEXP_XMAC_1xx_113E.mpq #XMAC STAR SEXP_111_STAR SEXP_XMAC_1xx_113E.mpq #XMAC SEXP SEXP_111B SEXP_XMAC_1xx_113E.mpq #XMAC STAR SEXP_111B_STAR SEXP_XMAC_1xx_113E.mpq #XMAC SEXP SEXP_112 SEXP_XMAC_1xx_113E.mpq #XMAC STAR SEXP_112_STAR SEXP_XMAC_1xx_113E.mpq #XMAC SEXP SEXP_113 SEXP_XMAC_1xx_113E.mpq #XMAC STAR SEXP_113_STAR SEXP_XMAC_1xx_113E.mpq #XMAC SEXP SEXP_113C SEXP_XMAC_1xx_113E.mpq #XMAC STAR SEXP_113C_STAR SEXP_XMAC_1xx_113E.mpq # Unfortunately versioncheck.conf does not contain entries for 1.13{a|b|d} # The next one is a Brood War wildcard entry. #XMAC SEXP SEXP_1xx SEXP_XMAC_1xx_113E.mpq # ========================= StarCraft (original) ============================ #XMAC STAR STAR_109B STAR_XMAC_1xx_113E.mpq #XMAC STAR STAR_110 STAR_XMAC_1xx_113E.mpq #XMAC STAR STAR_111 STAR_XMAC_1xx_113E.mpq #XMAC STAR STAR_111B STAR_XMAC_1xx_113E.mpq #XMAC STAR STAR_112 STAR_XMAC_1xx_113E.mpq # Unfortunately versioncheck.conf does not contain entries for 1.13{a-d} # The next one is a Starcraft wildcard entry. #XMAC STAR STAR_1xx STAR_XMAC_1xx_113E.mpq ##### Mac OS Classic (PMAC) ################################################## # ===================== Diablo II Expansion (LoD) =========================== # Note: the following assumes that you want to upgrade all Diablo II clients # to version 1.11b. If you are still running a 1.10 or 1.09d realm, you # will need to modify these entries accordingly. #PMAC D2XP D2XP_1xx D2XP_PMAC_1xx_111B.mpq # ========================== Diablo II (DV) ================================= # Note: the following assumes that you want to upgrade all Diablo II clients # to version 1.11b. If you are still running a 1.10 or 1.09d realm, you # will need to modify these entries accordingly. #PMAC D2DV D2DV_109D D2DV_PMAC_1xx_111B.mpq #PMAC D2DV D2DV_1xx D2DV_PMAC_1xx_111B.mpq # ================ StarCraft Expansion (Brood War) ========================== #PMAC SEXP SEXP_109B SEXP_PMAC_1xx_113E.mpq #PMAC SEXP SEXP_110 SEXP_PMAC_1xx_113E.mpq #PMAC STAR SEXP_110_STAR SEXP_PMAC_1xx_113E.mpq #PMAC SEXP SEXP_111 SEXP_PMAC_1xx_113E.mpq #PMAC STAR SEXP_111_STAR SEXP_PMAC_1xx_113E.mpq #PMAC SEXP SEXP_111B SEXP_PMAC_1xx_113E.mpq #PMAC STAR SEXP_111B_STAR SEXP_PMAC_1xx_113E.mpq #PMAC SEXP SEXP_112 SEXP_PMAC_1xx_113E.mpq #PMAC STAR SEXP_112_STAR SEXP_PMAC_1xx_113E.mpq # The next one is a Brood War wildcard entry. #PMAC SEXP SEXP_1xx SEXP_PMAC_1xx_113E.mpq # ========================= StarCraft (original) ============================ #PMAC STAR STAR_109B STAR_PMAC_1xx_113E.mpq #PMAC STAR STAR_110 STAR_PMAC_1xx_113E.mpq #PMAC STAR STAR_111 STAR_PMAC_1xx_113E.mpq #PMAC STAR STAR_111B STAR_PMAC_1xx_113E.mpq #PMAC STAR STAR_112 STAR_PMAC_1xx_113E.mpq # The next one is a Brood War wildcard entry. #PMAC STAR STAR_1xx STAR_PMAC_1xx_113E.mpq ##### Westwood Online update entries ######################################### # ========================== Westwood Online API ============================ #IX86 WWOL 65536 65551_65536.rtp wolapi #IX86 WWOL 65537 65551_65537.rtp wolapi #IX86 WWOL 65538 65551_65538.rtp wolapi #IX86 WWOL 65539 65551_65539.rtp wolapi #IX86 WWOL 65540 65551_65540.rtp wolapi #IX86 WWOL 65541 65551_65541.rtp wolapi #IX86 WWOL 65542 65551_65542.rtp wolapi #IX86 WWOL 65543 65551_65543.rtp wolapi #IX86 WWOL 65544 65551_65544.rtp wolapi #IX86 WWOL 65545 65551_65545.rtp wolapi #IX86 WWOL 65546 65551_65546.rtp wolapi #IX86 WWOL 65547 65551_65547.rtp wolapi #IX86 WWOL 65548 65551_65548.rtp wolapi #IX86 WWOL 65549 65551_65549.rtp wolapi #IX86 WWOL 65550 65551_65550.rtp wolapi #IX86 WWOL 65555 65556_65555.rtp wolapi # =============================== Renegade ================================== #IX86 RNGD 65538 65573_65538.rtp renegade #IX86 RNGD 65550 65573_65538.rtp renegade #IX86 RNGD 65551 65573_65538.rtp renegade #IX86 RNGD 65566 65573_65538.rtp renegade #IX86 RNGD 65567 65573_65538.rtp renegade #IX86 RNGD 65568 65573_65538.rtp renegade #IX86 RNGD 65570 65573_65570.rtp renegade #IX86 RNGD 65571 65573_65571.rtp renegade #IX86 RNGD 65572 65573_65572.rtp renegade # ==================== Renegade Free Dedicated Server ======================= #IX86 RFDS 65538 65572_65538.rtp renegade #IX86 RFDS 65550 65572_65550.rtp renegade #IX86 RFDS 65551 65572_65551.rtp renegade #IX86 RFDS 65553 65572_65553.rtp renegade #IX86 RFDS 65554 65572_65554.rtp renegade #IX86 RFDS 65566 65572_65566.rtp renegade #IX86 RFDS 65568 65572_65568.rtp renegade #IX86 RFDS 65570 65572_65570.rtp renegade #IX86 RFDS 65571 65572_65571.rtp renegade #IX86 RFDS 65572 65573_65572.rtp renegade # ============================= Tiberian Sun ================================ #IX86 TSUN 65536 131075_65536.rtp tibsun #IX86 TSUN 65541 131075_65536.rtp tibsun #IX86 TSUN 65542 131075_65536.rtp tibsun #IX86 TSUN 65543 131075_65536.rtp tibsun #IX86 TSUN 65544 131075_65536.rtp tibsun #IX86 TSUN 65545 131075_65536.rtp tibsun #IX86 TSUN 65546 131075_65536.rtp tibsun #IX86 TSUN 65547 131075_65536.rtp tibsun #IX86 TSUN 65548 131075_65548.rtp tibsun #IX86 TSUN 131072 131075_65548.rtp tibsun #IX86 TSUN 131074 131075_131074.rtp tibsun # ======================= Tiberian Sun: FireStorm =========================== #IX86 TSXP 131072 131074_131072.rtp tibsun #IX86 TSXP 131074 131075_131074.rtp tibsun # =============================== Dune 2000 ================================= #IX86 DN2K 65536 65542.rtp dune2k/1.006/1.000 #IX86 DN2K 65537 65542_65537.rtp dune2k/1.006/1.001 # ================================== NOX ==================================== #IX86 NOXX 65536 65538_65536.rtp nox #IX86 NOXX 65537 65538_65537.rtp nox # =============================== NOX: Quest ================================ #IX86 NOXQ 65536 65538_65536.rtp nox #IX86 NOXQ 65537 65538_65537.rtp nox #IX86 NOXQ 65538 65540_65538.rtp nox #IX86 NOXQ 65539 65540_65539.rtp nox # ======================= Emperor: Battle for Dune ========================== #IX86 EBFD 1 65545_1.rtp emperor #IX86 EBFD 65536 65545_65536.rtp emperor #IX86 EBFD 65537 65545_65537.rtp emperor #IX86 EBFD 65538 65545_65538.rtp emperor #IX86 EBFD 65539 65545_65539.rtp emperor #IX86 EBFD 65540 65545_65540.rtp emperor #IX86 EBFD 65541 65545_65541.rtp emperor #IX86 EBFD 65542 65545_65542.rtp emperor #IX86 EBFD 65543 65545_65543.rtp emperor #IX86 EBFD 65544 65545_65544.rtp emperor # ============================= Red Alert 2 ================================= #IX86 RAL2 65536 65542_65536.rtp ra2 #IX86 RAL2 65537 65542_65537.rtp ra2 #IX86 RAL2 65538 65542_65538.rtp ra2 #IX86 RAL2 65539 65542_65539.rtp ra2 #IX86 RAL2 65540 65542_65540.rtp ra2 #IX86 RAL2 65541 65542_65541.rtp ra2 # ============================= Yuri's Revenge ============================== #IX86 YURI 65536 65537_65536.rtp yuri # ============================= Lands of Lore 3 ============================= #IX86 LOR3 65536 65539_65536.rtp lore3/1.003 #IX86 LOR3 65537 65539_65537.rtp lore3/1.003 #IX86 LOR3 65538 65539_65538.rtp lore3/1.003 pvpgn-1.99.7.2.1+dfsg/conf/bnalias.conf.in000066400000000000000000000065511462640461400200210ustar00rootroot00000000000000############################################################################## # bnalias.list - List of Locally Defined Alias Commands # #----------------------------------------------------------------------------# # # # This file can be used to define shortcuts for often-used commands or for # # unusually long commands. It can also be used to make custom emotes which # # are especially useful for non-English servers. # # # # Lines starting with a # are considered to be comments. # # The format is: # # @ # # aliasname [aliasshortcut1 [aliasshortcut2 [...]]] # # [!]text or command to type as if the player typed it # # [!]%Iinformation to print to the player # # [!]%Eerror to print to the player # # # # Where ! is either an integer representing the number of arguments given to # # the alias by the user or an asterisk (*) meaning it will match any number # # of arguments. The plus sign (+) will match with any non-zero number of # # arguments. # # # # There may be as many lines as you want following the aliasname. If # # multiple lines match they will be executed in the order they are listed. # # # # The text or command is actually formatted text as used in the bnmotd file. # # It contains special formatting characters which can be used to specify the # # type of message or substitue special values (the hostname, the player's # # own name, and many more). These all start with a percent sign (%). There # # are also special formatters which subsitute arguments which were entered # # by the user. These start with a dollar sign. # # # # $* - substitute all arguments here # # $0 through $9 - substitute that argument here (0 means the command) # # ${43} - substitute that argument here (0 means the command) # # ${-19} - substitute arguments 0-19 here # # ${2-} - substitue all arguments from 2 until the end here # # # @ //doubt //dt //d [0]/me looks with doubt [1+]/me looks at ${1-} with doubt @ //numusers //nu [0]%IThere are %u users online. [0]%IThere are %g games online. [0]%IThere are %c channels online. [1+]%EThat command does not take any arguments. @ //askban //ab [2+]/w $1 Please ban ${2-}. @ //statsme //sm [0]/stats %u @ //pvpgn [*]/announce PVPGN rulez! # # ############################################################################## pvpgn-1.99.7.2.1+dfsg/conf/bnban.conf.in000066400000000000000000000003201462640461400174540ustar00rootroot00000000000000# an example wildcard-match entry #127.*.*.* # an example exact-match entry #127.0.0.1 # an example range-match entry #127.0.0.79-127.0.0.84 # two example network entries #127.0.0.0/8 #127.0.0.0/255.0.0.0 pvpgn-1.99.7.2.1+dfsg/conf/bnetd.conf.in000066400000000000000000000676321462640461400175130ustar00rootroot00000000000000############################################################################## # bnetd.conf - Configuration file for the main PvPGN service # #----------------------------------------------------------------------------# # # # ############ READ THIS ######### READ THIS ########## READ THIS ############ # This file is an example configuration that should work UNMODIFIED for most # # of the PvPGN setups and may require modification to suit your needs. # # Blank lines and everything after a "#" are ignored. Use quotes around # # values that contain spaces. # # # ############################################################################## ############################################################################## # Privileges to work under # # Use either the names from the corresponding files or numerical id's (with # # a leading '#'). If none are specified, the userid will not be changed. # #----------------------------------------------------------------------------# #effective_user = games #effective_group = games # effective_user = #12 # effective_group = #20 # # ############################################################################## ############################################################################## # Storage section # # storage_path will tell pvpgn how and where from/to to read/write accounts # # right now it supports 2 "drivers" : file and sql # # # # Syntax: # # * for plain file driver: # # storage_path = file:mode=plain;dir=;clan=;default=/path/to/default/account # # * for sql driver: # # storage_path = sql:variable=value;...;default=0 (0 is the default uid) # # # # Variables for sql can be: # # - "mode" : tells PVPGN the sql mode you will use (mysql/pgsql/etc..) # # - "host" : the database host # # - "port" : the TCP/IP port if needed # # - "socket" : the UNIX local socket if needed # # - "name" : database name # # - "user" : db username # # - "pass" : db password # # - "default" : specify the UID to use for the default account data # # - "prefix" : prefix to use for all pvpgn tables (default "") # # # # Examples: # # storage_path = "file:mode=plain;dir=${LOCALSTATEDIR}/users;clan=${LOCALSTATEDIR}/clans;team=${LOCALSTATEDIR}/teams;default=${SYSCONFDIR}/bnetd_default_user.plain" # storage_path = "sql:mode=mysql;host=127.0.0.1;name=PVPGN;user=pvpgn;pass=pvpgnrocks;default=0;prefix=pvpgn_" # storage_path = "sql:mode=pgsql;host=127.0.0.1;name=pvpgn;user=pvpgn;pass=pvpgnrocks;default=0;prefix=pvpgn_" # storage_path = "sql:mode=sqlite3;name=${LOCALSTATEDIR}/users.db;default=0;prefix=pvpgn_" # storage_path = "sql:mode=odbc;name=PVPGN;prefix=pvpgn_" #----------------------------------------------------------------------------# storage_path = "file:mode=plain;dir=${LOCALSTATEDIR}/users;clan=${LOCALSTATEDIR}/clans;team=${LOCALSTATEDIR}/teams;default=${SYSCONFDIR}/bnetd_default_user.plain" # # ############################################################################## ############################################################################## # File section # # The pidfile can be set to "" to turn it off. # # Use absolute paths in these lines to avoid problems! # #----------------------------------------------------------------------------# filedir = "${LOCALSTATEDIR}/files" scriptdir = "${LOCALSTATEDIR}/lua" reportdir = "${LOCALSTATEDIR}/reports" chanlogdir = "${LOCALSTATEDIR}/chanlogs" userlogdir = "${LOCALSTATEDIR}/userlogs" i18ndir = "${SYSCONFDIR}/i18n" issuefile = "${SYSCONFDIR}/bnissue.txt" channelfile = "${SYSCONFDIR}/channel.conf" adfile = "${SYSCONFDIR}/ad.json" topicfile = "${SYSCONFDIR}/topics.conf" ipbanfile = "${SYSCONFDIR}/bnban.conf" mpqfile = "${SYSCONFDIR}/autoupdate.conf" logfile = "${LOCALSTATEDIR}/bnetd.log" realmfile = "${SYSCONFDIR}/realm.conf" maildir = "${LOCALSTATEDIR}/bnmail" versioncheck_file = "${SYSCONFDIR}/versioncheck.json" mapsfile = "${SYSCONFDIR}/bnmaps.conf" xplevelfile = "${SYSCONFDIR}/bnxplevel.conf" xpcalcfile = "${SYSCONFDIR}/bnxpcalc.conf" #pidfile = "${LOCALSTATEDIR}/bnetd.pid" ladderdir = "${LOCALSTATEDIR}/ladders" command_groups_file = "${SYSCONFDIR}/command_groups.conf" tournament_file = "${SYSCONFDIR}/tournament.conf" statusdir = "${LOCALSTATEDIR}/status" aliasfile = "${SYSCONFDIR}/bnalias.conf" anongame_infos_file = "${SYSCONFDIR}/anongame_infos.conf" DBlayoutfile = "${SYSCONFDIR}/sql_DB_layout.conf" supportfile = "${SYSCONFDIR}/supportfile.conf" transfile = "${SYSCONFDIR}/address_translation.conf" customicons_file = "${SYSCONFDIR}/icons.conf" # # ############################################################################## ############################################################################## # Localized files realm server settings # #----------------------------------------------------------------------------# # These filenames can have translated versions in # the "i18ndir" directory specified above. # (there are also two downloadable files: chathelp-war3.txt, newaccount.txt) localizefile = common.xml motdfile = bnmotd.txt motdw3file = w3motd.txt newsfile = news.txt helpfile = bnhelp.conf tosfile = termsofservice.txt # Do localization by a game language or by user country? localize_by_country = true # # ############################################################################## ############################################################################## # Message logs # #----------------------------------------------------------------------------# # Multiple log levels can be defined by connecting them with a comma (,) # Available loglevels are: # none # trace # debug # info # warn # error # fatal loglevels = fatal,error,warn,info,debug,trace #loglevels = fatal,error,warn,info # # ############################################################################## ############################################################################## # D2CS realm server settings # #----------------------------------------------------------------------------# # Version of D2CS server to connect with (set to zero to disable version check) d2cs_version = 0 # Allow the D2CS server to change realm names? allow_d2cs_setname = true # # ############################################################################## ############################################################################## # Downloadable files # #----------------------------------------------------------------------------# # These filenames are reported directly to the client and are relative to # the "filedir" directory specified above. iconfile = "icons.bni" war3_iconfile = "icons-WAR3.bni" star_iconfile = "icons_STAR.bni" # # ############################################################################## ############################################################################## # Client verification and upgrades # #----------------------------------------------------------------------------# # This option lists the client types allowed to connect (only valid for # the bnet and wol protocol). The list is a comma separated list of any of # the following elements: # all : all client types allowed (default) # chat : client type "CHAT" allowed (used by some bot software) # dshr : client type Diablo 1 Shareware # drtl : client type Diablo 1 (Retail) # sshr : client type Starcraft Shareware # star : client type Starcraft # sexp : client type Starcraft Broodwar # w2bn : client type Warcraft II Battle.Net Edition # d2dv : client type Diablo 2 # d2xp : client type Diablo 2 LOD # war3 : client type Warcraft III (Reign Of Chaos) # w3xp : client type Warcraft III Frozen Throne # tsun : client type Tiberian Sun # tsxp : client type Tiberian Sun Firestorm # ralt : client type Red Alert (with 4 players internet patche) # ral2 : client type Red Alert 2 # dn2k : client type Dune 2000 # noxx : client type Nox # noxq : client type Nox Quest # rngd : client type Renegade # rfds : client type Renegade Free Dedicated Server # yuri : client type Yuri's Revenge # ebfd : client type Emperor: Battle for Dune # # Example: allowed_clients = war3,w3xp allowed_clients = all # If you enable the version checks but want to allow clients that don't pass # the checksum test then enable this. allow_bad_version = true # If you enable the version checks but want to allow clients that aren't # listed in the versioncheck configuration file then enable this. Unless # you have a very complete file or are very paranoid about cheaters this # is a good idea. allow_unknown_version = true # # ############################################################################## ############################################################################## # Time values # #----------------------------------------------------------------------------# # Time in seconds between account file updates, 0 means wait forever. usersync = 300 # Number of seconds of inactivity before file is unloaded from memory. # (only checked during account file updates) userflush = 3600 # Number of users checked for updates at once. Higher values make sense if you # either have very fast hardware or you don't have many number of accounts. # Lower values make sense if you have very high CPU usage on the system you run # the server (dont make it too low or your system will save accounts continously). # Modify this value ONLY if you know what you are doing!! userstep = 100 # Flush connected users? Set "false" and they will never flushed untill logout (it will consume more memory but less CPU usage, because of no queries to a file) userflush_connected = true # How often to send user latency tests in seconds. latency = 600 # How often to send null or keepalive packets in seconds. nullmsg = 120 # Amount of time to delay shutting down server in seconds. shutdown_delay = 300 # Amount of time delay period is decremented by either a SIGTERM or SIGINT # (control-c) signal in seconds. shutdown_decr = 60 # How often should bans be checked for expiration? (in seconds) #ipban_check_int = 30 # # ############################################################################## ############################################################################## # Policy options # #----------------------------------------------------------------------------# # If you don't want people to be able to create new accounts, set this to # false. new_accounts = true # Set this to the maximum number of accounts you want to allow to be # created on your server. A value of 0 means infinite and is the default. max_accounts = 0 # If someone attempts to log in more than once, should it kick off the old # login, or deny the new one? kick_old_login = true #kick_old_login = false # With no passwords, this is bad to have enabled --NonReal # load_new_account option has been eliminated and the functionality now is # always active in PvPGN # If a user is creating a new channel, should it be added automatically, or # prompt them first? ask_new_channel = true # Should a game report be written for every game played or just ladder # games? #report_all_games = false report_all_games = true # Should Diablo I/II reports be written? There are no winners/losers. report_diablo_games = false # Should games with passwords be hidden on the game list? hide_pass_games = true # Should games already started be hidden on the game list? (for heavily # loaded servers) hide_started_games = true # Should non-permanent channels hidden on the channel list? hide_temp_channels = true # Should any and all disconnects to be counted as losses? # (Turning this on will override the user's choice in ladder games!) disc_is_loss = false # List additional game types to be counted as ladder games # Curently allowed types: topvbot, melee, ffa, oneonone # Example: ladder_games = "topvbot,oneonone" ladder_games = "none" # If additional game types are configured (see above) to be counted as ladder # games then this setting configures a game name prefix to make only games # which match this game name prefix be counted as ladder. This allows to # still have normal games of the game types configured with "ladder_games" # directive. However if this setting is commented or "" then ALL games # which match the game types configured with "ladder_games" are to be # considered as ladder games. The prefix checking is CASE SENSITIVE! # Example: ladder_prefix = "ldr_" ladder_prefix = "" # Should all users be able to use the /con and /connections commands? enable_conn_all = true # Should client IP addresses (from /con, /games, /gameinfo, /netinfo) # be hidden from non-admins? hide_addr = false # Should private channel messages be logged to files in the chanlogdir # directory? (see channels.list for public channels) chanlog = false # Do you want to use the channel quota feature? quota = yes # The following options deal with flood prevention. # # How many lines do you accept in quota_time seconds? # (The default should allow 5 lines in 5 seconds, # longer time periods allow "bursts" of traffic before the quota is full.) quota_lines = 5 # must be between 1 and 100 lines quota_time = 5 # must be between 1 and 60 seconds # "virtual wrapping", so long lines count as multiple lines quota_wrapline = 40 # must be between 1 to 256 chars # absolute maximum characters allowed in a line quota_maxline = 200 # must be between 1 to 256 chars # # How many lines do you accept in quota_time seconds before user is # disconnected? # (According to Jung-woo, Dobae is a Korean term for flooding the game server... # it originally meant "to paint the wallpaper on a new or refurbished house"). # If it less than or equal to quota_lines, there is no warning before # disconnection so don't set this too low. quota_dobae = 10 # must be between 1 and 100 lines # Mail support mail_support = true mail_quota = 5 # Channel logging message log_notice = "*** Please note this channel is logged! ***" # Ban on repeated password fails against bruteforce password thieves # Fails required to get ip banned (0 to disable ban on password fail) passfail_count = 0 # Password fail IP ban duration (in seconds) passfail_bantime = 300 # Max users limit in private channels (0 = unlimited) maxusers_per_channel = 0 # # ############################################################################## ############################################################################## # Account configuration # #----------------------------------------------------------------------------# # Should account files be named by the account number or the player name? savebyname = true # Save the account data on logoff sync_on_logoff = false # How man rows should the account lookup hash table have? Servers with # more accounts should use a larger table for better performance. hashtable_size = 61 # Per default, only alphanumerical symbols are allowed in account names # with this variable you can add some extra symbols to be allowed # but be warned - that some of them might cause trouble - at least with # savebyname=true (some symbols are forbidden in filenames or might cause # you real trouble - plz neither allow wildcard symbols like '*' or '?'. # Path delimiters like '/' or '\' are hardcoded filtered and can't be allowed. # Also note that allowing the '.' might cause u some headache on win32 systems. # You have been warned - the rest is up to you. # default setting is "-_[]" as it was previous versions account_allowed_symbols = "-_[]" # This setting affects users that login with their uid rather than their # username. If set to true their displayed username will be forcefully # converted to their registered account name. account_force_username = false # maximum number of friends a user can add to there friends list # default setting is 20 max_friends = 20 # # ############################################################################## ############################################################################## # Tracking server info # #----------------------------------------------------------------------------# # Set track=0 to disable tracking. Any other number will set number # of seconds between sending tracking packets. This is ON by default. #track = 0 track = 0 # Tracking server(s) # Use a comma delimited list of hostnames with optional UDP port numbers # after colons. (port 6114 is the default for the newer tracking protocol) trackaddrs = "track.pvpgn.org,bntrack.darkwings.org,bnet.mivabe.nl,track.eurobattle.net" # Change these to match your system, for example: location = "unknown" description = "unknown" url = "https://github.com/pvpgn/pvpgn-server" contact_name = "a PvPGN user" contact_email = "unknown" # # ############################################################################## ############################################################################## # Server network info # #----------------------------------------------------------------------------# # Servername by which the server identifies itself (default: "PvPGN Realm") #servername = "PvPGN Realm" # Set this to the maximum number of concurrent connections allowed on # this server (minimum 32). This limit sets a general server connection # limit, NOT the concurrent user limit (for that see next option) max_connections = 1000 # Set maximum amount of packets in client packet queue # If limit is reached, client connection will be dropped # Set to 0 to disable packet_limit = 1000 # Maximum number of concurrent users (0 means unlimited). max_concurrent_logins = 0 # Set this option to true to allow TCP to detect and close stale # connections. use_keepalive = false # Limit maximum number of connections per IP (0 = unlimited) # this feature is new, so no recommended value so far # (d2cs connections are exempted from this limitation) max_conns_per_IP = 0 # This is a comma delimited list of hostnames that the server should # listen on. It might be useful to make an internal-only server on a # gateway machine for example. If the list is not set or if it has a # entry with no host component, the server will bind to that port on all # interfaces. #servaddrs = ":9999" #servaddrs = "myinternalname.some.com,localhost" servaddrs = ":" # default interface (all) and default port (6112) # Don't change these unless you really need to! You will need to run a proxy # or modify the clients. Also note that these will not change when simply # sending a HUP signal to the server; they are only read on startup. # This is the port the server send the UDP test packets to by default. # Setting it to zero makes the server use the same port as the TCP connection # comes from. Newer clients can override this setting on a per connection # basis. #udptest_port = 6112 # W3 Play Game router address. Just put your server address in here # or use 0.0.0.0:6200 for server to bind to all interfaces, # but make sure you set up w3trans if you do. w3routeaddr = "0.0.0.0:6200" # w3routeshow has been removed. # see the address_translation.conf for translating the w3route ip for local networks # initkill_timer sets up a periodic timer on init/defer class connections # this should detect and clean up stale connections to your server initkill_timer = 120 # # ############################################################################## ############################################################################## # Westwood Online (WOL) configuration # #----------------------------------------------------------------------------# # NOTE: WOL support is still experimental! # This specifies the addresses where WOL connections should be accepted. See # the description of servaddrs for formatting information. Leave this field # blank if you do not want to accept WOL connections. If the wolv1 port is # not specifed then 4005 will be used. Also if wolv2 port is not specifed # then 4000 will be used. # WOLv2 address includes servserv, game/chatting and ladder services. # # Note: DO NOT SET THE PORT TO ANYTHING OTHER THEN 4000 for WOLv1 or 4005 for # WOLv2, WOL WILL FAIL IF YOU DO! #wolv1addrs = ":4000" #wolv2addrs = ":4005" #wgameresaddrs = ":4807" #apiregaddrs = ":5400" # Just leave these as default (unless you know the timezone, longitiude and latitude # of your server woltimezone = "-8" wollongitude = "36.1083" wollatitude = "-115.0582" # These as default means that WOL autoupdate will use official WOL FTP server. # As "wol_update_serverhost" can be used also an IP address. FTP server is not # component of PvPGN so for that purpose can be used any FTP server. # Note that username and password is specifiable only in WOLv2 protocol. WOLv1 # will always use the default values. wol_autoupdate_serverhost = "westwood-patch.ea.com" wol_autoupdate_username = "update" wol_autoupdate_password = "world96" # # ############################################################################## ############################################################################## # Internet Relay Chat (IRC) configuration # #----------------------------------------------------------------------------# # NOTE: IRC support is still experimental! # This specifies the addresses where IRC connections should be accepted. See # the description of servaddrs for formatting information. Leave this field # blank if you do not want to accept IRC connections. If the port is not # specified then 6667 will be used. #ircaddrs = ":6667" # This is the IRC network name. If this is not specified then the default of # "PvPGN" will be used. #irc_network_name = "PvPGN" # This is the hostname used for IRC connections. Set this to your # hostname, if the automatic detection doesn't correctly. #hostname = "none" # Set this to the desired IRC connection timeout in seconds. #irc_latency = 180 # # ############################################################################## ############################################################################## # Telnet configuration # #----------------------------------------------------------------------------# # This specifies the addresses where telnet connections should be accepted. # See the description of servaddrs for formatting information. Leave this # field # blank if you do not want to accept telnet connections. If the port # is not specifed then 23 will be used. #telnetaddrs = ":23" ############################################################################### # war3 ladder textual output # #-----------------------------------------------------------------------------# # this is for all the guys, that want Warcraft 3 ladder, but don't want their # server to run with MySQL support. # For each ladder (solo, team, ffa, at) a corresponing file is created, # so it's easy to build your ladder pages with them # the following value determines, at which rate, these files are created # set to 0 if you don't want or need these files war3_ladder_update_secs = 300 # jfro's latest ladder is based on XML... so we can switch to XML output of ladder # on demand XML_output_ladder = false ############################################################################### # server status textual output # #-----------------------------------------------------------------------------# # This is for writing status of the server in an attempt to see number of user # on line actually, and games/chans. # This is store in file var\status\warcraft3.dat as a *.ini format. # Shouldn't be so hard in php to create dynamic website using this content. # the following value determines, at which rate, these files are created # set to 0 if you don't want or need these files output_update_secs = 60 # jfro's latest ladder is based on XML... so we can switch to XML output of ladder # on demand. Maybe we should set update interval bigger cause XML output version # is much more verbose than the standard output XML_status_output = false ############################################################################### # clan settings # #-----------------------------------------------------------------------------# # Time in hours for a new member of clan to be a newer(Peon icon, cannot promote to Grunt) # default value 168(7 days). If set to 0, all new members could be promote in no time clan_newer_time = 0 # max members count allowed in a clan, set between 10 and 100, default 50. clan_max_members = 50 # Default clan channel status when create a clan, 1 for private, 0 for public clan_channel_default_private = 0 # Minimum accepted invites for create new clan. 0 = clans will be created # without accepted invitations need. This does not affect WAR3/W3XP ingame # clan management. clan_min_invites = 2 ############################################################################### # user commands logging # #-----------------------------------------------------------------------------# # Should commands from users be logged to files in the userlogdir? log_commands = true # Commands will be logged for users with these command groups # (global operators and admins are included) log_command_groups = 2345678 # List of commands that will be logged # can be defined by connecting them with a comma (,) # set empty to log all commands log_command_list = "" pvpgn-1.99.7.2.1+dfsg/conf/bnetd.conf.win32000066400000000000000000000662131462640461400200410ustar00rootroot00000000000000############################################################################## # bnetd.conf - Configuration file for the Unix Battle.net daemon # #----------------------------------------------------------------------------# # # # This file is an example configuration and may require modification to # # suit your needs or your site. Blank lines and lines starting with a "#" # # are ignored. Use quotes around values that contain spaces. # # # ############################################################################## ############################################################################## # Storage section # # storage_path will tell pvpgn how and where from/to to read/write accounts # # right now it supports 2 "drivers" : file and sql # # # # Syntax: # # * for plain file driver: # # storage_path = file:mode=plain;dir=;clan=;team=;default=/path/to/default/account # # * for cdb file driver: # # storage_path = file:mode=cdb;dir=;clan=;team=;default=/path/to/default/account # # * for sql driver: # # storage_path = sql:variable=value;...;default=0 (0 is the default uid) # # # # Variables for sql can be: # # - "mode" : tells PVPGN the sql mode you will use (mysql/pgsql/etc..) # # - "host" : the database host # # - "port" : the TCP/IP port if needed # # - "socket" : the UNIX local socket if needed # # - "name" : database name # # - "user" : db username # # - "pass" : db password # # - "default" : specify the UID to use for the default account data # # - "prefix" : prefix to use for all pvpgn tables (default "") # # # # Examples: # # storage_path = file:mode=plain;dir=var\users;clan=var\clans;team=var\teams\;default=conf\bnetd_default_user.plain # storage_path = file:mode=cdb;dir=var\userscdb;clan=var\clans;team=var\teams\;default=conf\bnetd_default_user.cdb # storage_path = sql:mode=mysql;host=127.0.0.1;name=PVPGN;user=pvpgn;pass=pvpgnrocks;default=0;prefix=pvpgn_ # storage_path = sql:mode=pgsql;host=127.0.0.1;name=pvpgn;user=pvpgn;pass=pvpgnrocks;default=0;prefix=pvpgn_ # storage_path = sql:mode=sqlite3;name=var\users.db;default=0;prefix=pvpgn_ # storage_path = sql:mode=odbc;name=PVPGN;prefix=pvpgn_ #----------------------------------------------------------------------------# storage_path = file:mode=plain;dir=var\users;clan=var\clans;team=var\teams;default=conf\bnetd_default_user.plain # # ############################################################################## ############################################################################## # File section # # The pidfile can be set to "" to turn it off. # # Use absolute paths in these lines to avoid problems! # #----------------------------------------------------------------------------# filedir = files scriptdir = lua reportdir = var\reports chanlogdir = var\chanlogs userlogdir = var\userlogs i18ndir = conf\i18n issuefile = conf\bnissue.txt channelfile = conf\channel.conf adfile = conf\ad.json topicfile = conf\topics.conf ipbanfile = conf\bnban.conf transfile = conf\address_translation.conf mpqfile = conf\autoupdate.conf logfile = var\bnetd.log realmfile = conf\realm.conf versioncheck_file = conf\versioncheck.json mapsfile = conf\bnmaps.conf xplevelfile = conf\bnxplevel.conf xpcalcfile = conf\bnxpcalc.conf #pidfile = var\bnetd.pid ladderdir = var\ladders command_groups_file = conf\command_groups.conf statusdir = var\status aliasfile = conf\bnalias.conf anongame_infos_file = conf\anongame_infos.conf DBlayoutfile = conf\sql_DB_layout.conf supportfile = conf\supportfile.conf customicons_file = conf\icons.conf # # ############################################################################## ############################################################################## # Localized files realm server settings # #----------------------------------------------------------------------------# # These filenames can have translated versions in # the "i18ndir" directory specified above. # (there are also two downloadable files: chathelp-war3.txt, newaccount.txt) localizefile = common.xml motdfile = bnmotd.txt motdw3file = w3motd.txt newsfile = news.txt helpfile = bnhelp.conf tosfile = termsofservice.txt # Do localization by a game language or by user country? localize_by_country = true # # ############################################################################## ############################################################################## # Message logs # #----------------------------------------------------------------------------# # Multiple log levels can be defined by connecting them with a comma (,) # Available loglevels are: # none # trace # debug # info # warn # error # fatal #loglevels = fatal,error,warn,info,debug,trace loglevels = fatal,error,warn,info # # ############################################################################## ############################################################################## # D2CS realm server settings # #----------------------------------------------------------------------------# # Version of D2CS server to connect with (set to zero to disable version check) d2cs_version = 0 # Allow the D2CS server to change realm names? allow_d2cs_setname = true # # ############################################################################## ############################################################################## # Downloadable files # #----------------------------------------------------------------------------# # These filenames are reported directly to the client and are relative to # the "filedir" directory specified above. iconfile = "icons.bni" war3_iconfile = "icons-WAR3.bni" star_iconfile = "icons_STAR.bni" # # ############################################################################## ############################################################################## # Client verification and upgrades # #----------------------------------------------------------------------------# # This option lists the client types allowed to connect (only valid for # the bnet and wol protocol). The list is a comma separated list of any of # the following elements: # all : all client types allowed (default) # chat : client type "CHAT" allowed (used by some bot software) # dshr : client type Diablo 1 Shareware # drtl : client type Diablo 1 (Retail) # sshr : client type Starcraft Shareware # star : client type Starcraft # sexp : client type Starcraft Broodwar # w2bn : client type Warcraft II Battle.Net Edition # d2dv : client type Diablo 2 # d2xp : client type Diablo 2 LOD # war3 : client type Warcraft III (Reign Of Chaos) # w3xp : client type Warcraft III Frozen Throne # tsun : client type Tiberian Sun # tsxp : client type Tiberian Sun Firestorm # ralt : client type Red Alert (with 4 players internet patche) # ral2 : client type Red Alert 2 # dn2k : client type Dune 2000 # noxx : client type Nox # noxq : client type Nox Quest # rngd : client type Renegade # yuri : client type Yuri's Revenge # empr : client type Emperor: Battle for Dune # # Example: allowed_clients = war3,w3xp allowed_clients = all # If you enable the version checks but want to allow clients that don't pass # the checksum test then enable this. allow_bad_version = true # If you enable the version checks but want to allow clients that aren't # listed in the versioncheck configuration file then enable this. Unless # you have a very complete file or are very paranoid about cheaters this # is a good idea. allow_unknown_version = true # # ############################################################################## ############################################################################## # Time values # #----------------------------------------------------------------------------# # Time in seconds between account file updates, 0 means wait forever. usersync = 300 # Number of seconds of inactivity before file is unloaded from memory. # (only checked during account file updates) userflush = 3600 # Number of users checked for updates at once. Higher values make sense if you # either have very fast hardware or you don't have many number of accounts. # Lower values make sense if you have very high CPU usage on the system you run # the server (dont make it too low or your system will save accounts continously). # Modify this value ONLY if you know what you are doing!! userstep = 100 # Flush connected users? Set "false" and they will never flushed untill logout (it will consume more memory but less CPU usage, because of no queries to a file) userflush_connected = true # How often to send user latency tests in seconds. latency = 600 # How often to send null or keepalive packets in seconds. nullmsg = 120 # Amount of time to delay shutting down server in seconds. shutdown_delay = 300 # Amount of time delay period is decremented by either a SIGTERM or SIGINT # (control-c) signal in seconds. shutdown_decr = 60 # How often should bans be checked for expiration? (in seconds) #ipban_check_int = 30 # # ############################################################################## ############################################################################## # Policy options # #----------------------------------------------------------------------------# # If you don't want people to be able to create new accounts, set this to # false. new_accounts = true # Set this to the maximum number of accounts you want to allow to be # created on your server. A value of 0 means infinite and is the default. #max_accounts = 0 # If someone attempts to log in more than once, should it kick off the old # login, or deny the new one? kick_old_login = true #kick_old_login = false # With no passwords, this is bad to have enabled --NonReal # load_new_account option has been eliminated and the functionality now is # always active in PvPGN # If a user is creating a new channel, should it be added automatically, or # prompt them first? ask_new_channel = true # Should a game report be written for every game played or just ladder # games? #report_all_games = false report_all_games = true # Should Diablo I/II reports be written? There are no winners/losers. report_diablo_games = false # Should games with passwords be hidden on the game list? hide_pass_games = true # Should games already started be hidden on the game list? (for heavily # loaded servers) hide_started_games = true # Should non-permanent channels hidden on the channel list? hide_temp_channels = true # Should any and all disconnects to be counted as losses? # (Turning this on will override the user's choice in ladder games!) disc_is_loss = false # List additional game types to be counted as ladder games # Curently allowed types: topvbot, melee, ffa, oneonone # Example: ladder_games = "topvbot,oneonone" ladder_games = "none" # If additional game types are configured (see above) to be counted as ladder # games then this setting configures a game name prefix to make only games # which match this game name prefix be counted as ladder. This allows to # still have normal games of the game types configured with "ladder_games" # directive. However if this setting is commented or "" then ALL games # which match the game types configured with "ladder_games" are to be # considered as ladder games. The prefix checking is CASE SENSITIVE! # Example: ladder_prefix = "ldr_" ladder_prefix = "" # Should all users be able to use the /con and /connections commands? enable_conn_all = true # Should client IP addresses (from /con, /games, /gameinfo, /netinfo) # be hidden from non-admins? hide_addr = false # Should private channel messages be logged to files in the chanlogdir # directory? (see channels.list for public channels) chanlog = false # Do you want to use the channel quota feature? quota = yes # The following options deal with flood prevention. # # How many lines do you accept in quota_time seconds? # (The default should allow 5 lines in 5 seconds, # longer time periods allow "bursts" of traffic before the quota is full.) quota_lines = 5 # must be between 1 and 100 lines quota_time = 5 # must be between 1 and 60 seconds # "virtual wrapping", so long lines count as multiple lines quota_wrapline = 40 # must be between 1 to 256 chars # absolute maximum characters allowed in a line quota_maxline = 200 # must be between 1 to 256 chars # # How many lines do you accept in quota_time seconds before user is # disconnected? # (According to Jung-woo, Dobae is a Korean term for flooding the game server... # it originally meant "to paint the wallpaper on a new or refurbished house"). # If it less than or equal to quota_lines, there is no warning before # disconnection so don't set this too low. quota_dobae = 10 # must be between 1 and 100 lines # Mail support mail_support = true mail_quota = 5 # Channel logging message log_notice = "*** Please note this channel is logged! ***" # Ban on repeated password fails against bruteforce password thieves # Fails required to get ip banned (0 to disable ban on password fail) passfail_count = 0 # Password fail IP ban duration (in seconds) passfail_bantime = 300 # Max users limit in private channels (0 = unlimited) maxusers_per_channel = 0 # # ############################################################################## ############################################################################## # Account configuration # #----------------------------------------------------------------------------# # Should account files be named by the account number or the player name? savebyname = true # Save the account data on logoff sync_on_logoff = false # How man rows should the account lookup hash table have? Servers with # more accounts should use a larger table for better performance. hashtable_size = 61 # Per default, only alphanumerical symbols are allowed in account names # with this variable you can add some extra symbols to be allowed # but be warned - that some of them might cause trouble - at least with # savebyname=true (some symbols are forbidden in filenames or might cause # you real trouble - plz neither allow wildcard symbols like '*' or '?'. # Path delimiters like '/' or '\' are hardcoded filtered and can't be allowed. # Also note that allowing the '.' might cause u some headache on win32 systems. # You have been warned - the rest is up to you. # default setting is "-_[]" as it was previous versions account_allowed_symbols = "-_[]" # This setting affects users that login with their uid rather than their # username. If set to true their displayed username will be forcefully # converted to their registered account name. account_force_username = false # maximum number of friends a user can add to there friends list # default setting is 20 max_friends = 20 # # ############################################################################## ############################################################################## # Tracking server info # #----------------------------------------------------------------------------# # Set track=0 to disable tracking. Any other number will set number # of seconds between sending tracking packets. This is ON by default. #track = 0 track = 60 # Tracking server(s) # Use a comma delimited list of hostnames with optional UDP port numbers # after colons. (port 6114 is the default for the newer tracking protocol) trackaddrs = "track.pvpgn.org,bntrack.darkwings.org,bnet.mivabe.nl,track.eurobattle.net" # Change these to match your system, for example: location = "unknown" description = "unknown" url = "https://github.com/pvpgn/pvpgn-server" contact_name = "a PvPGN user" contact_email = "unknown" # # ############################################################################## ############################################################################## # Server network info # #----------------------------------------------------------------------------# # Servername by which the server identifies itself (default: "PvPGN Realm") #servername = "PvPGN Realm" # Set this to the maximum number of concurrent connections allowed on # this server (minimum 32). This limit sets a general server connection # limit, NOT the concurrent user limit (for that see next option) max_connections = 1000 # Set maximum amount of packets in client packet queue # If limit is reached, client connection will be dropped # Set to 0 to disable packet_limit = 1000 # Maximum number of concurrent users (0 means unlimited). max_concurrent_logins = 0 # Set this option to true to allow TCP to detect and close stale # connections. use_keepalive = false # Limit maximum number of connections per IP (0 = unlimited) # this feature is new, so no recommended value so far # (d2cs connections are exempted from this limitation) max_conns_per_IP = 0 # This is a comma delimited list of hostnames that the server should # listen on. It might be useful to make an internal-only server on a # gateway machine for example. If the list is not set or if it has a # entry with no host component, the server will bind to that port on all # interfaces. #servaddrs = ":9999" #servaddrs = "myinternalname.some.com,localhost" servaddrs = ":" # default interface (all) and default port (6112) # Don't change these unless you really need to! You will need to run a proxy # or modify the clients. Also note that these will not change when simply # sending a HUP signal to the server; they are only read on startup. # This is the port the server send the UDP test packets to by default. # Setting it to zero makes the server use the same port as the TCP connection # comes from. Newer clients can override this setting on a per connection # basis. #udptest_port = 6112 # W3 Play Game router address. Just put your server address in here # or use 0.0.0.0:6200 for server to bind to all interfaces, # but make sure you set up w3trans if you do. w3routeaddr = "0.0.0.0:6200" # w3routeshow has been removed. # see the address_translation.conf for translating the w3route ip for local networks # initkill_timer sets up a periodic timer on init/defer class connections # this should detect and clean up stale connections to your server initkill_timer = 120 # # ############################################################################## ############################################################################## # Westwood Online (WOL) configuration # #----------------------------------------------------------------------------# # NOTE: WOL support is still experimental! # This specifies the addresses where WOL connections should be accepted. See # the description of servaddrs for formatting information. Leave this field # blank if you do not want to accept WOL connections. If the wolv1 port is # not specifed then 4005 will be used. Also if wolv2 port is not specifed # then 4000 will be used. # WOLv2 address includes servserv, game/chatting and ladder services. # # Note: DO NOT SET THE PORT TO ANYTHING OTHER THEN 4000 for WOLv1 or 4005 for # WOLv2, WOL WILL FAIL IF YOU DO! #wolv1addrs = ":4000" #wolv2addrs = ":4005" #wgameresaddrs = ":4807" #apiregaddrs = ":5400" # Just leave these as default (unless you know the timezone, longitiude and latitude # of your server woltimezone = "-8" wollongitude = "36.1083" wollatitude = "-115.0582" # These as default means that WOL autoupdate will use official WOL FTP server. # As "wol_update_serverhost" can be used also an IP address. FTP server is not # component of PvPGN so for that purpose can be used any FTP server. # Note that username and password is specifiable only in WOLv2 protocol. WOLv1 # will always use the default values. wol_autoupdate_serverhost = "westwood-patch.ea.com" wol_autoupdate_username = "update" wol_autoupdate_password = "world96" # # ############################################################################## ############################################################################## # Internet Relay Chat (IRC) configuration # #----------------------------------------------------------------------------# # NOTE: IRC support is still experimental! # This specifies the addresses where IRC connections should be accepted. See # the description of servaddrs for formatting information. Leave this field # blank if you do not want to accept IRC connections. If the port is not # specifed then 6667 will be used. #ircaddrs = ":6667" # This is the IRC network name. If this is not specified then the default of # "PvPGN" will be used. #irc_network_name = "PvPGN" # This is the hostname used for IRC connections. Set this to your # hostname, if the automatic detection doesn't correctly. #hostname = "none" # Set this to the desired IRC connection timeout in seconds. #irc_latency = 180 # # ############################################################################## ############################################################################## # Telnet configuration # #----------------------------------------------------------------------------# # This specifies the addresses where telnet connections should be accepted. # See the description of servaddrs for formatting information. Leave this # field # blank if you do not want to accept telnet connections. If the port # is not specifed then 23 will be used. #telnetaddrs = ":23" # # ############################################################################## ############################################################################### # war3 ladder textual output # #-----------------------------------------------------------------------------# # this is for all the guys, that want Warcraft 3 ladder, but don't want their # server to run with MySQL support. # For each ladder (solo, team, ffa, at) a corresponing file is created, # so it's easy to build your ladder pages with them # the following value determines, at which rate, these files are created # set to 0 if you don't want or need these files war3_ladder_update_secs = 300 # jfro's latest ladder is based on XML... so we can switch to XML output of ladder # on demand. Maybe we should set update interval bigger cause XML output version # is much more verbose than the standard output XML_output_ladder = false ############################################################################### # server status textual output # #-----------------------------------------------------------------------------# # This is for writing status of the server in an attempt to see number of user # on line actually, and games/chans. # This is store in file var\status\warcraft3.dat as a *.ini format. # Shouldn't be so hard in php to create dynamic website using this content. # the following value determines, at which rate, these files are created # set to 0 if you don't want or need these files output_update_secs = 60 # jfro's latest ladder is based on XML... so we can switch to XML output of ladder # on demand. Maybe we should set update interval bigger cause XML output version # is much more verbose than the standard output XML_status_output = false ############################################################################### # clan settings # #-----------------------------------------------------------------------------# # Time in hours for a new member of clan to be a newer(Peon icon, cannot promote to Grunt) # default value 168(7 days). If set to 0, all new members could be promote in no time clan_newer_time = 0 # max members count allowed in a clan, set between 10 and 100, default 50. clan_max_members = 50 # Default clan channel status when create a clan, 1 for private, 0 for public clan_channel_default_private = 0 # Minimum accepted invites for create new clan. 0 = clans will be created # without accepted invitations need. This does not affect WAR3/W3XP ingame # clan management. clan_min_invites = 2 ############################################################################### # user commands logging # #-----------------------------------------------------------------------------# # Should commands from users be logged to files in the userlogdir? log_commands = true # Commands will be logged for users with these command groups # (global operators and admins are included) log_command_groups = 2345678 # List of commands that will be logged # can be defined by connecting them with a comma (,) # set empty to log all commands log_command_list = "" pvpgn-1.99.7.2.1+dfsg/conf/bnetd_default_user.plain.in000066400000000000000000000123621462640461400224210ustar00rootroot00000000000000############################################################################## # bnetd_default_user - Default values for user accounts # #----------------------------------------------------------------------------# # # # When a value is not stored in a player's account file, the value is taken # # from this file instead. # # # ############################################################################## ############################################################################## # Game record section # #----------------------------------------------------------------------------# # Brood War stats "Record\\SEXP\\0\\last game result"="NONE" "Record\\SEXP\\0\\last game"="0" "Record\\SEXP\\0\\disconnects"="0" "Record\\SEXP\\0\\losses"="0" "Record\\SEXP\\0\\wins"="0" "Record\\SEXP\\1\\last game result"="NONE" "Record\\SEXP\\1\\last game"="0" "Record\\SEXP\\1\\high rank"="0" "Record\\SEXP\\1\\rank"="0" "Record\\SEXP\\1\\high rating"="0" "Record\\SEXP\\1\\rating"="0" "Record\\SEXP\\1\\disconnects"="0" "Record\\SEXP\\1\\losses"="0" "Record\\SEXP\\1\\wins"="0" # Starcraft stats "Record\\Star\\0\\last game result"="NONE" "Record\\Star\\0\\last game"="0" "Record\\Star\\0\\disconnects"="0" "Record\\Star\\0\\losses"="0" "Record\\Star\\0\\wins"="0" "Record\\Star\\1\\last game result"="NONE" "Record\\Star\\1\\last game"="0" "Record\\Star\\1\\high rank"="0" "Record\\Star\\1\\rank"="0" "Record\\Star\\1\\high rating"="0" "Record\\Star\\1\\rating"="0" "Record\\Star\\1\\disconnects"="0" "Record\\Star\\1\\losses"="0" "Record\\Star\\1\\wins"="0" # Starcraft Shareware stats "Record\\SSHR\\0\\last game result"="NONE" "Record\\SSHR\\0\\last game"="0" "Record\\SSHR\\0\\disconnects"="0" "Record\\SSHR\\0\\losses"="0" "Record\\SSHR\\0\\wins"="0" # Diablo Retail stats "BNET\\Record\\DRTL\\0\\level"="0" "BNET\\Record\\DRTL\\0\\class"="99" "BNET\\Record\\DRTL\\0\\diablo kills"="0" "BNET\\Record\\DRTL\\0\\strength"="0" "BNET\\Record\\DRTL\\0\\magic"="0" "BNET\\Record\\DRTL\\0\\dexterity"="0" "BNET\\Record\\DRTL\\0\\vitality"="0" "BNET\\Record\\DRTL\\0\\gold"="0" # WarCraft II BNE stats "Record\\W2BN\\0\\last game result"="NONE" "Record\\W2BN\\0\\last game"="0" "Record\\W2BN\\0\\disconnects"="0" "Record\\W2BN\\0\\losses"="0" "Record\\W2BN\\0\\wins"="0" "Record\\W2BN\\1\\last game result"="NONE" "Record\\W2BN\\1\\last game"="0" "Record\\W2BN\\1\\high rank"="0" "Record\\W2BN\\1\\rank"="0" "Record\\W2BN\\1\\high rating"="0" "Record\\W2BN\\1\\rating"="0" "Record\\W2BN\\1\\disconnects"="0" "Record\\W2BN\\1\\losses"="0" "Record\\W2BN\\1\\wins"="0" # FIXME: what is 2 for? "Record\\W2BN\\3\\last game result"="NONE" "Record\\W2BN\\3\\last game"="0" "Record\\W2BN\\3\\high rank"="0" "Record\\W2BN\\3\\rank"="0" "Record\\W2BN\\3\\high rating"="0" "Record\\W2BN\\3\\rating"="0" "Record\\W2BN\\3\\disconnects"="0" "Record\\W2BN\\3\\losses"="0" "Record\\W2BN\\3\\wins"="0" # # ############################################################################## ############################################################################## # Default profile info # #----------------------------------------------------------------------------# # No good defaults for these :) "profile\\description"="" "profile\\location"="" "profile\\age"="" "profile\\sex"="" # These 3 are ignored in the template, but they make it easier # to use this file by hand to create a new account... "BNET\\acct\\username"="bnetd_default_user" "BNET\\acct\\userid"="0" "BNET\\acct\\passhash1"="67452301efcdab8998badcfe10325476c3d2e1f0" #"BNET\\fakeclienttag"="WAR3" # # ############################################################################## ############################################################################## # Authorization flags (comments show defaults) # #----------------------------------------------------------------------------# # "BNET\\auth\\admin"="false" "BNET\\auth\\command_groups"="1" # "BNET\\auth\\announce"="false" # "BNET\\auth\\botlogin"="false" # "BNET\\auth\\operator"="false" "BNET\\auth\\normallogin"="true" "BNET\\auth\\changepass"="true" "BNET\\auth\\changeprofile"="true" # "BNET\\auth\\createnormalgame"="true" # "BNET\\auth\\joinnormalgame"="true" # "BNET\\auth\\createladdergame"="true" # "BNET\\auth\\joinladdergame"="true" # "BNET\\auth\\lock"="false" # Specific channels can be specified to override the # channel defaults and the plain operator setting: # (use the long channel name) # "BNET\\auth\\operator\\example channel"="false" "BNET\\auth\\botlogin"="true" # "BNET\\auth\\operator"="false" # Set to "false" to not check IP, set to IP # (like: "192.168.1.2" to require an IP to login for warcraft3) "BNET\\auth\\w3_require_ip"="false" # # ############################################################################## pvpgn-1.99.7.2.1+dfsg/conf/bnissue.txt.in000066400000000000000000000001551462640461400177440ustar00rootroot00000000000000BOT or Telnet Connection from [%r] Enter your account name and password. Sorry, there is no guest account. pvpgn-1.99.7.2.1+dfsg/conf/bnmaps.conf.in000066400000000000000000000272321462640461400176670ustar00rootroot00000000000000######### WARCRAFT 3 REIGN OF CHAOS ################# WAR3 1v1 Maps\(2)PlunderIsle.w3m WAR3 1v1 Maps\(4)Frostsabre.w3m WAR3 1v1 Maps\(4)Legends.w3m WAR3 1v1 Maps\(4)LostTemple.w3m WAR3 1v1 Maps\(4)TranquilPaths.w3m WAR3 1v1 Maps\(6)GnollWood.w3m WAR3 1v1 Maps\(6)Moonglade.w3m WAR3 1v1 Maps\(6)ScorchedBasin.w3m WAR3 2v2 Maps\(4)Duskwood.w3m WAR3 2v2 Maps\(4)HarvestMoon.w3m WAR3 2v2 Maps\(4)LostTemple.w3m WAR3 2v2 Maps\(6)GnollWood.w3m WAR3 2v2 Maps\(6)ScorchedBasin.w3m WAR3 2v2 Maps\(6)Stromguarde.w3m WAR3 2v2 Maps\(6)SwampOfSorrows.w3m WAR3 2v2 Maps\(8)GolemsInTheMist.w3m WAR3 2v2 Maps\(8)TheCrucible.w3m WAR3 3v3 Maps\(6)DarkForest.w3m WAR3 3v3 Maps\(6)DragonFire.w3m WAR3 3v3 Maps\(6)Stromguarde.w3m WAR3 3v3 Maps\(6)SwampOfSorrows.w3m WAR3 3v3 Maps\(6)TimbermawHold.w3m WAR3 3v3 Maps\(8)Battleground.w3m WAR3 3v3 Maps\(8)PlainsOfSnow.w3m WAR3 3v3 Maps\(8)TheCrucible.w3m WAR3 4v4 Maps\(8)BlastedLands.w3m WAR3 4v4 Maps\(8)BloodvenomFalls.w3m WAR3 4v4 Maps\(8)GardenOfWar.w3m WAR3 4v4 Maps\(8)GolemsInTheMist.w3m WAR3 4v4 Maps\(8)PetrifiedForest.w3m WAR3 4v4 Maps\(8)PlainsOfSnow.w3m WAR3 4v4 Maps\(8)TheCrucible.w3m WAR3 sffa Maps\(4)HarvestMoon.w3m WAR3 sffa Maps\(4)LostTemple.w3m WAR3 sffa Maps\(4)MysticIsles.w3m WAR3 sffa Maps\(6)GnollWood.w3m WAR3 sffa Maps\(6)ScorchedBasin.w3m WAR3 sffa Maps\(6)Stromguarde.w3m WAR3 sffa Maps\(6)SwampOfSorrows.w3m WAR3 sffa Maps\(8)Battleground.w3m WAR3 sffa Maps\(8)TheCrucible.w3m WAR3 at2v2 Maps\(4)Duskwood.w3m WAR3 at2v2 Maps\(4)HarvestMoon.w3m WAR3 at2v2 Maps\(4)LostTemple.w3m WAR3 at2v2 Maps\(6)GnollWood.w3m WAR3 at2v2 Maps\(6)ScorchedBasin.w3m WAR3 at2v2 Maps\(6)Stromguarde.w3m WAR3 at2v2 Maps\(6)SwampOfSorrows.w3m WAR3 at2v2 Maps\(8)GolemsInTheMist.w3m WAR3 at2v2 Maps\(8)TheCrucible.w3m WAR3 at3v3 Maps\(6)DarkForest.w3m WAR3 at3v3 Maps\(6)DragonFire.w3m WAR3 at3v3 Maps\(6)Stromguarde.w3m WAR3 at3v3 Maps\(6)SwampOfSorrows.w3m WAR3 at3v3 Maps\(6)TimbermawHold.w3m WAR3 at3v3 Maps\(8)Battleground.w3m WAR3 at3v3 Maps\(8)PlainsOfSnow.w3m WAR3 at3v3 Maps\(8)TheCrucible.w3m WAR3 at4v4 Maps\(8)BlastedLands.w3m WAR3 at4v4 Maps\(8)BloodvenomFalls.w3m WAR3 at4v4 Maps\(8)GardenOfWar.w3m WAR3 at4v4 Maps\(8)GolemsInTheMist.w3m WAR3 at4v4 Maps\(8)PetrifiedForest.w3m WAR3 at4v4 Maps\(8)PlainsOfSnow.w3m WAR3 at4v4 Maps\(8)TheCrucible.w3m WAR3 tffa Maps\(4)HarvestMoon.w3m WAR3 tffa Maps\(4)LostTemple.w3m WAR3 tffa Maps\(4)MysticIsles.w3m WAR3 tffa Maps\(6)GnollWood.w3m WAR3 tffa Maps\(6)ScorchedBasin.w3m WAR3 tffa Maps\(6)Stromguarde.w3m WAR3 tffa Maps\(6)SwampOfSorrows.w3m WAR3 tffa Maps\(8)Battleground.w3m WAR3 tffa Maps\(8)TheCrucible.w3m WAR3 5v5 Maps\(10)DustwallowKeys.w3m WAR3 5v5 Maps\(12)DivideAndConquer.w3m WAR3 5v5 Maps\(12)IceCrown.w3m WAR3 6v6 Maps\(12)DivideAndConquer.w3m WAR3 6v6 Maps\(12)IceCrown.w3m WAR3 2v2v2 Maps\(6)DarkForest.w3m WAR3 2v2v2 Maps\(6)DragonFire.w3m WAR3 2v2v2 Maps\(6)Stromguarde.w3m WAR3 2v2v2 Maps\(6)SwampOfSorrows.w3m WAR3 2v2v2 Maps\(6)TimbermawHold.w3m WAR3 2v2v2 Maps\(8)Battleground.w3m WAR3 2v2v2 Maps\(8)PlainsOfSnow.w3m WAR3 2v2v2 Maps\(8)TheCrucible.w3m WAR3 3v3v3 Maps\(9)Riverrun.w3m WAR3 3v3v3 Maps\(10)DustwallowKeys.w3m WAR3 3v3v3 Maps\(12)DivideAndConquer.w3m WAR3 3v3v3 Maps\(12)IceCrown.w3m WAR3 4v4v4 Maps\(12)DivideAndConquer.w3m WAR3 4v4v4 Maps\(12)IceCrown.w3m WAR3 2v2v2v2 Maps\(8)BlastedLands.w3m WAR3 2v2v2v2 Maps\(8)BloodvenomFalls.w3m WAR3 2v2v2v2 Maps\(8)GardenOfWar.w3m WAR3 2v2v2v2 Maps\(8)GolemsInTheMist.w3m WAR3 2v2v2v2 Maps\(8)PetrifiedForest.w3m WAR3 2v2v2v2 Maps\(8)PlainsOfSnow.w3m WAR3 2v2v2v2 Maps\(8)TheCrucible.w3m WAR3 3v3v3v3 Maps\(12)DivideAndConquer.w3m WAR3 3v3v3v3 Maps\(12)IceCrown.w3m WAR3 at2v2v2 Maps\(6)DarkForest.w3m WAR3 at2v2v2 Maps\(6)DragonFire.w3m WAR3 at2v2v2 Maps\(6)Stromguarde.w3m WAR3 at2v2v2 Maps\(6)SwampOfSorrows.w3m WAR3 at2v2v2 Maps\(6)TimbermawHold.w3m WAR3 at2v2v2 Maps\(8)Battleground.w3m WAR3 at2v2v2 Maps\(8)PlainsOfSnow.w3m WAR3 at2v2v2 Maps\(8)TheCrucible.w3m ######### WARCRAFT 3 THE FROZEN THRONE ################# W3XP 1v1 Maps\FrozenThrone\(2)SecretValley.w3x W3XP 1v1 Maps\FrozenThrone\(2)MeltingValley.w3x W3XP 1v1 Maps\FrozenThrone\(2)RoadToStratholme.w3x W3XP 1v1 Maps\FrozenThrone\(2)EchoIsles.w3x W3XP 1v1 Maps\FrozenThrone\(2)TerenasStand.w3x W3XP 1v1 Maps\FrozenThrone\(2)TirisfalGlades.w3x W3XP 1v1 Maps\FrozenThrone\(4)BrokenShard.w3x W3XP 1v1 Maps\FrozenThrone\(4)CentaurGrove.w3x W3XP 1v1 Maps\(4)LostTemple.w3m W3XP 1v1 Maps\FrozenThrone\(4)TidewaterGlades.w3x W3XP 1v1 Maps\FrozenThrone\(4)TurtleRock.w3x W3XP 1v1 Maps\FrozenThrone\(4)TwistedMeadows.w3x W3XP 1v1 Maps\FrozenThrone\(6)GnollWood.w3x W3XP 2v2 Maps\FrozenThrone\(4)CentaurGrove.w3x W3XP 2v2 Maps\FrozenThrone\(4)ExcavationSite.w3x W3XP 2v2 Maps\FrozenThrone\(4)Goldshire.w3x W3XP 2v2 Maps\(4)LostTemple.w3m W3XP 2v2 Maps\FrozenThrone\(4)PhantomGrove.w3x W3XP 2v2 Maps\(4)TranquilPaths.w3m W3XP 2v2 Maps\FrozenThrone\(4)TidewaterGlades.w3x W3XP 2v2 Maps\FrozenThrone\(4)TurtleRock.w3x W3XP 2v2 Maps\FrozenThrone\(4)TwistedMeadows.w3x W3XP 2v2 Maps\FrozenThrone\(4)Wetlands.w3x W3XP 2v2 Maps\FrozenThrone\(6)GnollWood.w3x W3XP 3v3 Maps\FrozenThrone\(6)BloodstoneMesa.w3x W3XP 3v3 Maps\FrozenThrone\(6)CopperCanyon.w3x W3XP 3v3 Maps\FrozenThrone\(6)Dragonblight.w3x W3XP 3v3 Maps\FrozenThrone\(6)Everfrost.w3x W3XP 3v3 Maps\FrozenThrone\(6)GnollWood.w3x W3XP 3v3 Maps\FrozenThrone\(6)Highperch.w3x W3XP 3v3 Maps\FrozenThrone\(6)Monsoon.w3x W3XP 3v3 Maps\FrozenThrone\(6)RiceFields.w3x W3XP 3v3 Maps\FrozenThrone\(6)SilverpineForest.w3x W3XP 3v3 Maps\FrozenThrone\(6)SunrockCove.w3x W3XP 3v3 Maps\FrozenThrone\(6)Typhoon.w3x W3XP 3v3 Maps\FrozenThrone\(6)UpperKingdom.w3x W3XP 4v4 Maps\FrozenThrone\(8)Battleground.w3x W3XP 4v4 Maps\FrozenThrone\(8)Cherryville.w3x W3XP 4v4 Maps\FrozenThrone\(8)Deadlock.w3x W3XP 4v4 Maps\FrozenThrone\(8)DragonFalls.w3x W3XP 4v4 Maps\FrozenThrone\(8)Friends.w3x W3XP 4v4 Maps\FrozenThrone\(8)FullScaleAssault.w3x W3XP 4v4 Maps\FrozenThrone\(8)GoldRush.w3x W3XP 4v4 Maps\(8)GolemsInTheMist.w3m W3XP 4v4 Maps\FrozenThrone\(8)HurricaneIsle.w3x W3XP 4v4 Maps\FrozenThrone\(8)MarketSquare.w3x W3XP 4v4 Maps\FrozenThrone\(8)Mur'gulOasis.w3x W3XP 4v4 Maps\FrozenThrone\(8)Slalom.w3x W3XP sffa Maps\FrozenThrone\(4)Deathrose.w3x W3XP sffa Maps\FrozenThrone\(4)TwistedMeadows.w3x W3XP sffa Maps\FrozenThrone\(6)Duststorm.w3x W3XP sffa Maps\FrozenThrone\(6)EmeraldShores.w3x W3XP sffa Maps\FrozenThrone\(6)Monsoon.w3x W3XP sffa Maps\FrozenThrone\(6)SilverpineForest.w3x W3XP sffa Maps\FrozenThrone\(8)Deadlock.w3x W3XP sffa Maps\FrozenThrone\(8)GoldRush.w3x W3XP sffa Maps\FrozenThrone\(8)HurricaneIsle.w3x W3XP sffa Maps\FrozenThrone\(8)MarketSquare.w3x W3XP sffa Maps\FrozenThrone\(8)Mur'gulOasis.w3x W3XP sffa Maps\FrozenThrone\(8)TwilightRuins.w3x W3XP at2v2 Maps\FrozenThrone\(4)Avalanche.w3x W3XP at2v2 Maps\FrozenThrone\(4)LostTemple.w3x W3XP at2v2 Maps\FrozenThrone\(4)Predators.w3x W3XP at2v2 Maps\FrozenThrone\(4)StoneColdMountain.w3x W3XP at2v2 Maps\FrozenThrone\(4)TwistedMeadows.w3x W3XP at2v2 Maps\FrozenThrone\(6)CopperCanyon.w3x W3XP at2v2 Maps\FrozenThrone\(6)GnollWood.w3x W3XP at2v2 Maps\FrozenThrone\(6)RiverOfSouls.w3x W3XP at2v2 Maps\FrozenThrone\(6)RollingHills.w3x W3XP at2v2 Maps\FrozenThrone\(6)Typhoon.w3x W3XP at3v3 Maps\FrozenThrone\(6)BloodstoneMesa.w3x W3XP at3v3 Maps\FrozenThrone\(6)CopperCanyon.w3x W3XP at3v3 Maps\FrozenThrone\(6)GnollWood.w3x W3XP at3v3 Maps\FrozenThrone(6)HinterlandRaid.w3x W3XP at3v3 Maps\FrozenThrone\(6)Monsoon.w3x W3XP at3v3 Maps\FrozenThrone\(6)RiverOfSouls.w3x W3XP at3v3 Maps\FrozenThrone\(6)RuinsOfStratholme.w3x W3XP at3v3 Maps\FrozenThrone\(6)Typhoon.w3x W3XP at3v3 Maps\FrozenThrone\(6)UpperKingdom.w3x W3XP at3v3 Maps\FrozenThrone\(8)Mur'gulOasis.w3x W3XP at4v4 Maps\FrozenThrone\(8)Battleground.w3x W3XP at4v4 Maps\FrozenThrone\(8)Deadlands.w3x W3XP at4v4 Maps\FrozenThrone\(8)Deadlock.w3x W3XP at4v4 Maps\FrozenThrone\(8)DragonFalls.w3x W3XP at4v4 Maps\FrozenThrone\(8)Feralas.w3x W3XP at4v4 Maps\FrozenThrone\(8)GoldRush.w3x W3XP at4v4 Maps\FrozenThrone\(8)MarketSquare.w3x W3XP at4v4 Maps\FrozenThrone\(8)Mur'gulOasis.w3x W3XP at4v4 Maps\FrozenThrone\(8)TwilightRuins.w3x W3XP tffa Maps\FrozenThrone\(4)Deathrose.w3x W3XP tffa Maps\FrozenThrone\(4)TwistedMeadows.w3x W3XP tffa Maps\FrozenThrone\(6)Duststorm.w3x W3XP tffa Maps\FrozenThrone\(6)EmeraldShores.w3x W3XP tffa Maps\FrozenThrone\(6)Monsoon.w3x W3XP tffa Maps\FrozenThrone\(6)SilverpineForest.w3x W3XP tffa Maps\FrozenThrone\(8)Deadlock.w3x W3XP tffa Maps\FrozenThrone\(8)GoldRush.w3x W3XP tffa Maps\FrozenThrone\(8)HurricaneIsle.w3x W3XP tffa Maps\FrozenThrone\(8)MarketSquare.w3x W3XP tffa Maps\FrozenThrone\(8)Mur'gulOasis.w3x W3XP tffa Maps\FrozenThrone\(8)TwilightRuins.w3x W3XP 5v5 Maps\FrozenThrone\(10)RagingStream.w3x W3XP 5v5 Maps\FrozenThrone\(12)EmeraldGardens.w3x W3XP 6v6 Maps\FrozenThrone\(12)EmeraldGardens.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)BloodstoneMesa.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)CopperCanyon.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)Dragonblight.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)Everfrost.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)GnollWood.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)Highperch.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)Monsoon.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)RiceFields.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)SilverpineForest.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)SunrockCove.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)Typhoon.w3x W3XP 2v2v2 Maps\FrozenThrone\(6)UpperKingdom.w3x W3XP 3v3v3 Maps\FrozenThrone\(9)RoundVillage.w3x W3XP 3v3v3 Maps\FrozenThrone\(10)RagingStream.w3x W3XP 3v3v3 Maps\FrozenThrone\(12)EmeraldGardens.w3x W3XP 4v4v4 Maps\FrozenThrone\(12)EmeraldGardens.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)Battleground.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)Cherryville.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)Deadlock.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)DragonFalls.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)Friends.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)FullScaleAssault.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)GoldRush.w3x W3XP 2v2v2v2 Maps\(8)GolemsInTheMist.w3m W3XP 2v2v2v2 Maps\FrozenThrone\(8)HurricaneIsle.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)MarketSquare.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)Mur'gulOasis.w3x W3XP 2v2v2v2 Maps\FrozenThrone\(8)Slalom.w3x W3XP 3v3v3v3 Maps\FrozenThrone\(12)EmeraldGardens.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)BloodstoneMesa.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)CopperCanyon.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)GnollWood.w3x W3XP at2v2v2 Maps\FrozenThrone(6)HinterlandRaid.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)Monsoon.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)RiverOfSouls.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)RuinsOfStratholme.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)Typhoon.w3x W3XP at2v2v2 Maps\FrozenThrone\(6)UpperKingdom.w3x W3XP at2v2v2 Maps\FrozenThrone\(8)Mur'gulOasis.w3x ######### COMMAND & CONQUER: RED ALERT 2 ############ RAL2 1v1 eb2.map RAL2 1v1 mp02t2.map RAL2 1v1 mp06mw.map RAL2 1v1 mp06t2.map RAL2 1v1 mp08mw.map RAL2 1v1 mp08t2.map RAL2 1v1 mp11t2.map RAL2 1v1 mp14mv.map RAL2 1v1 mp14t2.map RAL2 1v1 mp21s2.map RAL2 1v1 mp24t2.map RAL2 1v1 mp29mw.map RAL2 1v1 mp29u2.map RAL2 1v1 mp31s2.map RAL2 1v1 mp09du.map RAL2 1v1 mp09t3.map RAL2 1v1 mp18s3.map ######### COMMAND & CONQUER: YURI'S REVENGE ######### YURI 1v1 xgrinder.map YURI 1v1 xeb1.map YURI 1v1 xeb1mw.map YURI 1v1 xeb2.map YURI 1v1 xeb3.map YURI 1v1 xeb4.map YURI 1v1 xeb5.map pvpgn-1.99.7.2.1+dfsg/conf/bnxpcalc.conf.in000066400000000000000000000010731462640461400201740ustar00rootroot00000000000000# This file is used to compute the XP points gained/lost by Warcraft III # anonymous matchmaking games # # # # Note: you may notice that blizzard has columns for points win/loss by # higher and lower opponets but because they must keep constant the sum # of points the columns are in "mirror" # used for all players 65 0 100 100 100 100 65 1 95 105 115 85 65 2 90 110 130 70 65 3 85 115 145 55 65 4 80 120 155 45 65 5 75 125 165 35 65 6 70 130 175 25 pvpgn-1.99.7.2.1+dfsg/conf/bnxplevel.conf.in000066400000000000000000000027731462640461400204110ustar00rootroot00000000000000# This file is used mainly for computing the level of players and # teams on W3 Anonymous Matchmaking, the format is: # # # # FIXME: Min Games is not used right now 1 0 100 0.10 0 2 100 100 0.11 0 3 200 200 0.11 0 4 400 200 0.25 1 5 600 300 0.25 1 6 900 300 0.43 1 7 1200 400 0.43 2 8 1600 400 0.67 2 9 2000 500 0.67 2 10 2500 500 1.00 3 11 3000 500 1.00 3 12 3500 500 1.00 3 13 4000 500 1.00 3 14 4500 500 1.00 4 15 5000 500 1.00 4 16 5500 500 1.00 4 17 6000 500 1.00 5 18 6500 500 1.00 5 19 7000 500 1.00 5 20 7500 500 1.00 6 21 8000 500 1.00 6 22 8500 500 1.00 6 23 9000 500 1.00 6 24 9500 500 1.00 7 25 10000 500 1.00 7 26 10500 500 1.00 7 27 11000 500 1.00 8 28 11500 500 1.00 8 29 12000 500 1.00 8 30 12500 500 1.00 9 31 13000 500 1.00 9 32 13500 500 1.00 9 33 14000 500 1.00 9 34 14500 500 1.00 10 35 15000 500 1.00 10 36 15500 500 1.00 10 37 16000 500 1.00 11 38 16500 500 1.00 11 39 17000 500 1.00 11 40 17500 500 1.00 12 41 18000 500 1.00 12 42 18500 500 1.00 12 43 19000 500 1.00 12 44 19500 500 1.00 13 45 20000 500 1.00 13 46 20500 500 1.00 13 47 21000 500 1.00 14 48 21500 500 1.00 14 49 22000 500 1.00 14 50 22500 500 1.00 15 51 23000 500 1.00 15 52 23500 500 1.00 15 53 24000 500 1.00 15 54 24500 500 1.00 16 55 25000 500 1.00 16 56 25500 500 1.00 16 57 26000 500 1.00 17 58 26500 500 1.00 17 59 27000 500 1.00 17 60 27500 500 1.00 18 61 28000 500 1.00 18 62 28500 500 1.00 18 63 29000 500 1.00 18 64 29500 500 1.00 19 65 30000 500 1.00 19 pvpgn-1.99.7.2.1+dfsg/conf/channel.conf.in000066400000000000000000000167501462640461400200220ustar00rootroot00000000000000############################################################################## # channel.list - Configuration for permanent channels # #----------------------------------------------------------------------------# # # # These are the channels to create when the server starts up. This file does # # not change the channel that the client requests when it logs in, but it # # can be used to give that channel a different "official" name. # # # # Quotes (") are required around the strings, but they are not allowed # # inside them. An unquoted NULL means there is no entry for that position. # # # # NONE for special name means to use the format "shortname-ctry num realm" # # e.g. "D2CS-Starcraft USA-1" # # NULL for country creates non-country specific channels. # # -1 for max sets no limit to the number of users allowed in the channel # # # # ---special name--- -----short name----- cltag bots- -ops- -log- ctry- realm max -mod-# "The Void" "The Void" NULL true false false NULL NULL -1 true NONE "Starcraft" STAR true false false NULL NULL -1 false NONE "Brood War" SEXP true false false NULL NULL -1 false NONE "Starcraft Shareware" SSHR true false false NULL NULL -1 false NONE "Diablo Retail" DRTL true false false NULL NULL -1 false NONE "War2BNE" W2BN true false false NULL NULL -1 false NONE "Diablo II" D2DV true false false NULL NULL -1 false NONE "Diablo II" D2XP true false false NULL NULL -1 false "Warcraft 3 Frozen Throne" "W3" W3XP true false false NULL NULL -1 false "Warcraft 3" "W3" WAR3 true false false NULL NULL -1 false "Human Castle" "W3" W3XP true false false NULL NULL -1 false "Orc Stronghold" "W3" W3XP true false false NULL NULL -1 false "Undead Necropolis" "W3" W3XP true false false NULL NULL -1 false "Night Elf Tree of Life" "W3" W3XP true false false NULL NULL -1 false "Dragon Roost" "W3" W3XP true false false NULL NULL -1 false "Human Castle" "W3" WAR3 true false false NULL NULL -1 false "Orc Stronghold" "W3" WAR3 true false false NULL NULL -1 false "Undead Necropolis" "W3" WAR3 true false false NULL NULL -1 false "Night Elf Tree of Life" "W3" WAR3 true false false NULL NULL -1 false "Dragon Roost" "W3" WAR3 true false false NULL NULL -1 false "Chat" "Chat" CHAT true false false NULL NULL -1 false "Backstage" "Backstage" NULL true false false NULL NULL 0 false "Moderated Support" "Support" NULL true false false NULL NULL -1 true ############################################################################## # Westwood Online Channels # #----------------------------------------------------------------------------# # Note: The Westwood channels must be formatted like "Lob xx x". # # Where xx=Game type number and x=channel number. First channel number is 0. # # For exemple "Lob 18 0" is the first channel for "Tiberian Sun". # # The proper game type numbers are as follows: # # - 12 = C&C Renegade channels # # - 14 = Dune 2000 channels # # - 16 = Nox channels # # - 18 = Tiberian Sun channels # # - 21 = Red Alert 1 channels # # - 31 = Emperor: Battle for Dune channels # # - 33 = Red Alert 2 channels # # - 37 = Nox Quest channels # # - 41 = Yuri's Revenge channels # # # # DO NOT USE game type nubers 38,39 and 40. This numbers are used for # # Quick match services and are hardcoded in PvPGN. # # # # In WOLv1 (Westwood Chat client) are not used "Lob xx x" formated channels. # # # # ---special name--- -----short name----- cltag bots- -ops- -log- ctry- realm max -mod-# "Command n Conqr" "Command n Conqr" WCHT true false false NULL NULL -1 false "Monopoly" "Monopoly" WCHT true false false NULL NULL -1 false "Red Alert" "Red Alert" WCHT true false false NULL NULL -1 false "Renegade-1" "Lob 12 0" RNGD true false false NULL NULL -1 false "Dune 2000-1" "Lob 14 0" DN2K true false false NULL NULL -1 false "Nox-1" "Lob 16 0" NOXX true false false NULL NULL -1 false "Nox-2" "Lob 16 1" NOXX true false false NULL NULL -1 false "Tiberian Sun-1" "Lob 18 0" TSUN true false false NULL NULL -1 false "Tiberian Sun-2" "Lob 18 1" TSUN true false false NULL NULL -1 false "Tiberian Sun-3" "Lob 18 2" TSUN true false false NULL NULL -1 false "Red Alert-1" "Lob 21 0" RALT true false false NULL NULL -1 false "Emperor-1" "Lob 31 0" EMPR true false false NULL NULL -1 false "Emperor-2" "Lob 31 1" EMPR true false false NULL NULL -1 false "Red Alert 2-1" "Lob 33 0" RAL2 true false false NULL NULL -1 false "Red Alert 2-2" "Lob 33 1" RAL2 true false false NULL NULL -1 false "Nox Quest-1" "Lob 37 0" NOXQ true false false NULL NULL -1 false "Yuri's Revenge-1" "Lob 41 0" YURI true false false NULL NULL -1 false "Yuri's Revenge-2" "Lob 41 1" YURI true false false NULL NULL -1 false "Lob 38 0" "Lob 38 0" CHAT true false false NULL NULL -1 false "Lob 39 0" "Lob 39 0" CHAT true false false NULL NULL -1 false "Lob 40 0" "Lob 40 0" CHAT true false false NULL NULL -1 false # # ############################################################################## pvpgn-1.99.7.2.1+dfsg/conf/command_groups.conf.in000066400000000000000000000141221462640461400214160ustar00rootroot00000000000000# /////////////////////////////////////////////////////////////////////// # ////////////// Command Groups Configuration File ////////////// # /////////////////////////////////////////////////////////////////////// # # Format of this file is simple. There are 8 different groups. # # Each line must start with the group number followed by as many # commands as you wish. # # Group '1' is common user commands and can be used by any user. # # Rights to use commands in Groups '2' through '8' must be given # to a user using the /commandgroups or /cg command. # # usage /cg [] # # ie. /cg list pvpgnplayer <-- list the current command groups for user # ie. /cg add pvpgnplayer 234 <-- adds groups 2, 3, & 4 to user # ie. /cg del pvpgnplayer 24 <-- deletes groups 2 & 4 from user # # If the following commands were done, pvpgnplayer would have access # to commands in groups 1 & 3 # # Commands not listed in this file will be deactivated by the server # # FILE MODE: # Add the following line to your user file to get access to all groups: # "BNET\\auth\\command_groups"="255" # # For regular users to be able to use the regular commands make sure to # use the updated 'bnetd_default_user' file # # SQL MODE: # Issue the following commands to get access to all groups: # ALTER TABLE BNET ADD COLUMN auth_command_groups VARCHAR(128) default '1'; # UPDATE BNET SET auth_command_groups='255' WHERE uid='youruserid' # # For regular users to be able to use the regular commands # issue the following command: # UPDATE BNET SET auth_command_groups='1' WHERE uid='0' # # [ <...>] # //////////////////////////////////////////////// # ///// Regular commands usable by all users ///// # //////////////////////////////////////////////// 1 /c /clan 1 /f /friends 1 /me /emote 1 /msg /whisper /w /m 1 /r /reply 1 /status /users /lusers 1 /who /whois /whereis /where /whoami 1 /beep /nobeep 1 /version /copyright /warranty /license 1 /uptime 1 /stats /astat 1 /time 1 /channel /join /j /rejoin 1 /away /dnd /ignore /unignore /squelch /unsquelch 1 /watch /unwatch /watchall /unwatchall 1 /news /games /channels /chs /admins 1 /logout /quit /exit 1 /ladderinfo 1 /timer /quota 1 /help /? 1 /mail 1 /motd 1 /tos 1 /latency /ping /p 1 /topic 1 /icon 1 /language /lang # ///////////////////////////// # ///// Operator commands ///// # ///////////////////////////// # # -- These commands require the user to be one of the following: # # -- Server Admin - admin status set to true - use --> /admin + # -- can /aop users for any channel # -- can /op & /deop users for any channel # -- can /kick /ban & /unban users for any channel # -- can /vop users for any channel # -- can /voice & /devoice users for any channel # -- Server Operator - operator status set to true - use --> /operator + # -- can /op & /deop users for any channel # -- can /kick /ban & /unban users for any channel # -- can /voice & /devoice users for any channel # -- Channel Admin - admin starus for channel set to true - use --> /aop # -- can /aop users for the channel they are admin of # -- can /op & /deop users for the channel they are admin of # -- can /kick /ban & /unban users for the channel they are admin of # -- can /vop users for the chnnel they are admin of # -- can /voice & /devoice users for the channel they are admin of # -- Channel Operator - operator status for channel set to true - use --> /op # -- can /op & /deop users for the channel they are operator of # -- can /kick /ban & /unban users for the channel they are operator of # -- can /voice & /devoice users for the channel they are operator of # # -- You must be in the channel you wish to give or take Channel Operator or # Channel Admin status too or from. ** /aop /op & /deop ** # -- P.S. You may now have more than one operator and/or admin per channel. # -- No need to make everyone Server Admins. 1 /kick /ban /unban /op /deop /aop /voice /devoice /vop /devop /tmpop /moderate # //////////////////////////////// # ///// Commands with limits ///// # //////////////////////////////// # # -- Although set to Group '1', these command have the following limitations for users. # # -- /admin-con & /admin-addr /admin-chpass are not real commands. # # -- Without 'enable_conn_all = true' in bnetd.conf, only users with access to # /admin-con may use the /con or /connections. 1 /connections /con # -- Without 'hide_addr = false' in bnetd.conf, only users with access to # /admin-addr will see IP Numbers. 1 /games /finger /gameinfo # -- Users may use /chpass on themselves, but require access to # /admin-chpass to change others. # -- Users may use /netinfo on themselves, but require access to # /admin-addr to see others info. 1 /netinfo /chpass # -- /admin-con & /admin-addr /admin-chpass are not real commands. # -- Access to these commands gives more info for the following commands: # /connections /con /games /finger /gameinfo /netinfo /chpass # as described above. 6 /admin-con /admin-addr /admin-chpass # ////////////////////////////////////////// # ///// Command Groups Access Required ///// # ////////////////////////////////////////// # # -- All the following do not require Server Admin Status. # -- They are all locked out for regular users and require access to the Group to Use. # -- Use the /cg command to assign groups to users. 2 /realmann 2 /ann /announce 2 /alert 3 /serverban /ipban 3 /ipscan 4 /kill 4 /lock /lockacct /unlock /unlockacct 4 /mute /muteacct /unmute /unmuteacct 5 /ladderactivate # -- Access to group '6' allows users to not only make others Server Admins & Server Operators # but will allow them to make themselves Server Admin & Server Operator. 6 /operator /admin /flag /tag 6 /log 7 /set 7 /commandgroups /cg 7 /clearstats 8 /shutdown /rehash /find /save # ////////////////////////////////////// # ///// Commands for Debugging ///// # ////////////////////////////////////// #8 /killsession #8 /addacct # ////////////////////////////////////// # ///// End of Command Groups File ///// # ////////////////////////////////////// pvpgn-1.99.7.2.1+dfsg/conf/d2cs.conf.in000066400000000000000000000212021462640461400172310ustar00rootroot00000000000000################################################################################# # d2cs.conf - Configuration file for the diablo2 game control server # #-------------------------------------------------------------------------------# # # # This file is an example configuration and may require modification to # # suit your needs or your site. Blank lines and lines starting with a "#" # # are ignored. Use quotes around values that contain spaces. # # # ################################################################################# ################################################################################# # Server Major Settings # #-------------------------------------------------------------------------------# # # # Your realm server name # # Here you put the value of the first column from the realm line in realm.conf # Example, if you have the line # "MyRealm" "My Super Realm" :6113 # in realm.conf then here you put realmname = "MyRealm" realmname = D2CS # This is a comma delimited list of hostnames that the server should # listen on. Generally, YOU DONT NEED TO TOUCH THIS but it might be useful # to make an internal-only server on a gateway machine for example. If the # list is not set or if it has a entry with no host component, the server will # bind to that port on all interfaces. # d2cs default listening port is 6113 # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! servaddrs = 0.0.0.0:6113 # This is a comma delimited list of game servers. Put here the IPs # (without ports) of the machines running d2gs. If you run d2gs on the same # machine as d2cs you put the IP of that machine here. # Example: gameservlist = 192.168.1.10 # (if you have a d2gs running on a machine with the IP 192.168.1.10) # # YOU MUST CHANGE THIS OR D2CS WON'T WORK PROPERLY # # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! gameservlist = , # Put here the address and port of the machine running bnetd (or pvpgn.exe). # If you run d2cs on the same machine as bnetd (or pvpgn.exe) then here you # put the IP of that machine. # # YOU MUST CHANGE THIS OR D2CS WON'T WORK PROPERLY # # bnetd default listening port is 6112 # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! bnetdaddr = :6112 # Set this to the maximum number of concurrent connections allowed on # this server. DO NOT TOUCH UNLESS STRICTLY NECESSARY! max_connections = 1000 # This sets the realm to Classic or LOD or Both # Classic = 0 # LOD = 1 # Both = 2 (default) lod_realm = 2 # This sets whether you can convert a clasic char to # an expansion char. # Allow Convert = 1 # Don't allow Convert = 0 allow_convert = 0 # here u can enter the symbols allowed in accountnames (additional to # alphanumerical symbals which are always accepted). please note that # it is absolutly mandatory that u set up the same symbols here as in # bnetd.conf or else u wont be able to play D2 with some characters # (those whose symbols are allowed in bnetd.conf but not here) # same warnings as in bnetd.conf apply here - be warned - and just # change something if you know what you are doing #default setting is "-_[]" account_allowed_symbols = "-_[]" # # ################################################################################# ################################################################################# # Message logs # #-------------------------------------------------------------------------------# # Multiple log levels can be defined by connecting them with a comma (,) # Available loglevels are: # none # trace # debug # info # warn # error # fatal #loglevels = fatal,error,warn,info loglevels = fatal,error,warn,info,debug,trace # # ################################################################################# ################################################################################# # File and Path section # # Use absolute paths in these lines to avoid problems! # #-------------------------------------------------------------------------------# # # logfile = "${LOCALSTATEDIR}/d2cs.log" charsavedir = "${LOCALSTATEDIR}/charsave" charinfodir = "${LOCALSTATEDIR}/charinfo" bak_charsavedir = "${LOCALSTATEDIR}/bak/charsave" bak_charinfodir = "${LOCALSTATEDIR}/bak/charinfo" ladderdir = "${LOCALSTATEDIR}/ladders" transfile = "${SYSCONFDIR}/address_translation.conf" d2gsconffile = "${SYSCONFDIR}/d2server.ini" #pidfile = "${LOCALSTATEDIR}/d2cs.pid" # d2s template for a new created characters newbiefile_amazon = "${LOCALSTATEDIR}/files/newbie.save" newbiefile_sorceress = "${LOCALSTATEDIR}/files/newbie.save" newbiefile_necromancer = "${LOCALSTATEDIR}/files/newbie.save" newbiefile_paladin = "${LOCALSTATEDIR}/files/newbie.save" newbiefile_barbarian = "${LOCALSTATEDIR}/files/newbie.save" newbiefile_druid = "${LOCALSTATEDIR}/files/newbie.save" newbiefile_assasin = "${LOCALSTATEDIR}/files/newbie.save" # # ################################################################################# ################################################################################# # Misc # #-------------------------------------------------------------------------------# # # # Message Of The Day motd = "No Message Of The Day Set" # Set to non-zero to allow creation of new realm character allow_newchar = 1 # Do you want d2cs to check client for multilogin for security reason? check_multilogin = 0 # Maxinum number of character per account # Max allowed value is 18 (enforced by server) maxchar = 8 # Character sorting. Options are: level, ctime, mtime, name, none. (none assumed if # not specified). #charlist_sort = "none" # Do we need ascending or descending order for charlist? #charlist_sort_order = "ASC" # Maxinum number of games will be shown in join game list # Zero = infinite maxgamelist = 20 # Set to non-zero to allow show all games with difficulty < character difficulty # Otherwise, only game with difficulty = character difficulty will be shown gamelist_showall = 0 # Maxinum time in seconds that a user can idle # Zero = infinite idletime = 3600 # Amount of time to delay shutting down server in seconds. shutdown_delay = 300 # Amount of time delay period is decremented by either a SIGTERM or SIGINT # (control-c) signal in seconds. shutdown_decr = 60 # ################################################################################# ################################################################################# # Internal System Settings # # You may just ignore them and use the default value # #-------------------------------------------------------------------------------# # # How often will the server purge all list to clean unused data (in seconds) listpurgeinterval = 300 # How often will the server check game queues (in seconds) gqcheckinterval = 60 # How often will the server retry to connect to bnetd # when connection lost (in seconds) s2s_retryinterval = 10 # How long time the s2s connection will timeout s2s_timeout = 10 # How often the server will check server queues for expired data sq_checkinterval = 300 # How long time will a server queue data expire sq_timeout = 300 # Game serer binary files checksum, use zero to skip this checking d2gs_checksum = 0 # Game server version, use zero to skip this checking d2gs_version = 0 # Game server password d2gs_password = "" # Maxinum number of second that a game will be shown on list( zero = infinite ) game_maxlifetime = 0 # Maximum level allowed in this (realm) game. # Normal games have a max level 99, while the internal field size limit is 255. # Extreme game MODs may have higher values, but be warned: # !!! Use them at your own risk !!! # game_maxlevel = 255 # A game will be automatically destroied after how long time idle max_game_idletime = 0 # Allow Limitation created game with password, player number or level limit? allow_gamelimit = 1 # Ladder refresh time ladder_refresh_interval = 3600 # server to server connection max idle time in seconds s2s_idletime = 300 # server to server connection keepalive interval s2s_keepalive_interval = 60 # all connection timeout check interval timeout_checkinterval = 60 # game server restart interval # when sending SIGUSR2 signal to your d2cs this issues a restart # of all connected d2gs after d2gs_restart_delay seconds d2gs_restart_delay = 300 # ladder start time # format: yyyy-mm-dd hh:mm:ss # be carefull: # all chars created before this date will revert to non-ladder chars ladder_start_time = "" # number of days before a char expires (default 0=unlimited) char_expire_day = 0 # ################################################################################# pvpgn-1.99.7.2.1+dfsg/conf/d2cs.conf.win32000066400000000000000000000205671462640461400176020ustar00rootroot00000000000000################################################################################# # d2cs.conf - Configuration file for the diablo2 game control server # #-------------------------------------------------------------------------------# # # # This file is an example configuration and may require modification to # # suit your needs or your site. Blank lines and lines starting with a "#" # # are ignored. Use quotes around values that contain spaces. # # # ################################################################################# ################################################################################# # Server Major Settings # #-------------------------------------------------------------------------------# # # # Your realm server name # # Here you put the value of the first column from the realm line in realm.conf # Example, if you have the line # "MyRealm" "My Super Realm" :6113 # in realm.conf then here you put realmname = "MyRealm" realmname = D2CS # This is a comma delimited list of hostnames that the server should # listen on. Generally, YOU DONT NEED TO TOUCH THIS but it might be useful # to make an internal-only server on a gateway machine for example. If the # list is not set or if it has a entry with no host component, the server will # bind to that port on all interfaces. # d2cs default listening port is 6113 # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! servaddrs = 0.0.0.0:6113 # This is a comma delimited list of game servers. Put here the IPs # (without ports) of the machines running d2gs. If you run d2gs on the same # machine as d2cs you put the IP of that machine here. # Example: gameservlist = 192.168.1.10 # (if you have a d2gs running on a machine with the IP 192.168.1.10) # # YOU MUST CHANGE THIS OR D2CS WON'T WORK PROPERLY # # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! gameservlist = , # Put here the address and port of the machine running bnetd (or pvpgn.exe). # If you run d2cs on the same machine as bnetd (or pvpgn.exe) then here you # put the IP of that machine. # # YOU MUST CHANGE THIS OR D2CS WON'T WORK PROPERLY # # bnetd default listening port is 6112 # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! bnetdaddr = :6112 # Set this to the maximum number of concurrent connections allowed on # this server. DO NOT TOUCH UNLESS STRICTLY NECESSARY! max_connections = 1000 # This sets the realm to Classic or LOD or Both # Classic = 0 # LOD = 1 # Both = 2 (default) lod_realm = 2 # This sets whether you can convert a clasic char to # an expansion char. # Allow Convert = 1 # Don't allow Convert = 0 allow_convert = 0 # here u can enter the symbols allowed in accountnames (additional to # alphanumerical symbals which are always accepted). please note that # it is absolutly mandatory that u set up the same symbols here as in # bnetd.conf or else u wont be able to play D2 with some characters # (those whose symbols are allowed in bnetd.conf but not here) # same warnings as in bnetd.conf apply here - be warned - and just # change something if you know what you are doing #default setting is "-_[]" account_allowed_symbols = "-_[]" # # ################################################################################# ################################################################################# # Message logs # #-------------------------------------------------------------------------------# # Multiple log levels can be defined by connecting them with a comma (,) # Available loglevels are: # none # trace # debug # info # warn # error # fatal #loglevels = fatal,error,warn,info loglevels = fatal,error,warn,info,debug,trace # # ################################################################################# ################################################################################# # File and Path section # # Use absolute paths in these lines to avoid problems! # #-------------------------------------------------------------------------------# # # logfile = var\d2cs.log charsavedir = var\charsave charinfodir = var\charinfo bak_charsavedir = var\bak\charsave bak_charinfodir = var\bak\charinfo ladderdir = var\ladders transfile = conf\address_translation.conf d2gsconffile = conf\d2server.ini #pidfile = var\d2cs.pid # d2s template for a new created characters newbiefile_amazon = files\newbie.save newbiefile_sorceress = files\newbie.save newbiefile_necromancer = files\newbie.save newbiefile_paladin = files\newbie.save newbiefile_barbarian = files\newbie.save newbiefile_druid = files\newbie.save newbiefile_assasin = files\newbie.save # # ################################################################################# ################################################################################# # Misc # #-------------------------------------------------------------------------------# # # # Message Of The Day motd = "No Message Of The Day Set" # Set to non-zero to allow creation of new realm character allow_newchar = 1 # Do you want d2cs to check client for multilogin for security reason? check_multilogin = 0 # Maxinum number of character per account # Max allowed value is 18 (enforced by server) maxchar = 8 # Character sorting. Options are: level, mtime, name, none. (none assumed if # not specified). #charlist_sort = "none" # Do we need ascending or descending order for charlist? #charlist_sort_order = "ASC" # Maxinum number of games will be shown in join game list # Zero = infinite maxgamelist = 20 # Set to non-zero to allow show all games with difficulty < character difficulty # Otherwise, only game with difficulty = character difficulty will be shown gamelist_showall = 0 # Maxinum time in seconds that a user can idle # Zero = infinite idletime = 3600 # Amount of time to delay shutting down server in seconds. shutdown_delay = 300 # Amount of time delay period is decremented by either a SIGTERM or SIGINT # (control-c) signal in seconds. shutdown_decr = 60 # ################################################################################# ################################################################################# # Internal System Settings # # You may just ignore them and use the default value # #-------------------------------------------------------------------------------# # # How often will the server purge all list to clean unused data (in seconds) listpurgeinterval = 300 # How often will the server check game queues (in seconds) gqcheckinterval = 60 # How often will the server retry to connect to bnetd # when connection lost (in seconds) s2s_retryinterval = 10 # How long time the s2s connection will timeout s2s_timeout = 10 # How often the server will check server queues for expired data sq_checkinterval = 300 # How long time will a server queue data expire sq_timeout = 300 # Game serer binary files checksum, use zero to skip this checking d2gs_checksum = 0 # Game server version, use zero to skip this checking d2gs_version = 0 # Game server password d2gs_password = "" # Maxinum number of second that a game will be shown on list( zero = infinite ) game_maxlifetime = 0 # Maximum level allowed in this (realm) game. # Normal games have a max level 99, while the internal field size limit is 255. # Extreme game MODs may have higher values, but be warned: # !!! Use them at your own risk !!! # game_maxlevel = 255 # A game will be automatically destroied after how long time idle max_game_idletime = 0 # Allow Limitation created game with password, player number or level limit? allow_gamelimit = 1 # Ladder refresh time ladder_refresh_interval = 3600 # server to server connection max idle time in seconds s2s_idletime = 300 # server to server connection keepalive interval s2s_keepalive_interval = 60 # all connection timeout check interval timeout_checkinterval = 60 # game server restart interval # when sending SIGUSR2 signal to your d2cs this issues a restart # of all connected d2gs after d2gs_restart_delay seconds d2gs_restart_delay = 300 # ladder start time # format: yyyy-mm-dd hh:mm:ss # be carefull: # all chars created before this date will revert to non-ladder chars ladder_start_time = "" # number of days before a char expires (default 0=unlimited) char_expire_day = 0 # ################################################################################# pvpgn-1.99.7.2.1+dfsg/conf/d2dbs.conf.in000066400000000000000000000110501462640461400173740ustar00rootroot00000000000000################################################################################# # d2dbs.conf - Configuration file for the Diablo2 Game database server # #-------------------------------------------------------------------------------# # # # This file is an example configuration and may require modification to # # suit your needs or your site. Blank lines and lines starting with a "#" # # are ignored. Use quotes around values that contain spaces. # # # ################################################################################# ################################################################################# # Server Major Settings # #-------------------------------------------------------------------------------# # This is a comma delimited list of hostnames that the server should # listen on. Generally, YOU DONT NEED TO TOUCH THIS but it might be useful # to make an internal-only server on a gateway machine for example. If the # list is not set or if it has a entry with no host component, the server will # bind to that port on all interfaces. # d2dbs default listening port is 6114 # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! servaddrs = 0.0.0.0:6114 # Put here EXACTLY the same thing you put in d2cs.conf gameservlist setting. # For more information about this setting consult the d2cs.conf comments # above it. # # YOU MUST CHANGE THIS OR D2DBS WON'T WORK PROPERLY # # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! gameservlist = , # # ################################################################################# ################################################################################# # Message logs # #-------------------------------------------------------------------------------# # Multiple log levels can be defined by connecting them with a comma (,) # Available loglevels are: # none # trace # debug # info # warn # error # fatal #loglevels = fatal,error,warn,info loglevels = fatal,error,warn,info,debug,trace # # ################################################################################# ################################################################################# # File and Path section # # Use absolute paths in these lines to avoid problems! # #-------------------------------------------------------------------------------# logfile = "${LOCALSTATEDIR}/d2dbs.log" logfile-gs = "${LOCALSTATEDIR}/d2dbs-gs.log" charsavedir = "${LOCALSTATEDIR}/charsave" charinfodir = "${LOCALSTATEDIR}/charinfo" ladderdir = "${LOCALSTATEDIR}/ladders" bak_charsavedir = "${LOCALSTATEDIR}/bak/charsave" bak_charinfodir = "${LOCALSTATEDIR}/bak/charinfo" #pidfile = "${LOCALSTATEDIR}/d2dbs.pid" # # ################################################################################# ################################################################################# # Misc # #-------------------------------------------------------------------------------# # # # Ladder save interval laddersave_interval = 3600 # Time that allow characters create after it insert into ladder # These code should be rewritted to support string formmated time ladderinit_time = 0 # create extra ladder file in XML format that can be parsed more easy # 0 = turn extra output off (default) # 1 = turn on the XML output XML_ladder_output = 0 # Amount of time to delay shutting down server in seconds. shutdown_delay = 360 # Amount of time delay period is decremented by either a SIGTERM or SIGINT # (control-c) signal in seconds. shutdown_decr = 60 # connection max idle time in seconds idletime = 300 # connection keep alive interval keepalive_interval = 60 # connection time out check interval timeout_checkinterval = 60 # experience threshold, minimum experience needed to register on the ladder ladderupdate_threshold = 0 # What chars will be added to ladder? # 0 = ladder and non-ladder chars # 1 = only ladder chars ladder_chars_only = 1 # This setting activates a workaround for the well known "Lvl 100" bug. # It should be activated for mods where the max level is greater than 99. # Activating this should work for mods with any max level # If level is greater than 255, it will be set to 255 # 0 = deactivated # 1 = activated difficulty_hack = 0 # # ################################################################################# pvpgn-1.99.7.2.1+dfsg/conf/d2dbs.conf.win32000066400000000000000000000106601462640461400177360ustar00rootroot00000000000000################################################################################# # d2dbs.conf - Configuration file for the Diablo2 Game database server # #-------------------------------------------------------------------------------# # # # This file is an example configuration and may require modification to # # suit your needs or your site. Blank lines and lines starting with a "#" # # are ignored. Use quotes around values that contain spaces. # # # ################################################################################# ################################################################################# # Server Major Settings # #-------------------------------------------------------------------------------# # This is a comma delimited list of hostnames that the server should # listen on. Generally, YOU DONT NEED TO TOUCH THIS but it might be useful # to make an internal-only server on a gateway machine for example. If the # list is not set or if it has a entry with no host component, the server will # bind to that port on all interfaces. # d2dbs default listening port is 6114 # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! servaddrs = 0.0.0.0:6114 # Put here EXACTLY the same thing you put in d2cs.conf gameservlist setting. # For more information about this setting consult the d2cs.conf comments # above it. # # YOU MUST CHANGE THIS OR D2DBS WON'T WORK PROPERLY # # WARNING!! DO NOT USE "127.0.0.1" or "localhost" !!! gameservlist = , # # ################################################################################# ################################################################################# # Message logs # #-------------------------------------------------------------------------------# # Multiple log levels can be defined by connecting them with a comma (,) # Available loglevels are: # none # trace # debug # info # warn # error # fatal #loglevels = fatal,error,warn,info loglevels = fatal,error,warn,info,debug,trace # # ################################################################################# ################################################################################# # File and Path section # # Use absolute paths in these lines to avoid problems! # #-------------------------------------------------------------------------------# logfile = var\d2dbs.log logfile-gs = var\d2dbs-gs.log charsavedir = var\charsave charinfodir = var\charinfo ladderdir = var\ladders bak_charsavedir = var\bak\charsave bak_charinfodir = var\bak\charinfo #pidfile = var\d2d2s.pid # # ################################################################################# ################################################################################# # Misc # #-------------------------------------------------------------------------------# # # # Ladder save interval laddersave_interval = 3600 # Time that allow characters create after it insert into ladder # These code should be rewritted to support string formmated time ladderinit_time = 0 # create extra ladder file in XML format that can be parsed more easy # 0 = turn extra output off (default) # 1 = turn on the XML output XML_ladder_output = 0 # Amount of time to delay shutting down server in seconds. shutdown_delay = 360 # Amount of time delay period is decremented by either a SIGTERM or SIGINT # (control-c) signal in seconds. shutdown_decr = 60 # connection max idle time in seconds idletime = 300 # connection keep alive interval keepalive_interval = 60 # connection time out check interval timeout_checkinterval = 60 # experience threshold, minimum experience needed to register on the ladder ladderupdate_threshold = 0 # What chars will be added to ladder? # 0 = ladder and non-ladder chars # 1 = only ladder chars ladder_chars_only = 1 # This setting activates a workaround for the well known "Lvl 100" bug. # It should be activated for mods where the max level is greater than 99. # Activating this should work for mods with any max level # If level is greater than 255, it will be set to 255 # 0 = deactivated # 1 = activated difficulty_hack = 0 # # ################################################################################# pvpgn-1.99.7.2.1+dfsg/conf/i18n/000077500000000000000000000000001462640461400157045ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/CMakeLists.txt000066400000000000000000000010131462640461400204370ustar00rootroot00000000000000# copy all files from directory file(GLOB DEPLOY_FILES_AND_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/*") foreach(ITEM ${DEPLOY_FILES_AND_DIRS}) IF( IS_DIRECTORY "${ITEM}" ) LIST( APPEND DIRS_TO_DEPLOY "${ITEM}" ) ELSE() IF(NOT ${ITEM} MATCHES "CMakeLists.txt") LIST( APPEND FILES_TO_DEPLOY "${ITEM}" ) ENDIF(NOT ${ITEM} MATCHES "CMakeLists.txt") ENDIF() endforeach() INSTALL( FILES ${FILES_TO_DEPLOY} DESTINATION ${SYSCONFDIR}/i18n ) INSTALL( DIRECTORY ${DIRS_TO_DEPLOY} DESTINATION ${SYSCONFDIR}/i18n ) pvpgn-1.99.7.2.1+dfsg/conf/i18n/bgBG/000077500000000000000000000000001462640461400165055ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/bgBG/bnmotd.txt000066400000000000000000000011501462640461400205260ustar00rootroot00000000000000%EЗдравей %l, добре дошъл в %s! %IВерÑ�иÑ�та на Ñ�ървъра е %v на %h. %IТози Ñ�ървър Ñ�е хоÑ�тва от %H %I %IИма %a потребителÑ�ки акаунти на този Ñ�ървър. %IÐ’ момента има %U потребители в %G игри на %N %Iи общо %u потребители играещи общо %g игри и разговарÑ�щи в общо %c канала. %IИмате проблем Ñ� команда? Въведете /help за да %Iнаучите повече за неÑ�. %IType /language to switch translation. %I %I%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/bgBG/common.xml000066400000000000000000000004001462640461400205110ustar00rootroot00000000000000 Bulgarian BGR pvpgn-1.99.7.2.1+dfsg/conf/i18n/bnhelp.conf000066400000000000000000000447371462640461400200420ustar00rootroot00000000000000############################################################################## # bnhelp - This Help File is used for describing the commands to the user # #----------------------------------------------------------------------------# # Entries are separated by a percent sign (%) at the beginning of the line. # # The commands that correspond with the entry should follow the % on the # # same line. # # # # You can enter comments by starting a line with #. Comments extend to the # # end of the line. # # Tabs replaces with 3 spaces. # # Empty lines are ignored. # # # ############################################################################## %whois whereis where -------------------------------------------------------- /whois (aliases: /where /whereis) Displays where a is on the server. Example: /whois nomad %msg whisper m w -------------------------------------------------------- /whisper (aliases: /w /m /msg) Sends a private to Example: /whisper nomad How are you doing? %ignore squelch -------------------------------------------------------- /squelch (alias: ignore, see also: /unsquelch) Blocks future messages sent from . Example: /squelch nomad %unignore unsquelch -------------------------------------------------------- /unsquelch (alias: /unignore) Allows a previously squelched to talk to you normally. Example: /unsquelch nomad %away -------------------------------------------------------- /away [message] Displays [message] to users who whisper to you. To disable, use the /away command again with no [message]. Example: /away Eating dinner %dnd -------------------------------------------------------- /dnd [message] Prevents all whispers from displaying to your screen. To disable, use the command again with no [message]. Example: /dnd I'm playing a game and don't want to hear whispers. %who -------------------------------------------------------- /who Displays a list of users in Example: /who Moderated Support %stats astat -------------------------------------------------------- /stats [player] [client] (alias: /astat) Displays a [player]'s Win/Loss record. Example: /stats nomad %status users -------------------------------------------------------- /users [gametag] (alias: /status) Displays the current number of users connected to the server. Example: /users W3XP %time -------------------------------------------------------- /time Displays the current server and your local time. %channel join j -------------------------------------------------------- /channel (alias: /join /j) Moves you to Example: /channel Moderated Support %rejoin -------------------------------------------------------- /rejoin (alias: /resign) Removes your status as the channel Operator. %me emote -------------------------------------------------------- /me (alias: /emote) Displays your name and in a different color. Example: /emote wants to play a game. %kick -------------------------------------------------------- /kick Kicks from the channel. Example: /kick nomad %ban -------------------------------------------------------- /ban (see also: /unban) Bans from the channel, and prevents him from returning. Example: /ban nomad %unban -------------------------------------------------------- /unban Allows a banned to return to the channel. Example: /unban nomad %serverban -------------------------------------------------------- /serverban (see also: /unban) Bans by IP and lock his account Example: /serverban nomad %ipscan -------------------------------------------------------- /ipscan Finds all currently logged in users with the given or . Example: /ipscan 127.0.0.1 Example: /ipscan nomad %whoami -------------------------------------------------------- /whoami Displays where you are on the server. %uptime -------------------------------------------------------- /uptime Displays how long the server has been running. %version -------------------------------------------------------- /version Displays the version of the server %lusers -------------------------------------------------------- /lusers Displays a list of players who have been banned from the channel %games -------------------------------------------------------- /games [gametag] [difficulty] Displays a list of online games. Set [difficulty] for Diablo games only (norm|night|hell) -------------------------------------------------------- /games all Displays a list of all games. /games l[obby] Displays a list of games in lobby. %channels chs -------------------------------------------------------- /channels [gametag] (alias: /chs) Displays a list of channels. -------------------------------------------------------- /channels all Displays a list of all channels. %connections con -------------------------------------------------------- /connections (alis: /con) Displays a list of connections. %finger -------------------------------------------------------- /finger Displays detailed information about Example: /finger nomad %admins -------------------------------------------------------- /admins Displays a list of all currently logged in administrators. %reply r -------------------------------------------------------- /reply (alias: /r) Replies to the last player who whispered you with . Example: /r Hi, mate! %announce ann -------------------------------------------------------- /announce (alias: /ann) Announces to everyone. Example: /ann Hello everyone! %realmann -------------------------------------------------------- /realmann Announces to everyone in the current Diablo 2 realm. Example: /realmann Hello everyone! %alert -------------------------------------------------------- /alert Show MessageBox with to everyone. Use \n as a new line symbol. Example: /alert Hello\neveryone! %news -------------------------------------------------------- /news Displays the news. %logout quit exit -------------------------------------------------------- /logout (alias: /quit /exit) Disconnects you from the server. %kill -------------------------------------------------------- /kill {|#} [min] Disconnects from the server and bans the player's IP address for [min] minutes. Example: /kill nomad 5 %killsession -------------------------------------------------------- /killsession [min] Disconnects the session from the server and bans the session's IP address for [min] minutes. Example: /killsession 1 5 %watch -------------------------------------------------------- /watch Enables notifications for . Example: /watch nomad %unwatch -------------------------------------------------------- /unwatch Disables notifications for . Example: /unwatch nomad %watchall -------------------------------------------------------- /watchall [gametag] Enables notifications for everyone. Example: /watchall SEXP %unwatchall -------------------------------------------------------- /unwatchall [gametag] Disables notifications for everyone. Example: /unwatchall SEXP %gameinfo -------------------------------------------------------- /gameinfo Displays information about . Example: /gameinfo FS 1v1!!! %ladderactivate -------------------------------------------------------- /ladderactivate Copies current ladder statistics to active ladder statistics. %ladderinfo -------------------------------------------------------- /ladderinfo [gametag] Displays ladder information for of [gametag] Example: /ladderinfo 1000 SEXP %timer -------------------------------------------------------- /timer [message] Displays [message] after seconds. Example: /timer 60 one minute has passed %netinfo -------------------------------------------------------- /netinfo [player] Displays [player]'s network information. Example: /netinfo nomad %addacct -------------------------------------------------------- /addacct Creates a new account named with password . Example: /addacct nomad password %chpass -------------------------------------------------------- /chpass [] - change a player's password Changes []'s password to . If [] is empty then change password for your account. Example: /chpass nomad password %quota -------------------------------------------------------- /quota Displays your message quota. %shutdown -------------------------------------------------------- /shutdown [duration] Sets the shutdown sequence at [duration] seconds or cancels it if set to zero. Example: /shutdown 300 %lock lockacct -------------------------------------------------------- /lock [hours] [reason] (alias: /lockacct) Locks 's account to prevent him/her from logging in with it. Set [hours] = 0 to ban permanently. Example: /lock nomad 0 bye noob! %unlock unlockacct -------------------------------------------------------- /unlock (alias: /unlockacct) Unlocks 's account to allow him/her to log in with it. Example: /unlock nomad %mute muteacct -------------------------------------------------------- /mute [hours] [reason] (alias: /muteacct) Mutes 's account to prevent him/her from talking on channels. Set [hours] = 0 to mute permanently. Example: /mute nomad 6 stop spam! %unmute unmuteacct -------------------------------------------------------- /unmute (alias: /unmuteacct) Unmutes 's account to allow him/her to talk on channels. Example: /unmute nomad %friends f -------------------------------------------------------- /friends [options] (alias: /f) -------------------------------------------------------- /friends add Adds to your friends list. /friends del Removes from your friends list. /friends promote Promotes one line up your friends list. /friends demote Demotes one line down your friends list /friends list Displays your friends list. /friends online Displays your online friends list. /friends msg Whisper to all of your online friends. %mail -------------------------------------------------------- /mail [options] -------------------------------------------------------- /mail s[end] Sends mail to with . /mail r[ead] [index] Reads mail [index] /mail del[ete] {all|} Deletes mail or [all] mail. %flag -------------------------------------------------------- /flag A debug tool for icon flags. %tag -------------------------------------------------------- /tag A debug tool for client tags. %help -------------------------------------------------------- /help [command] Displays help about [command] Example: /help whisper %ipban -------------------------------------------------------- /ipban [option] [time] -------------------------------------------------------- /ipban l[ist] Displays a list of banned IP addresses /ipban c[heck] Checks if IP address is banned or not. /ipban d[el] Deletes IP address or /ipban a[dd] [time] Bans IP address for [time] minutes. [time] = 0 - permanent ban %set -------------------------------------------------------- /set [value] Sets or returns the value of for account . Set [value] = null to erase value. Example: /set nomad BNET\auth\botlogin true Example: /set nomad Record\SEXP\0_wins 999 %motd -------------------------------------------------------- /motd Displays the MOTD. %tos -------------------------------------------------------- /tos /warranty /license Displays the Terms of Service. %admin -------------------------------------------------------- /admin [+|-] Promotes/demotes to/from server administrator. Example: /admin +nomad %rehash -------------------------------------------------------- /rehash Forces the server to reload specified config. Set "all" to reload all configs. = all | i18n | channels | realms | autoupdate | news | versioncheck | ipbans | helpfile | banners | tracker | commandgroups | aliasfile | transfile | tournament | icons | anongame | topiclist | lua %clan -------------------------------------------------------- /clan [option] -------------------------------------------------------- /clan create Create a new clan (max length = 4; spaces are allowed in ) Commands for clan members: /clan m[sg] (alias [w]hisper) Whispers a message to all your fellow clan members /clan inv[ite] Invite to your clan. /clan inv[ite] get Show clanname which you have been invited /clan inv[ite] accept Accept invitation to clan /clan inv[ite] decline Decline invitation to clan Commands for clan chieftain: /clan motd Update the clan's Message of the Day to . /clan pub[lic] (alias: pub) Opens the clan channel up to the public so that anyone may enter. /clan priv[ate] (alias: priv) Closes the clan channel such that only members of the clan may enter. /clan dis[band] Disband your clan. %ping p latency -------------------------------------------------------- /ping (alias: /p /latency) Displays your ping to the server. %command_groups cg -------------------------------------------------------- /command_groups [groups] (alias: /cg) -------------------------------------------------------- /cg list Displays 's command groups. /cg add Adds command group(s) to . /cg del Deletes command group(s) from . %operator -------------------------------------------------------- /operator [+|-] (alias: /op) Promotes/demotes to/from server operator. Example: /operator +nomad %aop -------------------------------------------------------- /aop Promotes to channel administrator. Example: /aop nomad %op -------------------------------------------------------- /op Promotes to channel operator. Example: /op nomad %deop -------------------------------------------------------- /deop Demotes from channel administrator or operator. Example: /deop nomad %tmpop -------------------------------------------------------- /tmpop Promotes to temporary channel operator. Example: /tmpop %vop -------------------------------------------------------- /vop Adds to the VOP list. Example: /vop nomad %voice -------------------------------------------------------- /voice Temporarily gives voice privileges to . Example: /voice nomad %devoice -------------------------------------------------------- /devoice Removes from the VOP list and removes temporary voice privileges. Example: /devoice nomad %topic -------------------------------------------------------- /topic [message] Sets or displays current channel's topic. Example: /topic Supported is given in this channel...\n(some text on a new line) %moderate -------------------------------------------------------- /moderate Toggles the current channel's moderated status. %clearstats -------------------------------------------------------- /clearstats Clears 's statistics, where can be any valid client or ALL for all clients Supported clients: DRTL|DSHR|W2BN|STAR|SEXP|SSHR|WAR3|W3XP Example: /clearstats nomad SEXP %find -------------------------------------------------------- /find Finds users with in their username. MUST be lowercase! %save -------------------------------------------------------- /save Forces the server to save account and clan changes to the database. %icon -------------------------------------------------------- /icon [name] Set custom icon for normal user. Use /icon without [name] to display list of available icons in your stash. -------------------------------------------------------- Syntax for operator/admin: /icon a[dd] Add icon into user stash /icon d[el] Remove icon from user stash /icon s[et] Set custom icon to user without adding it in user stash /icon l[ist] Display icons in user's stash /icon l[ist] Display availaible icons in server stash that can be assigned to users %language lang -------------------------------------------------------- /lang [code] Set your language to get another translation: %log -------------------------------------------------------- /log [args] Read log with commands (from the end) -------------------------------------------------------- /log r[ead] [startline] Show last lines in user log from [startline] /log f[ind] [startline] Find in user log from [startline] Example: /log read Joe Example: /log find Joe shutdown %quiz -------------------------------------------------------- /quiz [option] Trivia Quiz Game -------------------------------------------------------- /quiz start Start game with given dictionary name in current channel /quiz stop Finish game by force /quiz stats [username] Display record statistics for user /quiz stats Display Top records %host -------------------------------------------------------- /host Create a ladder DotA map using Host Bot Available types: 5x5/3x3 Available modes: ap/cm/rd/sd/ar/tt/aptb/rdtb/artb/sdtb/tttb %chost -------------------------------------------------------- /chost Create a custom map from the list using Host Bot Available maps: pvpgn-1.99.7.2.1+dfsg/conf/i18n/bnmotd.txt000066400000000000000000000005511462640461400177310ustar00rootroot00000000000000%EHello %l, welcome to %s! %IRunning version %v on %h. %IThis server is hosted by %H %I %IThere are %a user accounts on this server. %IThere are currently %U users in %G games of %N, %Iand %u users playing %g games and chatting in %c channels. %IHaving trouble with a command? Type /help to %Ilearn more about it. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/chathelp-war3.txt000066400000000000000000000005371462640461400211140ustar00rootroot00000000000000PvPGN Bnet Emulation Chat Help Commands New Commands: use /f help for a friend lists help use /users to view users online use /w or /msg to whisper people use /join to join a channel For Chan Ops: use /ban bans a user from channel use /kick kicks a user from channel For a more detailed help type: /helppvpgn-1.99.7.2.1+dfsg/conf/i18n/common.xml000066400000000000000000002553641462640461400177350ustar00rootroot00000000000000 English by {} for {} permanently with a reason "{}" No such alias. Use // to show the list. Alias list: you are now tempOP for this channel You can't talk on the channel. Your account has been muted This channel is moderated No one hears you. Account name contains some invalid symbol! Maximum password length allowed is {} Trying to add account "{}" with password "{}" Hash is: {} Failed to create account! Account {} created. You must supply a username. There's no account with username {}. {} is already a Server Admin {} has been promoted to a Server Admin {} has promoted you to a Server Admin {} is not a Server Admin. {} has been demoted from a Server Admin {} has demoted you from a Server Admin Currently logged on Administrators: Your game client doesn't support MessageBox. \n\n***************************\nBy {} Information from {} for {} Announcement from {}: {} Could not broadcast message. This command can only be used inside a channel. You must be at least a Channel Admin to use this command. There's no account with username {}. {} is already a Channel Admin {} has been promoted to a Channel Admin {} has promoted you to a Channel Admin for channel "{}" You are now marked as being away. You are no longer marked as away. This command can only be used inside a channel. You have to be at least a Channel Operator to use this command. That account doesn't currently exist. You cannot ban administrators. Audible notification on. Channel Arranged Teams is a RESTRICTED Channel! Max channel name length exceeded (max {} symbols) Command disabled while inside a game. Currently accessible channels: All current channels: Current channels of type {} -----------name----------- users ----admin/operator---- Only admins may change passwords for other accounts. Account does not exist. Maximum password length allowed is {} Trying to change password for account "{}" to "{}" Unable to set password. Message was sent to all currently available clan members. All fellow members of your clan are currently offline. Clan channel is opened up! Clan channel has already been opened up! Clan channel is closed! Clan channel has already been closed! Clan message of day is updated! User {} was invited to your clan! You are invited to {} by {}! User {} is not online or is already member of clan! This is one-way action! If you really want to disband your clan, type /clan disband yes Your clan was disbanded. You have been invited to {} You are now a clan member of {} Clan {} was be created You are no longer ivited to {} You are already in clan "{}" Clan with your specified &lt;clantag&gt; already exist! Please choice another one. Clan {} is created! Clan {} is pre-created, please invite at least {} players to your clan by using /clan invite &lt;username&gt; command. Invalid user. Invalid user. {}'s command group(s): {} Got bad group: {} Groups {} has been added to {} Groups {} has been deleted from {} Got unknown command: {} This command is only enabled for admins. Current connections: -class -tag -----name------ -lat(ms)- ----channel---- --game-- -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game-- -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game-- ---------addr-------- Unknown option. (none) none This command can only be used inside a channel. You must be at least a Channel Operator or tempOP to use this command. There's no account with username {}. You must be at least a Channel Admin to demote another Channel Admin {} has been demoted from a Channel Admin. {} has demoted you from a Channel Admin of channel "{}" {} has been demoted from a Channel Operator {} has demoted you from a Channel Operator of channel "{}" {} has been demoted from a tempOP of this channel {} has demoted you from a tmpOP of channel "{}" {} is no Channel Admin or Channel Operator or tempOP, so you can't demote him. {} has demoted you from a tempOP of channel "{}" {} is no tempOP in this channel, so you can't demote him This command can only be used inside a channel. You must be at least a Channel Operator to use this command. There's no account with username {}. {} has been removed from VOP list. {} has removed you from VOP list of channel "{}" You must be at least Channel Admin to remove {} from the VOP list Voice has been taken from {} in this channel {} has taken your Voice in channel "{}" {} has no Voice in this channel, so it can't be taken away Do Not Disturb mode engaged. Not available Do Not Disturb mode canceled. -- name -- similar to {} Invalid user. Login: {} {} Sex: {} Created: {} Clan: {} Rank: Chieftain Rank: Shaman Rank: Grunt Rank: Peon Location: {} Age: {} Client: {} Ver: {} Country: {} unknown Last login {} from On since {} from Operator: {}, Admin: {}, Locked: {}, Muted: {} Yes Email: {} Last login Owner: {} Idle {} Flags set to {}. That user does not exist. Server error. You can't add yourself to your friends list. You can only have a maximum of {} friends. {} is already on your friends list! Added {} to your friends list. {} added you to his/her friends list. All of your friends are offline. {} was not found on your friends list. Removed {} from your friends list. Promoted {} in your friends list. Demoted {} in your friends list. Your {} - Friends List Your {} - Online Friends List , offline using {} , in game "{}" , in game AT Preparation , in channel "{}", , is in AT Preparation , is in a game , is in a chat channel End of Friends List You are not in a game. That game does not exist. Name: {} ID: {} ({}) none unknown Owner: {} Address: {} Address: {} (trans {}) Client: {} (version {}, startver {}) Created: {} Started: {} Status: {} Type: {} Speed: {} Difficulty: {} Option: {} Map: {} Map Size: {}x{} Map Tileset: {} Map Type: {} Players: {} current, {} total, {} max Description: {} public private Currently accessible games: All current games: Games in lobby: Current games of type {} Current games of type {} {} ------name------ p -status- --------type--------- count ctag --------addr-------- Warning: That user is not online, using last known address. Sorry, no IP address could be retrieved. Scanning online users for IP {}... There are no online users with that IP address This command can only be used inside a channel. You have to be at least a Channel Operator or tempOP to use this command. That user is not logged in. That user is not in this channel. That connection doesn't exist. That user is not logged in? An admin has closed your connection and banned your IP address. Connection closed by admin. Operation successful. That is not a valid session. That session does not exist. Connection closed by admin and banned your IP's. Connection closed by admin. Copied current scores to active scores on all ladders. This game does not support win/loss records. You must supply a rank and a valid program ID. Example: /ladderinfo 1 STAR Unable to determine client game. Invalid user. Account {} is now locked Your account has just been locked This command can only be used inside a channel. Banned users: You are not in a channel. This command can only be used inside a channel. You must be at least a Channel Operator to use this command. Unable to change channel flags. Channel is now unmoderated. Channel is now moderated. Unable to open motd. No motd. Invalid user. Account {} is now muted Your account has just been muted That user is not logged on. Address information for other users is only available to admins. Server TCP: {} (bind {}) Client TCP: {} Client UDP: {} Client UDP: {} (trans {}) Game UDP: {} Game UDP: {} (trans {}) Game UDP: none Audible notification off. This command can only be used inside a channel. You must be at least a Channel Operator or tempOP to use this command. There's no account with username {}. {} is already a Channel Operator {} has been promoted to a Channel Operator {} has promoted you to a Channel Operator in channel "{}" {} must be on the same channel to tempOP him {} already is operator or admin, no need to tempOP him {} has been promoted to a tempOP {} has promoted you to a tempOP in this channel You must supply a username. There's no account with username {}. {} is already a Server Operator {} has been promoted to a Server Operator {} has promoted you to a Server Operator {} is no Server Operator, so you can't demote him {} has been demoted from a Server Operator {} latency: {} Your latency {} {} latency () Invalid user. Connection closed. Your quota allows you to write {} line(s) per {} second(s). Long lines will be wrapped every {} characters. You are not allowed to send lines with more than {} characters. You must join a realm first Announcement from {}@{}: {} Invalid mode. Rehash of "{}" is complete! You are not in a channel. No one messaged you, use /m instead Pending changes has been saved into the database. That user is not logged on. Banning {} who is using IP address {} User's account is also LOCKED! Only an admin can unlock it! /ipban a {} You have been banned by Admin: {} Your account is also LOCKED! Only an admin can UNLOCK it! Access denied due to security reasons. Invalid user. Current value of {} is "{}" Value currently not set Unable to set key for Key set successfully for Invalid delay. You've initialized the shutdown sequence. You've canceled the shutdown sequence. No such user. You can't squelch yourself. Could not squelch user. {} has been squelched. Invalid user. Unable to determine client game. This game does not support win/loss records. You must supply a user name and a valid program ID. Example: /stats joe STAR {}'s record: level: {} class: {} stats: {} str {} mag {} dex {} vit {} gld Diablo kills: {} Normal games: {}-{}-{} Ladder games: {}-{}-{} (rating {}) Ladder games: 0-0-0 IronMan games: {}-{}-{} (rating {}) IronMan games: 0-0-0 {}'s Ladder Record's: Users Solo Level: {}, Experience: {} SOLO Ladder Record: {}-{}-0 SOLO Rank: {} Users Team Level: {}, Experience: {} TEAM Ladder Record: {}-{}-0 TEAM Rank: {} Users FFA Level: {}, Experience: {} FFA Ladder Record: {}-{}-0 FFA Rank: {} Users AT Team No. {} Users AT TEAM Level: {}, Experience: {} AT TEAM Ladder Record: {}-{}-0 AT TEAM Rank: {} There are currently {} user(s) in {} games of {} There are currently {} users online, in {} games, and in {} channels. Client tag set to {}. Invalid clienttag {} specified Server Time: {} Your local time: {} Invalid duration. Your timer has expired. Could not set timer. Timer set for {} second(s) This command can only be used inside a channel. You must be at least a Channel Operator or tmpOP to use this command. There's no account with username {}. {} has already tmpOP in this channel {} must be on the same channel to tempOP him {} already is operator or admin, no need to tempOP him {} has been promoted to tmpOP in this channel {} has promoted you to a tempOP in this channel This command can only be used inside a channel. Max topic length exceeded (max {} symbols) You must be at least a Channel Operator of {} to set the topic {} topic: no topic Unable to send TOS (Terms of Service). This command can only be used inside a channel. You are not a channel operator. That user is not banned. {} is no longer banned from this channel. Invalid user. Your account has just been unlocked by {} That user's account is now unlocked. Invalid user. Your account has just been unmuted by {} That user's account is now unmuted. No such user. User was not being ignored. No longer ignoring. That user does not exist. Removal from watch list failed. User {} removed from your watch list. Removal from watch list failed. All {} users removed from your watch list. All users removed from your watch list. Uptime: {} This command can only be used inside a channel. You must be at least a Channel Operator to use this command. There's no account with username {}. {} is already on VOP list, no need to Voice him {} must be on the same channel to voice him {} already has Voice in this channel {} is already an operator or admin. {} has been granted Voice in this channel {} has granted you Voice in this channel This command can only be used inside a channel. You must be at least a Channel Admin to use this command. There's no account with username {}. {} is already on VOP list {} has been added to the VOP list {} has added you to the VOP list of channel "{}" That user does not exist. Add to watch list failed. User {} added to your watch list. Add to watch list failed. All {} users added to your watch list. All users added to your watch list. That channel does not exist. (If you are trying to search for a user, use the /whois command.) You are banned from that channel. Users in channel {}: Unable to obtain your account name. (invalid date) Reset {}'s {} stats Reset {}'s {} stats Reset {}'s {} stats Created: {} Clan: {} Rank: Chieftain Rank: Shaman Rank: Grunt Rank: Peon Client: {} Ver: {} Country: {} unknown Last login {} from On since {} from Yes No Operator: {}, Admin: {}, Locked: {}, Muted: {} Email: {} Last login Owner: {} Idle {} You cannot kick administrators. You cannot kick operators. {} has been kicked by {} ({}). {} has been kicked by {}. You cannot ban operators. Unable to ban {}. {} has been banned by {} ({}). {} has been banned by {}. Password for account {} updated. Hash is: {} Your account has been muted, you can't whisper to other users. That user is not logged on. {} is unavailable ({}) {} is away ({}) Unknown user. User was last seen on: {} User is offline You are is {} {} using {} and {} currently in {} game "{}". {} {} using {} and {} currently in channel "{}". {} {} using {}. {} {} refusing messages ({}) {} away ({}) You are sending commands to {} too quickly and risk being disconnected for flooding. Please slow down. This command has been deactivated This command is reserved for admins. Unknown command. Your line length quota has been exceeded! Your message quota has been exceeded! This is a private clan channel, unable to join! You are banned from that channel. That channel is for Admins/Operators only. The channel is currently full. This channel does not have chat privileges. This channel is moderated. Created ladder game Joined ladder game This account has been locked This account has been locked No help available for that command Oops ! There is a problem with the help file. Please contact the administrator of the server. Chat commands : Bad language code. Set your language to {} This command can only be used inside a channel. This command can only be used from the game. Bad icon. Set default icon. Set new icon is succeed. You have {} icons in stash: Available icons in server stash: Invalid user. Set default icon for {} That icon doesn't exist in server stash. Set new icon is succeed for {} {} has {} icons in stash : User already has that icon in stash. Add new icon to {}'s stash. That icon doesn't exist in user stash. Delete icon from {}'s stash. IP not banned. Error occured. IP banned by rule #{}. Illegal IP entry. No matching entry. Wrong entry number. Entry deleted. Banned IPs: (perm) none {} banned permamently by {}. {} banned permamently. {} banned for {} by {}. {} banned for {}. Bad IP. Banning only for: {} minutes. This server has NO mail support. Please specify which message to delete. Use the following syntax: /mail delete {&lt;index&gt;|all} . Successfully deleted messages. Invalid index. Please use /mail delete {&lt;index&gt;|all} where &lt;index&gt; is a number. Succesfully deleted message. You have no mail. There was an error completing your request. ID Sender Date Use /mail read &lt;ID&gt; to read the content of any message Invalid index. Please use /mail read &lt;index&gt; where &lt;index&gt; is a number. Receiver UNKNOWN! Receiver has reached his mail quota. Your message will NOT be sent. Your mail has been sent successfully. There was an error completing your request! You must be at least a Channel Operator to use this command. This command can only be used inside a channel. Quiz has already ran in channel "{}". Use /quiz stop to force finish. Available Quiz dictionaries: Quiz is not running. {}'s Quiz record: {} has never played Quiz. Top {} Quiz records: points This command is reserved for admins. User "{}" is offline pvpgn-1.99.7.2.1+dfsg/conf/i18n/csCZ/000077500000000000000000000000001462640461400165465ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/csCZ/bnmotd.txt000066400000000000000000000006041462640461400205720ustar00rootroot00000000000000%EAhoj %l, %IVitej na serveru %s, verze %v. %I %IMomentalne je zde registrovanych %a uzivatelu, z toho je %u online, hrajicich %g hry. %I %I%N si muzes zahrat s %U hraci, v %G prave probihajicich hrach nebo si muzes pokecat v %c chatech. %I %INevis si rady ? Napis /help pro napovedu. %I %ITvoje IP adresa: %r, tvoje ID %i a tvuj klient %t. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/csCZ/common.xml000066400000000000000000000004021462640461400205540ustar00rootroot00000000000000 Czech CZE pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/000077500000000000000000000000001462640461400165055ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/bnhelp.conf000066400000000000000000000455631462640461400206410ustar00rootroot00000000000000############################################################################## # bnhelp - This Help File is used for describing the commands to the user # #----------------------------------------------------------------------------# # Entries are separated by a percent sign (%) at the beginning of the line. # # The commands that correspond with the entry should follow the % on the # # same line. # # # # You can enter comments by starting a line with #. Comments extend to the # # end of the line. # # Tabs replaces with 3 spaces. # # Empty lines are ignored. # # # ############################################################################## %whois whereis where -------------------------------------------------------- /whois (oder auch: /where /whereis) Zeigt an wo ein sich gerade auf dem Server befindet. Beispiel: /whois nomad %msg whisper m w -------------------------------------------------------- /whisper (oder auch: /w /m /msg) Sendet eine private an Beispiel: /whisper nomad Wie geht es dir? %ignore squelch -------------------------------------------------------- /squelch (oder auch: ignore, siehe aauch: /unsquelch) Blockiert weitere Nachrichten von Beispiel: /squelch nomad %unignore unsquelch -------------------------------------------------------- /unsquelch (oder auch: /unignore) Erlaubt einem vorher geblocktem , normal mit dir zu reden. Beispiel: /unsquelch nomad %away -------------------------------------------------------- /away [Nachricht] Zeigt den Benutzern, die dich anflüstern eine [Nachricht] an. Nutze den /away Befehl nochmals ohne [Nachricht], um diese Funktion abzuschalten. Beispiel: /away beim Abendessen %dnd -------------------------------------------------------- /dnd [Nachricht] Bei dir wird kein Chat mehr auf dem Bildschirm gezeigt. Benutze den Befehl nochmals ohne [Nachricht], um ihn aufzuheben. Beispiel: /dnd Ich bin im Spiel und möchte keinen Chat bekommen. %who -------------------------------------------------------- /who Zeigt eine Liste der Nutzer in Beispiel: /who Moderated Support %stats astat -------------------------------------------------------- /stats [Spieler] [client] (oder auch: /astat) Zeigt die Gewinn/Verlust Statisitk eines [Spielers]. Example: /stats nomad %status users -------------------------------------------------------- /users [Spieletag] (oder auch: /status) Zeigt die Anzahl der User, die mit dem Server verbunden sind. Beispiel: /users W3XP %time -------------------------------------------------------- /time Zeigt den aktuellen Server und deine lokale Zeit an. %channel join j -------------------------------------------------------- /channel (oder auch: /join /j) Du betrittst den Beispiel: /channel Moderated Support %rejoin -------------------------------------------------------- /rejoin (oder auch: /resign) Entfernt deinen Status als Kanal Operator. %me emote -------------------------------------------------------- /me (oder auch: /emote) Zeigt deinen Namen und eine in einer anderen Farbe. Example: /emote will ein Spiel spielen. %kick -------------------------------------------------------- /kick kickt aus dem Kanal. Beispiel: /kick nomad %ban -------------------------------------------------------- /ban (siehe auch: /unban) Bannt aus dem Kana und hindert ihn daran, zurückzukommen. Beispiel: /ban nomad %unban -------------------------------------------------------- /unban Erlaubt einem gebannten , in den Kanal zurückzukehren. Beispiel: /unban nomad %serverban -------------------------------------------------------- /serverban (siehe auch: /unban) Bannt die IP eines und sperrt seinen Zugang. Beispiel: /serverban nomad %ipscan -------------------------------------------------------- /ipscan Findet alle zur Zeit eingeloggten Benutzer mit dem gesuchten oder . Beispiel: /ipscan 127.0.0.1 Beispiel: /ipscan nomad %whoami -------------------------------------------------------- /whoami Zeigt an, wo du dich auf dem Server befindest. %uptime -------------------------------------------------------- /uptime Zeigt an, seit wann der Server laeuft. %version -------------------------------------------------------- /version Zeigt die Serverversion an. %lusers -------------------------------------------------------- /lusers Zeigt die Lister der Spieler, die vom Server gebannt wurden. %games -------------------------------------------------------- /games [Spieletag] [Schwierigkeit] Zeigt eine Liste der Onlinespiele. Setze die [Schwierigkeit] nur für for Diablo Spiele(norm|night|hell) -------------------------------------------------------- /games all Zeigt eine Liste aller Spiele. /games l[obby] Zeigt eine Liste der Spiele in der Lobby. %channels chs -------------------------------------------------------- /channels [Spieletag] (oder auch: /chs) Zeigt eine Liste aller Kanäle für diesen [Spieletag]. -------------------------------------------------------- /channels all Zeigt eine Liste aller Kanäle. %connections con -------------------------------------------------------- /connections (oder auch: /con) Zeigt eine Liste der Verbindungen an. %finger -------------------------------------------------------- /finger Zeigt genaue Informationen über an. Beispiel: /finger nomad %admins -------------------------------------------------------- /admins Zeigt eine Liste der zur Zeit eingeloggten Admins an. %reply r -------------------------------------------------------- /reply (alias: /r) Antwortet dem letzten Spieler, der dich mit einer angeflüstert hat. Beispiel: /r Hi Kumpel! %announce ann -------------------------------------------------------- /announce (oder auch: /ann) Schickt eine an alle. Beispiel: /ann Hallo zusammen! %realmann -------------------------------------------------------- /realmann Schickt eine an alle in der aktuellen Diablo2 Instanz. Beispiel: /realmann Hallo zusammen! %alert -------------------------------------------------------- /alert Zeigt eine Textbox mit deiner an alle. Nutze \n um einen Absatz zu setzen. Beispiel: /alert Hallo\nzusammen! %news -------------------------------------------------------- /news Zeigt die Neuigkeiten an. %logout quit exit -------------------------------------------------------- /logout (alias: /quit /exit) Trennt dich vom Server. %kill -------------------------------------------------------- /kill {|#} [min] Trennt vom Server und bannt die IP des Spielers für [min] Minuten. Beispiel: /kill nomad 5 %killsession -------------------------------------------------------- /killsession [min] Trennt die Sitzung vom Server und bannt die zugehörige IP-Adresse für [min] Minuten. Beispiel: /killsession 1 5 %watch -------------------------------------------------------- /watch Du wirst über benachrichtigt. Beispiel: /watch nomad %unwatch -------------------------------------------------------- /unwatch Du wirst nicht mehr über benachrichtigt. Beispiel: /unwatch nomad %watchall -------------------------------------------------------- /watchall [Spieletag] Du wirst über die Aktionen von allen benachrichtigt. Beispiel: /watchall SEXP %unwatchall -------------------------------------------------------- /unwatchall [gametag] Du wirst nicht mehr über die Aktionen von allen benachrichtigt. Example: /unwatchall SEXP %gameinfo -------------------------------------------------------- /gameinfo Zeigt Informationen über . Beispiel: /gameinfo FS 1v1!!! %ladderactivate -------------------------------------------------------- /ladderactivate Kopiert aktuelle ladder Statistiken zu aktiven Ladder Statistiken %ladderinfo -------------------------------------------------------- /ladderinfo [Spieletag] Displays ladder information for of [gametag] Zeigt Ladderinformationen für den mi [Spieletag] Beispiel: /ladderinfo 1000 SEXP %timer -------------------------------------------------------- /timer [Nachricht] Zeigt [Nachricht] nach Sekunden. Beispiel: /timer 60 eine Minute ist vergangen. %netinfo -------------------------------------------------------- /netinfo [Spieler] Zeigt die Netzwerkinformationen von [Spieler]. Beispiel: /netinfo nomad %addacct -------------------------------------------------------- /addacct Erschafft einen neuen account mit Namen mit dem gewählten . Beispiel: /addacct nomad password %chpass -------------------------------------------------------- /chpass [] - ändert das Passwort von Ändert []s Passwort zu . Wenn du [] leer lässt, wird das Passwort zu deinem account geändert. Beispiel: /chpass nomad password %quota -------------------------------------------------------- /quota Zeigt, wieviel du geschrieben hast. %shutdown -------------------------------------------------------- /shutdown [Dauer] Stellt die Shutdown Sequenz auf [Dauer] Sekunden, oder hebt sie auf, wenn du die [Dauer] auf 0 setzt. Beispiel: /shutdown 300 %lock lockacct -------------------------------------------------------- /lock [Stunden] [Grund] (oder auch: /lockacct) Sperrt s account, damit er sich nicht einloggen kann. Setze [Stunden] = 0 um dauerhaft zu bannen. Beispiel: /lock nomad 0 bye noob! %unlock unlockacct -------------------------------------------------------- /unlock (oder auch: /unlockacct) Entsperrt s account, damit er sich wieder einloggen kann. Beispiel: /unlock nomad %mute muteacct -------------------------------------------------------- /mute [Stunden] [Grund] (oder auch: /muteacct) Schaltet s account auf stumm, so dass er in Kanälen nichts schreiben kann. Setze [Stunden] = 0 um den Account dauerhaft auf stumm zu schalten. Beispiel: /mute nomad 6 lass den spam! %unmute unmuteacct -------------------------------------------------------- /unmute (oder auch: /unmuteacct) Hebt die Stummschaltung von s account auf, damit er wieder in Kanälen schreiben kann. Beispiel: /unmute nomad %friends f -------------------------------------------------------- /friends [Optionen] (oder auch: /f) -------------------------------------------------------- /friends add Fügt deiner Freundesliste hinzu. /friends del Entfernt von deiner Freundesliste. /friends promote Befördert eine Zeile in deiner Freundesliste. /friends demote Stuft eine Zeile in deiner Freundesliste herab. /friends list Zeigt deine Freundesliste. /friends online Zeigt die Liste deiner Freunde, die online sind. /friends msg Flüstert an alle deine Freunde, die online sind. %mail -------------------------------------------------------- /mail [Optionen] -------------------------------------------------------- /mail s[end] Sendet einen Brief an mit der . /mail r[ead] [index] Liesst den Brief [index] /mail del[ete] {all|} Löscht Briefe im oder [all]e Briefe. %flag -------------------------------------------------------- /flag Ein Reperaturwerkzeug für Symboltags. %tag -------------------------------------------------------- /tag Ein Reperaturwerkzeug für Klienttags. %help -------------------------------------------------------- /help [Befehl] Zeigt die Hilfe zu [Befehl]. Beispiel: /help whisper %ipban -------------------------------------------------------- /ipban [Option] [Zeit] -------------------------------------------------------- /ipban l[ist] Zeigt eine Liste der gebannten IP-Adressen. /ipban c[heck] Überprüft ob die IP-Adresse gebannt ist. /ipban d[el] Loescht den Ban der IP Adresse oder den dazu. /ipban a[dd] [time] Bannt IP Addresse für [Zeit] Minuten. [Zeit] = 0 - dauerhafter Bann. %set -------------------------------------------------------- /set [Wert] Setzt oder den Wert von für den account , oder setzt ihn zurück. Setze [Wert] = null um den Wert zurückzusetzen. Beispiel: /set nomad BNET\auth\botlogin true Beispiel: /set nomad Record\SEXP\0_wins 999 %motd -------------------------------------------------------- /motd Zeigt die MOTD. %tos -------------------------------------------------------- /tos /warranty /license Zeigt die Endbenutzerbedingungen. %admin -------------------------------------------------------- /admin [+|-] Befördert/degradiert zum/als Server Administrator. Beispiel: /admin +nomad %rehash -------------------------------------------------------- /rehash Zwingt den Server, eine spezifische Konfig. zu laden. Setze auf "alle" um alle Konfigs neu zu laden. = alle | i18n | channels | realms | autoupdate | news | versioncheck | ipbans | helpfile | banners | tracker | commandgroups | aliasfile | transfile | tournament | icons | anongame | topiclist | lua %clan -------------------------------------------------------- /clan [Option] -------------------------------------------------------- /clan create Erschafft einen neuen Clan (max Länge = 4; Freizeichen sind erlaubt im n) Befehle für clan Mitglieder: /clan m[sg] (oder auch: [w]hisper) Fluestert eine Nachricht an alle deine Clan Mitglieder /clan inv[ite] Laed in deinen Clan ein. /clan inv[ite] get Zeigt, in welchen Clan du eingeladen wurdest. /clan inv[ite] accept Akzeptiert eine Clan Einladung. /clan inv[ite] decline Lehnt eine Clan Einladung ab. Befehle für Clanleader: /clan motd Updated die Clan "Nachricht des Tages". /clan pub[lic] (oder auch: pub) Öffnet den Clan Kanal für andere. /clan priv[ate] (oder auch: priv) Schliesst den Clan Kanal,nur Mitglieder haben zutritt. /clan dis[band] Löst den Clan Kanal auf. %ping p latency -------------------------------------------------------- /ping (oder auch: /p /latency) Zeigt deinen Ping zum Server. %command_groups cg -------------------------------------------------------- /command_groups [Gruppen] (oder auch: /cg) -------------------------------------------------------- /cg list Zeigt die Befehlgruppe zu . /cg add Fügt eine Befehlgruppe(n) zu hinzu. /cg del Löscht Befehlgruppe(n) von . %operator -------------------------------------------------------- /operator [+|-] (oder auch: /op) Befoerdert/degradiert zum/vom Server Operator. Beispiel: /operator +nomad %aop -------------------------------------------------------- /aop Befördert zum Kanal Administrator. Beispiel: /aop nomad %op -------------------------------------------------------- /op Befördert zum Kanal Operator. Beispiel: /op nomad %deop -------------------------------------------------------- /deop Degradiert als Kanal Administrator oder Operator. Beispiel: /deop nomad %tmpop -------------------------------------------------------- /tmpop Befördert zum vorübergehenden Kanal Operator. Beispiel: /tmpop %vop -------------------------------------------------------- /vop Fügt der VOP Liste hinzu. Beispiel: /vop nomad %voice -------------------------------------------------------- /voice Gibt vorübergehend Voice Rechte an . Beispiel: /voice nomad %devoice -------------------------------------------------------- /devoice Entfernt von der VOP Liste und vorübergehende Voice Rechte. Beispiel: /devoice nomad %topic -------------------------------------------------------- /topic [Nachricht] Setzt oder zeigt die aktuelle Kanal Überschrift. Beispiel: /topic Supported is given in this channel...\n(some text on a new line) %moderate -------------------------------------------------------- /moderate schaltet den aktuellen Moderationsstatus des Kanals um. %clearstats -------------------------------------------------------- /clearstats Löscht die Statistik von s , dabei kann jeder zulässige Klient oder JEDER Klient sein. Unterstützte Klienten: DRTL|DSHR|W2BN|STAR|SEXP|SSHR|WAR3|W3XP Beispiel: /clearstats nomad SEXP %find -------------------------------------------------------- /find Findet einen Nutzer mit dem in seinem Nutzernamen. Der MUSS in Kleinbuchstaben stehen! %save -------------------------------------------------------- /save Zwingt den Server, Änderungen am Account und Clan Änderungen in der Datenbank zu speichern. %icon -------------------------------------------------------- /icon [Name] Setzt ein spezifisches Icon für normale Nutzer. Nutze /icon ohne [Name] um die Liste der verfügbaren Icons in deinem Vorrat anzuzeigen. -------------------------------------------------------- Syntax fuer Operatoren/Admins: /icon a[dd] Fügt ein Icon dem Vorrat eines Nutzers hinzu. /icon d[el] Entfernt ein Icon aus dem Vorrat eines Nutzers /icon s[et] Setzt ein spezifisches Icon für den Nutzer, ohne es seinem Vorrat hinzuzufügen. /icon l[ist] Zeigt die Icons im Vorrat des Nutzers an. /icon l[ist] Zeigt die auf dem Server vorrätigen Icons an, die Nutzern zugewiesen werden können. %language /lang -------------------------------------------------------- /lang [code] Stellt deine Sprache auf eine andere Übersetzung um: %quiz -------------------------------------------------------- /quiz [Option] Trivia Quiz Spiel -------------------------------------------------------- /quiz start Startet ein Spiel mit der Datenbak im aktuellen Kanal /quiz stop Beendet das Quiz erzwungen. /quiz stats [Nutzername] Zeigt die Quiz Statistik für [Nutzername] /quiz stats Zeigt die Bestenliste des Quiz Nutzername pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/bnmotd.txt000066400000000000000000000005771462640461400205420ustar00rootroot00000000000000%EHallo %l, willkommen zu %s! %ILaufende Version %v auf %h. %IDer Server ist von %H gehostet %I %IEs sind %a Benutzerprofile auf dem Server. %IAktuell sind %U Spieler in %G Spielen von %N, %Iund %u Spieler spielen %g Spiele und chatten in %c Kanälen. %IEin Problem mit den Befehlen? Schreibe /help und %Ilerne mehr über die Befehle. %IType /language to switch translation. %I %M%m pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/chathelp-war3.txt000066400000000000000000000007021462640461400217070ustar00rootroot00000000000000PvPGN Bnet Emulation Chat Hilfe Befehle Neue Befehle: nutze /f help für Hilfe mit der Freundesliste. nutze /users um zu sehen, welche Nutzer online sind. nutze /w or /msg um Nutzer anzuflüstern. nutze /join um einem Kanal beizutreten Fuerr Chan Ops: nutze /ban um einen Nutzer aus dem Kanal zu bannen. nutze /kick um einen Nutzer aus dem Kanal zu kicken. Für eine genauere Hilfe schreibe: /help pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/common.xml000066400000000000000000003140211462640461400205200ustar00rootroot00000000000000 German DEU AUT LIE LUX by {} von {} for {} wegen {} permanently dauerhaft with a reason "{}" wegen dem grund "{}" No such alias. Use // to show the list. Kein solcher alias. Nutze // um die Liste zu zeigen. Alias list: Alias Liste: you are now tempOP for this channel Du bist jetzt tempOP in diesem Kanal You can't talk on the channel. Your account has been muted Du kannst in diesem Kanal nichts sagen. Dein Account wurde auf stumm gesetzt This channel is moderated Dieser Kanal ist moderiert No one hears you. Niemand hört dich. Account name contains some invalid symbol! Der Account Name beeinhalted unzulässige Symbole! Maximum password length allowed is {} Die maximale Länge für ein Passwort ist {} Trying to add account "{}" with password "{}" Versuche Account "{}" mit Passwort "{}" hinzuzfügen Hash is: {} Hash ist {} Failed to create account! Accounterstellung fehlgeschlagen! Account {} created. Account {} erstellt. You must supply a username. Du musst einen Nutzernamen eintragen. There's no account with username {}. Es gibt keinen Account mit dem Nutzernamen {}. {} is already a Server Admin {} ist schon Server Admin {} has been promoted to a Server Admin {} wurde zu einem Server Admin befördert {} has promoted you to a Server Admin {} hat dich zu einem Server Admin befördert {} is not a Server Admin. {} ist kein Server Admin. {} has been demoted from a Server Admin {} wurde vom Rang Server Admin degradiert {} has demoted you from a Server Admin {} hat dich vom Rang Server Admin degradiert Currently logged on Administrators: Momentan eingeloggte Administratoren: Your game client doesn't support MessageBox. Dein Game Client unterstützt keine MessageBox. \n\n***************************\nBy {} \n\n***************************\nBy {} Information from {} Information von {} for {} Announcement from {}: {} Bekanntmachung durch {}: {} Could not broadcast message. Konnte Nachricht nicht übertragen. This command can only be used inside a channel. Dieser Befehl kann nur innerhalb eines Kanals genutzt werden. You must be at least a Channel Admin to use this command. Du musst mindestestens ein Kanal Admin sein um diesen Befehl zu nutzen. There's no account with username {}. {} is already a Channel Admin {} ist bereits Kanal Admin {} has been promoted to a Channel Admin {} wurde zu einem Kanal Admin befördert {} has promoted you to a Channel Admin for channel "{}" {} wurde zu einem Kanal Admin für den Kanal "{}" befördert You are now marked as being away. Du wirst nun als abwesend angezeigt. You are no longer marked as away. Du wirst nicht mehr als abwesend angezeigt. This command can only be used inside a channel. You have to be at least a Channel Operator to use this command. Du musst mindestens ein Channel Operator sein um diesen Befehl zu nutzen. That account doesn't currently exist. Dieser Account existiert momentan nicht. You cannot ban administrators. Du kannst Administratoren nicht bannen. Audible notification on. Hoerbare Benachrichtigungen an. Channel Arranged Teams is a RESTRICTED Channel! Der Kanal Arranged Teams ist nur bestimmten Gruppen zugänglich! Max channel name length exceeded (max {} symbols) Maximale Länge fuer Kanal Name überschritten (max. {} Symbole) Command disabled while inside a game. Befehl im Spiel deaktiviert Currently accessible channels: Momentan verfügbare Kanäle: All current channels: Alle momentane Kanäle: Current channels of type {} Momentane Kanäle vom Typ {} -----------name----------- users ----admin/operator---- -----------Name----------- Nutzer ----Admin/Operator---- Only admins may change passwords for other accounts. Nur Admins können Passwoerter anderer Accounts ändern. Account does not exist. Account existiert nicht. Maximum password length allowed is {} Trying to change password for account "{}" to "{}" Versuche Passwort von Account "{}" in "{}" zu ändern. Unable to set password. Passowrt setzen nicht möglich. Message was sent to all currently available clan members. Nachricht wurde an alle momentan verfügbare Clan Mitglieder gesendet. All fellow members of your clan are currently offline. Alle deine Clan Mitglieder sind momentan offline. Clan channel is opened up! Clan Kanal wurde geöffnet! Clan channel has already been opened up! Clan Kanal wurde bereits geöffnet! Clan channel is closed! Clan Kanal ist geschlossen! Clan channel has already been closed! Clan Kanal wurde bereits geschlossen! Clan message of day is updated! Clan Nachricht des Tages wurde aktualisiert! User {} was invited to your clan! Nutzer {} wurde in deinen Clan eingeladen! You are invited to {} by {}! Du wurdest von {} zu {} eingeladen! User {} is not online or is already member of clan! Nutzer {} ist nicht online oder bereits Mitglied eines Clans! This is one-way action! If you really want Dies ist eine unumkehrbare Aktion! Wenn du wirklich to disband your clan, type /clan disband yes um deinen Clan aufzulösen, schreibe /clan disband yes Your clan was disbanded. Dein clan wurde aufgelöst. You have been invited to {} Du wurdest zu {} eingeladen You are now a clan member of {} Du bist nun Mitglied des Clans {} Clan {} was be created Clan {} wurde erstellt You are no longer ivited to {} Du bist nicht länger zu {} eingeladen You are already in clan "{}" Du bist bereits im Clan "{}" Clan with your specified &lt;clantag&gt; already exist! Clan mit den Spezifikationen &lt;clantag&gt; existiert bereits! Please choice another one. Bitte wähle etwas anderes. Clan {} is created! Clan {} wurde erstellt! Clan {} is pre-created, please invite Clan {} ist vor-erstellt, bitte lade mindestens at least {} players to your clan by using {} Spieler ein deinem Clan beizutreten, mit dem /clan invite &lt;username&gt; command. /clan invite &lt;username&gt; Befehl. Invalid user. Invalid user. {}'s command group(s): {} {}s Kommandogruppe(n): {} Got bad group: {} Fehlerhafte Gruppe: {} Groups {} has been added to {} Gruppe {} wurde zu {} hinzugefügt. Groups {} has been deleted from {} Gruppe {} wurde von {} gelöscht. Got unknown command: {} Fehlerhafter Befehl: {} This command is only enabled for admins. Dieser Befehl ist Admins vorbehalten. Current connections: Momentane Verbindungen: -class -tag -----name------ -lat(ms)- ----channel---- --game-- -Klasse -Tag -----Name------ -Verz(ms)- ----Kanal---- --Spiel-- -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game-- -#- -Klasse ----Status--- -Tag -----Name------ -Sitzung-- -Flagge- -Verz(ms)- ----Kanal---- --Spiel-- -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game-- ---------addr-------- -#- -Klasse ----Status--- -Tag -----Name------ -Sitzung-- -Flagge- -Verz(ms)- ----Kanal---- --Spiel-- ---------Addr-------- Unknown option. Unbekannte Option. (none) (keine) none keine This command can only be used inside a channel. You must be at least a Channel Operator or tempOP to use this command. There's no account with username {}. You must be at least a Channel Admin to demote another Channel Admin Du musst mindestens ein Kanal Admin sein um einen anderen Kanal Admin zu degradieren {} has been demoted from a Channel Admin. {} wurde von einem Kanal Admin herabgestuft. {} has demoted you from a Channel Admin of channel "{}" {} hat dir den Status Kanal Admin des Kanals "{}" aberkannt {} has been demoted from a Channel Operator {} wurde der Status von einem Kanal Operator aberkannt {} has demoted you from a Channel Operator of channel "{}" {} hat dir den Status Kanal Operator des Kanals "{}" aberkannt {} has been demoted from a tempOP of this channel {} wurde der Status tempOP dieses Kanals aberkannt {} has demoted you from a tmpOP of channel "{}" {} hat dir den Status tempOP des Kanals "{}" aberkannt {} is no Channel Admin or Channel Operator or tempOP, so you can't demote him. {} ist kein Kanal Admin oder Kanal Operator oder tempOP, weswegen du ihn nicht degradieren kannst {} has demoted you from a tempOP of channel "{}" {} hat dir den Status tempOP des Kanals "{}" aberkannt. {} is no tempOP in this channel, so you can't demote him {} ist kein tempOP in diesem Kanal, weswegen du ihn nicht degradieren kannst This command can only be used inside a channel. You must be at least a Channel Operator to use this command. There's no account with username {}. {} has been removed from VOP list. {} wurde von der VOP Liste entfernt. {} has removed you from VOP list of channel "{}" {} hat dich von der VOP Liste des Kanals "{}" entfernt. You must be at least Channel Admin to remove {} from the VOP list Du musst mindestens Kanal Admin sein um {} von der VOP Liste zu entfernen Voice has been taken from {} in this channel Voice wurde von {} in diesem Kanal entfernt {} has taken your Voice in channel "{}" {} hat dir Voice im Kanal "{}" genommen {} has no Voice in this channel, so it can't be taken away {} hat kein Voice in diesem Kanal, weswegen es nicht agenommen werden kan Do Not Disturb mode engaged. Do Not Disturb Modus aktiviert. Not available Nicht verfügbar. Do Not Disturb mode canceled. Do Not Disturb Modus abgebrochen. -- name -- similar to {} -- Name -- aehnelt {} Invalid user. Login: {} {} Sex: {} Login: {} {} Geschlecht: {} Created: {} Erstellt: {} Clan: {} Klan: {} Rank: Chieftain Rang: Häuptling Rank: Shaman Rang: Schamane Rank: Grunt Rang: Kämpfer Rank: Peon Rang: Arbeiter Location: {} Age: {} Ort: {} Alter: {} Client: {} Ver: {} Country: {} Client: {} Ver: {} Land: {} unknown unbekannt Last login {} from letzter login {} von On since {} from On seit {} durch Operator: {}, Admin: {}, Locked: {}, Muted: {} Operator: {}, Admin: {}, Geschlossene: {}, stumm geschaltet: {} Yes ja Email: {} Email: {} Last login Owner: {} Letzter Login Besitzer: {} Idle {} Untätig {} Flags set to {}. Flaggen als {} gesetzt. That user does not exist. Dieser Nutzer existiert nicht. Server error. Serverfehler. You can't add yourself to your friends list. Du kannst dich nicht deiner eigenen Friends List hinzufügen. You can only have a maximum of {} friends. Du kannst nur maximal {} Freunde haben. {} is already on your friends list! {} ist bereits in deiner Friends List! Added {} to your friends list. {} zu deiner Friends List hinzugefügt. {} added you to his/her friends list. {} hat dich zu seiner/ihrer Friends List hinzugefügt. All of your friends are offline. Alle deiner Freunde sind offline. {} was not found on your friends list. {} wurde nicht in deiner Friends List gefunden. Removed {} from your friends list. {} wurde von deiner Friends List entfernt. Promoted {} in your friends list. {} wurde in deiner Friends List befördert. Demoted {} in your friends list. {} wurde in deiner Friends List herabgestuft. Your {} - Friends List Deine {} - Friends List Your {} - Online Friends List Deine {} - Online Friends List , offline , offline using {} nutzt {} , in game "{}" , im Spiel "{}" , in game AT Preparation , in der AT Vorbereitung , in channel "{}", , im Kanal "{}", , is in AT Preparation , ist in der AT Vorbereitung , is in a game , ist im Spiel , is in a chat channel , ist in einem Chat Kanal End of Friends List Ende der Friends List You are not in a game. Du bist nicht in einem Spiel. That game does not exist. Dieses Spiel existiert nicht. Name: {} ID: {} ({}) Name: {} ID: {} ({}) none unknown Owner: {} Besitzer: {} Address: {} Addresse: {} Address: {} (trans {}) Addresse: {} (trans {}) Client: {} (version {}, startver {}) Client: {} (Version {}, Startver {}) Created: {} Started: {} Begonnen: {} Status: {} Status: {} Type: {} Typ: {} Speed: {} Geschwindigkeit: {} Difficulty: {} Schwierigkeit: {} Option: {} Option: {} Map: {} Karte: {} Map Size: {}x{} Kartengrösse: {}x{} Map Tileset: {} Kartentyp: {} Map Type: {} Kartentyp: {} Players: {} current, {} total, {} max Spieler: {} momentan, {} total, {} max Description: {} Beschreibung: {} public öffentlich private privat Currently accessible games: Momentan verfügbare Spiele: All current games: Alle momentanen Spiele: Games in lobby: Spiele in der Lobby: Current games of type {} Momentane Spiele des Typs {} Current games of type {} {} Momentane Spiele des Typs {} {} ------name------ p -status- --------type--------- count ------Name------ p -Status- --------Typ--------- Zahl ctag ctag --------addr-------- --------Addr-------- Warning: That user is not online, using last known address. Warnung: Dieser Nutzer ist nicht online, nutze letzte bekannte Addresse. Sorry, no IP address could be retrieved. Entschuldigung, keine IP Adresse auffindbar. Scanning online users for IP {}... Scanne online Nutzer für IP {}... There are no online users with that IP address Es gibt keine online Nutzer mit dieser IP Adresse This command can only be used inside a channel. You have to be at least a Channel Operator or tempOP to use this command. Du musst mindestens Kanal Operator oder tempOP sein um diesen Befehl zu nutzen. That user is not logged in. Dieser Nutzer ist nicht eingeloggt. That user is not in this channel. Dieser Nutzer ist nicht in diesem Kanal. That connection doesn't exist. Die Verbindung existiert nicht. That user is not logged in? Dieser Nutzer ist nicht eingeloggt? An admin has closed your connection and banned your IP address. Ein Admin hat deine Verbindung geschlossen und deine IP Adresse gebannt. Connection closed by admin. Verbindung durch einen Admin geschlossen. Operation successful. Operation erfolgreich. That is not a valid session. Das ist keine gültige Sitzung. That session does not exist. Diese Sitzung existiert nicht. Connection closed by admin and banned your IP's. Verbindung durch einen Admin geschlossen und deine IPs sind gebannt. Connection closed by admin. Copied current scores to active scores on all ladders. Momentante Resultate auf aktiven Resultate aller Ladder kopiert. This game does not support win/loss records. You must supply a rank and a valid program ID. Du musst einen Rang und eine valide Programm ID zur Verfügung stellen. Example: /ladderinfo 1 STAR Beispiel: /ladderinfo 1 STAR Unable to determine client game. Invalid user. Account {} is now locked Account {} ist jetzt gesperrt Your account has just been locked Dein Account wurde gerade gesperrt. This command can only be used inside a channel. Banned users: Gebannte Nutzer: You are not in a channel. Du bist nicht in einem Kanal. This command can only be used inside a channel. You must be at least a Channel Operator to use this command. Unable to change channel flags. Flaggentausch für den Kanal nicht möglich. Channel is now unmoderated. Kanal ist jetzt nicht moderiert. Channel is now moderated. Kanal ist jetzt moderiert. Unable to open motd. Öffnen der MOTD nicht möglich. No motd. Keine motd. Invalid user. Account {} is now muted Account {} ist jetzt stumm geschaltet. Your account has just been muted Dein Account wurde gerade eben stumm geschaltet. That user is not logged on. Address information for other users is only available to admins. Adressen anderer Nutzer sind nur für Admins verfügbar. Server TCP: {} (bind {}) Server TCP: {} (bind {}) Client TCP: {} Client TCP: {} Client UDP: {} Client UDP: {} Client UDP: {} (trans {}) Client UPD: {} (trans {}) Game UDP: {} Spiel UDP: {} Game UDP: {} (trans {}) Spiel UDP: {} (trans {}) Game UDP: none Spiel UDP: keine Audible notification off. Hörbare Benachrichtigung aus. This command can only be used inside a channel. You must be at least a Channel Operator or tempOP to use this command. Du musst mindestens ein Kanal Operator oder ein tempOP sein um diesen Befehl zu nutzen. There's no account with username {}. {} is already a Channel Operator {} ist bereits ein Kanal Operator {} has been promoted to a Channel Operator {} wurde zu einem Kanal Operator befördert. {} has promoted you to a Channel Operator in channel "{}" {} wurde zu einem Kanal Operator im Kanal "{}" befördert {} must be on the same channel to tempOP him {} muss im gleichen Kanal sein um ihn als tempOP zu setzen {} already is operator or admin, no need to tempOP him {} ist bereits operator or admin, tempOP nicht nötig {} has been promoted to a tempOP {} wurde zu einem tempOP befördert. {} has promoted you to a tempOP in this channel {} hat dich zu einem tempOP in diesem Kanal befördert. You must supply a username. There's no account with username {}. {} is already a Server Operator {} ist bereits ein Server Operator {} has been promoted to a Server Operator {} wurde zu einem Server Operator befördert {} has promoted you to a Server Operator {} hat dich zu einem Server Operator befördert {} is no Server Operator, so you can't demote him {} ist kein Server Operator, weswegen du ihn nicht degradieren kannst. {} has been demoted from a Server Operator {} wurde der Server Operator Status aberkannt. {} latency: {} {} Verzögerung: {} Your latency {} Deine Verzögerung {} {} latency () {} Verzögerung () Invalid user. Connection closed. Verbindung geschlossen. Your quota allows you to write {} line(s) per {} second(s). Deine Quote erlaubt es dir {} Zeilen pro {} Sekunden zu schreiben. Long lines will be wrapped every {} characters. Längere Zeilen werden nach jeweils {} Zeichen umgebrochen. You are not allowed to send lines with more than {} characters. Dir ist es nicht erlaubt Zeilen mit mehr als {} Zeichen zu senden. You must join a realm first Du musst erst einen Realm beitreten. Announcement from {}@{}: {} Benachrichtigung von {}@{}: {} Invalid mode. Unzulässiger Modus. Rehash of "{}" is complete! Rehash von "{}" ist komplett! You are not in a channel. No one messaged you, use /m instead Niemand schrieb dir, nutze /m stattdessen Pending changes has been saved into the database. Ausstehende Änderungen wurden in die Datenbank gespeichert. That user is not logged on. Banning {} who is using IP address {} Banne {}, der die IP Adresse {} nutzt User's account is also LOCKED! Only an admin can unlock it! Nutzer Account ist auch GESPERRT! Nur ein Admin kann ihn entsperren! /ipban a {} /ipban a {} You have been banned by Admin: {} Du wurdest von Admin: {} gebannt Your account is also LOCKED! Only an admin can UNLOCK it! Dein Account ist auch GESPERRT! Nur ein Admin kann ihn ENTSPERREN! Access denied due to security reasons. Zugang wegen Sicherheitsgruenden verweigert. Invalid user. Current value of {} is "{}" Momentaner Wert von {} ist "{}" Value currently not set Wert momentan nicht gesetzt Unable to set key for Unsetzbarer Schlüssel für Key set successfully for Erfolgreich Schlüssel gesetzt für Invalid delay. Unzulässige Verzögerung. You've initialized the shutdown sequence. Du hast die Shutdownsequenz eingeleitet. You've canceled the shutdown sequence. Du hast die Shutzdownsequenz abgebrochen. No such user. Kein solcher Nutzer. You can't squelch yourself. Du kannst dich nicht selbst ignorieren. Could not squelch user. Konnte Nutzer nicht ignorieren. {} has been squelched. {} wird ignoriert. Invalid user. Unzulässiger Nutzer. Unable to determine client game. Das Bestimmen des Client Spiels ist nicht möglich. This game does not support win/loss records. Dieses Spiel unterstützt keine Win/Loss Statistiken. You must supply a user name and a valid program ID. Du musst einen Nutzernamen und eine gültige Programm ID zur Verfügung stellen. Example: /stats joe STAR Beispiel: /stats joe STAR {}'s record: {}s Rekord: level: {} Level: {} class: {} Klasse: {} stats: {} str {} mag {} dex {} vit {} gld Stats: {} str {} mag {} dex {} vit {} gld Diablo kills: {} Diablo Kills: {} Normal games: {}-{}-{} Normale Spiele: {}-{}-{} Ladder games: {}-{}-{} (rating {}) Ladder Spiele: {}-{}-{} ({} Punkte) Ladder games: 0-0-0 Ladder Spiele: 0-0-0 IronMan games: {}-{}-{} (rating {}) IronMan Spiele: {}-{}-{} ({} Punkte) IronMan games: 0-0-0 IronMan Spiele: 0-0-0 {}'s Ladder Record's: {} Ladder Statistiken: Users Solo Level: {}, Experience: {} Nutzer Solo Level: {}, Erfahrung: {} SOLO Ladder Record: {}-{}-0 SOLO Ladder Statistik: {}-{}-0 SOLO Rank: {} SOLO Rang: {} Users Team Level: {}, Experience: {} Nutzer Team Level: {}, Erfahrung: {} TEAM Ladder Record: {}-{}-0 TEAM Ladder Statistik: {}-{}-0 TEAM Rank: {} TEAM Rang: {} Users FFA Level: {}, Experience: {} Nutzer FFA Level: {}, Erfahrung: {} FFA Ladder Record: {}-{}-0 FFA Ladder Stat file=istik: {}-{}-0 FFA Rank: {} FFA Rang: {} Users AT Team No. {} Nutzer AT Team Nr. {} Users AT TEAM Level: {}, Experience: {} Nutzer AT TEAM Level: {}, Erfahrung: {} AT TEAM Ladder Record: {}-{}-0 AT TEAM Ladder Statistik: {}-{}-0 AT TEAM Rank: {} AT TEAM Rang: {} There are currently {} user(s) in {} games of {} Es sind momentan {}Nutzer in {} Spielen des Titels {} There are currently {} users online, in {} games, and in {} channels. Es sind momentan {} Nutzer online, in {} Spielen, und {} Kanälen. Client tag set to {}. Client Tag auf {} gesetzt. Invalid clienttag {} specified Unzulässiger Clienttag {} spezifiziert. Server Time: {} Server Zeit: {} Your local time: {} Deine Lokalzeit: {} Invalid duration. Unzulässige Dauer. Your timer has expired. Dein Timer ist abgelaufen. Could not set timer. Konnte Timer nicht setzen. Timer set for {} second(s) Timer auf {} Sekunden gesetzt. This command can only be used inside a channel. You must be at least a Channel Operator or tmpOP to use this command. Du musst mindestens ein Kanal Operator oder ein tempOP sein um diesen Befehl zu nutzen. There's no account with username {}. {} has already tmpOP in this channel {} hat bereits tempOP in diesem Kanal {} must be on the same channel to tempOP him {} already is operator or admin, no need to tempOP him {} has been promoted to tmpOP in this channel {} wurde zu einem tempOP in diesem Kanal befördert {} has promoted you to a tempOP in this channel This command can only be used inside a channel. Max topic length exceeded (max {} symbols) Die max. Themalänge wurde überschritten (max. {} Symbole) You must be at least a Channel Operator of {} to set the topic Du musst mindestens ein Kanal Operator von {} sein, um das Thema zu setzen. {} topic: no topic {} Thema: Kein Thema Unable to send TOS (Terms of Service). Nicht möglich AGB zu senden (Allgemeine Geschaeftsbedingungen) This command can only be used inside a channel. You are not a channel operator. Du bist kein Kanal Operator. That user is not banned. Dieser nutzer ist nicht gebannt. {} is no longer banned from this channel. {} ist nicht länger von diesem Channel gebannt. Invalid user. Your account has just been unlocked by {} Dein account wurde gerade von {} entsperrt That user's account is now unlocked. Dieser Nutzeraccount ist jetzt entsperrt. Invalid user. Your account has just been unmuted by {} Dieser Account wurde gerade von {} auf nicht-stumm geschaltet. That user's account is now unmuted. Dieser Nutzeraccount ist jetzt nicht mehr stumm. No such user. User was not being ignored. Nutzer wurde nicht ignoriert. No longer ignoring. Nicht länger ignorierend. That user does not exist. Removal from watch list failed. Entfernen von der Watch List fehlgeschlagen. User {} removed from your watch list. Nutzer {} von Watch List entfernt. Removal from watch list failed. All {} users removed from your watch list. Alle {} Nutzer von Watch List entfernt. All users removed from your watch list. Alle Nutzer von Watch List entfernt. Uptime: {} Laufzeit: {} This command can only be used inside a channel. You must be at least a Channel Operator to use this command. Du musst mindestens ein Kanal Operator sein, um diesen Befehl zu nutzen. There's no account with username {}. {} is already on VOP list, no need to Voice him {} is bereits auf der VOP list, kein Voice benötigt. {} must be on the same channel to voice him {} muss im gleichen Kanal sein um ihm Voice zu geben. {} already has Voice in this channel {} hat bereits Voice in diesem Kanal {} is already an operator or admin. {} ist bereits ein Operator oder Admin. {} has been granted Voice in this channel {} wurde Voice in diesem Kanal gegeben. {} has granted you Voice in this channel {} hat dir Voice in diesem Channel gegeben. This command can only be used inside a channel. You must be at least a Channel Admin to use this command. There's no account with username {}. {} is already on VOP list {} ist bereits auf der VOP Liste. {} has been added to the VOP list {} wurde auf die VOP Liste gesetzt. {} has added you to the VOP list of channel "{}" {} hat dich auf die VOP Liste des Kanals "{}" gesetzt. That user does not exist. Add to watch list failed. Hinzufügen auf die Watch List fehlgeschlagen. User {} added to your watch list. Nutzer {} zur Watch List hinzugefuegt. Add to watch list failed. All {} users added to your watch list. Alle {} Nutzer zur Watch List hinzugefügt. All users added to your watch list. Alle Nutzer zur Watch List hinzugefügt. That channel does not exist. Dieser Kanal existiert nicht. (If you are trying to search for a user, use the /whois command.) (Wenn du einen Nutzer suchst, nutze den /whois Befehl.) You are banned from that channel. Du bist in diesem Kanal gebannt. Users in channel {}: Nutzer im Kanal {}: Unable to obtain your account name. Beziehen deines Nutzernamens fehlgeschlagen. (invalid date) (unzulässiges Datum) Reset {}'s {} stats Zurücksetzen von {}s {} Statistik Reset {}'s {} stats Reset {}'s {} stats Created: {} Clan: {} Rank: Chieftain Rank: Shaman Rank: Grunt Rank: Peon Client: {} Ver: {} Country: {} unknown Last login {} from On since {} from Yes No Nein Operator: {}, Admin: {}, Locked: {}, Muted: {} Email: {} Last login Owner: {} Idle {} You cannot kick administrators. Du kannst Administratoren nicht kicken. You cannot kick operators. Du kannst Operatoren nicht kicken. {} has been kicked by {} ({}). {} wurde von {} gekickt ({}). {} has been kicked by {}. {} wurde von {} gekickt. You cannot ban operators. Du kannst Operatoren nicht bannen. Unable to ban {}. Bannen von {} nicht möglich. {} has been banned by {} ({}). {} von {} gebannt. ({}). {} has been banned by {}. {} von {} gebannt. Password for account {} updated. Passwort von Account {} aktualisiert. Hash is: {} Your account has been muted, you can't whisper to other users. Dein Account wurde stumm gesetzt, du kannst keinem anderen Nutzer flüstern. That user is not logged on. Dieser Nutzer ist nicht eingeloggt. {} is unavailable ({}) {} ist nicht verfügbar ({}) {} is away ({}) {} ist abwesend ({}) Unknown user. Unbekannter Nutzer. User was last seen on: {} Nutzer zuletzt gesehen am: {} User is offline Nutzer ist offline. You Du are bist is ist {} {} using {} and {} currently in {} game "{}". {} {} nutzt {} und {} ist momentan im {} Spiel "{}" {} {} using {} and {} currently in channel "{}". {} {} nutzt {} und {} ist momentan im Kanal "{}" {} {} using {}. {} {} nutzt {}. {} {} refusing messages ({}) {} {} lehnt Nachrichten ab ({}). {} away ({}) {} abwesend ({}) You are sending commands to {} too quickly and risk being disconnected for flooding. Please slow down. Du sendest Befehle an {} zu schnell ab und riskierst einen Verbindungsabbruch wegen Fluten. Bitte mach langsamer. This command has been deactivated Dieser Befehl wurde deaktiviert This command is reserved for admins. Dieser Befehl ist Admins vorbehalten. Unknown command. Unbekannter Befehl. Your line length quota has been exceeded! Maximale Eingabelänge überschritten! Your message quota has been exceeded! Maximale Nachrichtenanzahl überschritten! This is a private clan channel, unable to join! Das ist ein privater Kanal, du kannst nicht beitreten You are banned from that channel. That channel is for Admins/Operators only. Dieser Kanal ist nur Admins/Operatoren zugänglich. The channel is currently full. Dieser Kanal ist zur Zeit voll. This channel does not have chat privileges. Dieser Kanal hat keine Chat Privilegien. This channel is moderated. Dieser Kanal ist moderiert. Created ladder game Ladder Spiel erstellt Joined ladder game Ladder Spiel beigetreten This account has been locked Dieser Account wurde gesperrt. This account has been locked No help available for that command Keine Hilfe zu diesem Befehl verfügbar. Oops ! There is a problem with the help file. Please contact the administrator of the server. Oops ! Es gibt ein Problem mit der Hilfedatei. Bitte kontaktiere den Administrator des Servers. Chat commands : Chat Befehle : Bad language code. falscher Sprachcode. Set your language to {} Setze deine Sprache auf {} This command can only be used inside a channel. This command can only be used from the game. Dieser Befehl kann nur im Spiel genutzt werden. Bad icon. falsches Icon. Set default icon. Setze Standard Icon. Set new icon is succeed. Neues Icon erfolgreich gesetzt. You have {} icons in stash: Du hast {} Icons zur Verfügung: Available icons in server stash: zur Verfügung stehende Icons im Server Lager Invalid user. Set default icon for {} Setze Standard Icon für {} That icon doesn't exist in server stash. Dieses Icon existiert nicht im Server Lager. Set new icon is succeed for {} Neues Icon erfolgreich gesetzt für {} {} has {} icons in stash : {} hat {} Icons im Lager : User already has that icon in stash. Nutzer hat dieses Icon schon im Lager. Add new icon to {}'s stash. Füge neues Icon zu {}'s Lager hinzu. That icon doesn't exist in user stash. Dieses Icon existiert nicht im Lager des Nutzers. Delete icon from {}'s stash. Lösche Icons aus {}'s Lager. IP not banned. IP nicht gebannt Error occured. Fehler aufgetreten. IP banned by rule #{}. IP gebannt durch Regel #{}. Illegal IP entry. ungültiger IP Eintrag. No matching entry. Kein passender Eintrag. Wrong entry number. falsche Eingabenummer. Entry deleted. Eintrag gelöscht. Banned IPs: gebannte IPs: (perm) (permanent) none {} banned permamently by {}. {} permanent gebannt von {}. {} banned permamently. {} permanent gebannt. {} banned for {} by {}. {} wegen {} von {} gebannt. {} banned for {}. {} wegen {} gebannt. Bad IP. falsche IP. Banning only for: {} minutes. Ban beschränkt auf: {} Minuten. This server has NO mail support. Dieser Server bietet KEINEN Mail Support an. Please specify which message to delete. Use the following syntax: /mail delete {&lt;index&gt;|all} . Bitte spezifizieren, welche Nachricht gelöscht werden soll. Nutze den folgenden Syntax: /mail delete {&lt;index&gt;|all} Successfully deleted messages. Nachrichten erfolgreich gelöscht. Invalid index. Please use /mail delete {&lt;index&gt;|all} where &lt;index&gt; is a number. Unzulaessiger Index. Bitte nutze /mail delete {&lt;index&gt|all} für &lt;index&gt; ist eine Nummer. Succesfully deleted message. Nachricht erfolgreich gelöscht. You have no mail. Du hast keine Mails. There was an error completing your request. Fehler aufgetreten beim Vervollständigen deiner Anfrage. ID Sender Date ID Absender Datum Use /mail read &lt;ID&gt; to read the content of any message Nutze /mail read &lt;ID&gt; um den Inhalt einer Nachricht zu lesen Invalid index. Please use /mail read &lt;index&gt; where &lt;index&gt; is a number. Unzulaessiger Index. Bitte nutze /mail read &lt;index&gt; wobei &lt;index&gt; ist eine Nummer. Receiver UNKNOWN! Empfänger UNBEKANNT! Receiver has reached his mail quota. Your message will NOT be sent. Empfänger hat seine maximale Mail Quote erreicht. Deine Nachricht wird NICHT gesendet. Your mail has been sent successfully. Deine Nachricht wurde erfolgreich versendet. There was an error completing your request! Es gab einen Fehler bei der Vervollständigung deiner Anfrage! You must be at least a Channel Operator to use this command. This command can only be used inside a channel. Quiz has already ran in channel "{}". Use /quiz stop to force finish. Quiz wurde bereits in Kanal "{}" ausgeführt. Nutze /quiz stop um das Beenden zu erzwingen. Available Quiz dictionaries: vorliegende Quiz Datenbanken: Quiz is not running. Quiz läuft nicht. {}'s Quiz record: {}'s Quiz Statistik {} has never played Quiz. {} hat nie Quiz gespielt. Top {} Quiz records: Top {} Quiz Statistik: points Punkte This command is reserved for admins. User "{}" is offline Nutzer "{}" ist offline pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/newaccount.txt000066400000000000000000000032001462640461400214070ustar00rootroot00000000000000Sie sind zur Zeit mit einem PvPGN Server (Player -vs- Player Gaming Network) verbunden. PvPGN Allgemeine Geschaeftsbedingungen: PvPGN ist ein Battle.net(R) Emulationsserver und ist auf keine Weise verbunden mit bnetd, warforge, oder Blizzard Entertainment(R). PvPGN wird zur Verfügung gestellt "wie er ist" ohne Gewährleistungen jedweder Art. Wir unterstützen Softwarepiraterie nicht und halten Sie an, eine legale Kopie der Spiele zu erwerben, die Sie auf unserem Server spielen möchten. 1. Inhalte, die auf PvPGN gefunden werden. PvPGN unterstützt oder garantiert nicht für die Genauigkeit, den Wahrheitsgehalt oder die Zuverlässigkeit jedweder Informationen, darunter auch Meinungen und Ratschläge, die auf oder über PvPGN vermittelt werden. Jede und alle Aussagen, die in Foren, Chatverläufen oder in Spielen gemacht werden, geben nur die Meinung ihrer Urheber wieder. 2. Ausschluss von Gewährleistung. PvPGN wird gestellt "wie er ist" ohne Gewährleistungen irgendeiner Art. PvPGN gewährleistet nicht, dass ihr Programm ungestört und fehlerfrei läuft. 3. begrenzte Haftbarkeit PvPGN ist in keiner Weise haftbar für Verlust oder Schaden jedweder Art, der aus der Benutzung von PvPGN resultiert. Das schliesst ein, ist aber nicht begrenzt auf, Verlust von Geschäftswerten, Computerfehlern oder -Versagen und jedwede Art von Schaden, Verlust, sei er kommerziell oder privat. Battle.net(R) und Blizzard Entertainment(R) sind Warenzeichen oder eingetragene Warenzeichen von Blizzard Entertainment in den Vereinigten Staaten von Amerika und oder anderen Staaten. Davon abgesehen... Viel Spaß... PvPGN Projekt Seite: http://pvpgn.berlios.de/ pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/termsofservice.txt000066400000000000000000000032051462640461400223060ustar00rootroot00000000000000Sie sind zur Zeit mit einem PvPGN Server (Player -vs- Player Gaming Network) verbunden. PvPGN Allgemeine Geschaeftsbedingungen: PvPGN ist ein Battle.net(R) Emulationsserver und ist auf keine Weise verbunden mit bnetd, warforge, oder Blizzard Entertainment(R). PvPGN wird zur Verfügung gestellt "wie er ist" ohne Gewährleistungen jedweder Art. Wir unterstützen Softwarepiraterie nicht und halten Sie an, eine legale Kopie der Spiele zu erwerben, die Sie auf unserem Server spielen möchten. 11. Inhalte, die auf PvPGN gefunden werden. PvPGN unterstützt oder garantiert nicht für die Genauigkeit, den Wahrheitsgehalt oder die Zuverlässigkeit jedweder Informationen, darunter auch Meinungen und Ratschläge, die durch oder über PvPGN vermittelt werden. Jede und alle Aussagen, die in Foren, Chatverläufen oder in Spielen gemacht werden, geben nur die Meinung ihrer Urheber wieder. 2. Ausschluss von Gewaehrleistung. PvPGN wird gestellt "wie er ist" ohne Gewährleistungen irgendeiner Art. PvPGN gewährleistet nicht, dass ihr Programm ungestört und fehlerfrei läuft. 3. begrenzte Haftbarkeit PvPGN ist in keiner Weise haftbar für Verlust oder Schaden jedweder Art, der aus der Benutzung von PvPGN resultiert. Das schliesst ein, ist aber nicht begrenzt auf, Verlust von Geschäftswerten, Computerfehlern oder -Versagen und jedwede Art von Schaden, Verlust, sei er kommerziell oder privat. Battle.net(R) und Blizzard Entertainment(R) sind Warenzeichen oder eingetragene Warenzeichen von Blizzard Entertainment in den Vereinigten Staaten von Amerika und oder anderen Staaten. Davon abgesehen... Viel Spaß... PvPGN Projekt Seite: http://pvpgn.berlios.de/ pvpgn-1.99.7.2.1+dfsg/conf/i18n/deDE/w3motd.txt000066400000000000000000000004271462640461400204660ustar00rootroot00000000000000|c00FF0000Welcome |c00FF9933to |c0000CCFFthe |c00FF66FF%s |c0000CC00Version |c009999FF%v |c00FFFFFF Es sind zur Zeit %U Nutzer in %G Spielen von %N und %u Nutzer spielen %g Spiele and chatten in %c Kanälen. Dieser Text ist nicht sichtbar wegen der 11 Zeilen Begrenzung! pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/000077500000000000000000000000001462640461400165435ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/bnhelp.conf000066400000000000000000000465421462640461400206750ustar00rootroot00000000000000############################################################################## # bnhelp - Este Archivo de Ayuda se usa para describir comandos al usuario # #----------------------------------------------------------------------------# # Cada entrada está separada por un signo de porcentaje (%) al inicio # # de la línea. # # Los comandos que se corresponden con la entrada deberán estar seguidos de # # % en la mísma línea # # # # Se pueden insertar comentarios al empezar una línea con #. # # Los comentarios se extienden hasta el final de la línea. # # Las tabulaciones se reemplazan con 3 espacios. # # Las líneas vacias se ignoran. # # # ############################################################################## %whois whereis where -------------------------------------------------------- /whois (también se puede usar: /where /whereis) Muestra donde un se encuentra en el servidor. Ejemplo: /whois nomad %msg whisper m w -------------------------------------------------------- /whisper (también se puede usar: /w /m /msg) Envía un privado para Ejemplo: /whisper nomad ¿Qué tal todo? %ignore squelch -------------------------------------------------------- /squelch (también se puede usar: /ignore, Véase también: /unsquelch) Bloquea mensajes futuros enviados por . Ejemplo: /squelch nomad %unignore unsquelch -------------------------------------------------------- /unsquelch (también se puede usar: /unignore) Permite a previamente ignorado poder volver a hablar con normalidad. Ejemplo: /unsquelch nomad %away -------------------------------------------------------- /away [mensaje] Muestra [mensaje] a los usuarios que le susurren (whisper). Para desactivar, usar el comando /away de nuevo sin [mensaje]. Ejemplo: /away Estoy cenando. %dnd -------------------------------------------------------- /dnd [mensaje] Previene que se muestre cualquier susurro en su pantalla. Para desactivar, usar el comando /dnd de nuevo sin [mensaje]. Ejemplo: /dnd Estoy jugando y no quiero leer mensajes. %who -------------------------------------------------------- /who Muestra una lista de los usuarios en Ejemplo: /who Moderated Support %stats astat -------------------------------------------------------- /stats [jugador] [cliente] (también se puede usar: /astat) Muestra el record de Victorias/Derrotas de un [jugador]. Ejemplo: /stats nomad %status users -------------------------------------------------------- /users [SiglasJuego] (también se puede usar: /status) Muestra el actual número de usuarios conectados al servidor. Ejemplo: /users W3XP %time -------------------------------------------------------- /time Muestra su hora local y el servidor actual. %channel join j -------------------------------------------------------- /channel (también se puede usar: /join /j) Mueve al usuario a Ejemplo: /channel Moderated Support %rejoin -------------------------------------------------------- /rejoin (también se puede usar: /resign) Elimina su status como Operador del canal. %me emote -------------------------------------------------------- /me (también se puede usar: /emote) Muestra su nombre y el en diferente color. Ejemplo: /emote quiere jugar una partida. %kick -------------------------------------------------------- /kick Expulsa del canal. Ejemplo: /kick nomad %ban -------------------------------------------------------- /ban (véase también: /unban) Banea al del canal, previniendo que regrese. Ejemplo: /ban nomad %unban -------------------------------------------------------- /unban Permite a un baneado poder volver a entrar al canal. Example: /unban nomad %serverban -------------------------------------------------------- /serverban (véase también: /unban) Banea por su IP y bloquea la cuenta. Ejemplo: /serverban nomad %ipscan -------------------------------------------------------- /ipscan Busca a todos los usuarios actualmente conectados con el o dados. Ejemplo: /ipscan 127.0.0.1 Ejemplo: /ipscan nomad %whoami -------------------------------------------------------- /whoami Muestra donde se encuentra uno mismo en el servidor. %uptime -------------------------------------------------------- /uptime Muestra durante cuanto tiempo el servidor ha estado en marcha. %version -------------------------------------------------------- /version Muestra la versión del servidor. %lusers -------------------------------------------------------- /lusers Muestra una lista de los jugadores que han sido baneados del canal. %games -------------------------------------------------------- /games [SiglasJuego] [dificultad] Muestra una lista de los juegos online. Establece [dificultad] solamente para juegos en Diablo (norm|night|hell) -------------------------------------------------------- /games all Muestra una lista de todos los juegos. /games l[obby] Muestra una lista de los juegos en la sala de espera (lobby). %channels chs -------------------------------------------------------- /channels [SiglasJuego] (también se puede usar: /chs) Muestra una lista de los canales. -------------------------------------------------------- /channels all Muestra una lista de todos los canales. %connections con -------------------------------------------------------- /connections (también se puede usar: /con) Muestra una lista de las conexiones. %finger -------------------------------------------------------- /finger Muestra informaciónd detallada sobre Ejemplo: /finger nomad %admins -------------------------------------------------------- /admins Muestra una lista de todos los administradores actualmente conectados. %reply r -------------------------------------------------------- /reply (también se puede usar: /r) Contesta al último jugador que le haya susurrado con . Ejemplo:/r Hey, ¿como vá? %announce ann -------------------------------------------------------- /announce (también se puede usar: /ann) Anuncia un para todos. Ejemplo: /ann ¡Hola a todos! %realmann -------------------------------------------------------- /realmann Anuncia un para todos en el reino actual de Diablo 2. Ejemplo: /realmann ¡Hola a todos! %alert -------------------------------------------------------- /alert Muestra un cuadro de mensaje con para todos. Usar \n como símbolo para una nueva línea. Example: /alert ¡Hola\na todos! %news -------------------------------------------------------- /news Muestra las noticias. %logout quit exit -------------------------------------------------------- /logout (también se puede usar: /quit /exit) Desconecta del servidor. %kill -------------------------------------------------------- /kill {|#} [min] Desconecta a del servidor y banea la dirección IP del jugador durante [min] minutos. Ejemplo: /kill nomad 5 %killsession -------------------------------------------------------- /killsession [min] Desconecta la sesión del servidor y banea la dirección IP de la sesión durante [min] minutos. Ejemplo: /killsession 1 5 %watch -------------------------------------------------------- /watch Activa notificaciones para . Ejemplo: /watch nomad %unwatch -------------------------------------------------------- /unwatch Desactiva notificaciones para . Ejemplo: /unwatch nomad %watchall -------------------------------------------------------- /watchall [SiglasJuego] Activa notificaciones para todos. Ejemplo: /watchall SEXP %unwatchall -------------------------------------------------------- /unwatchall [SiglasJuego] Desactiva notificaciones para todos. Ejemplo: /unwatchall SEXP %gameinfo -------------------------------------------------------- /gameinfo Muestra información sobre . Ejemplo: /gameinfo FS 1v1!!! %ladderactivate -------------------------------------------------------- /ladderactivate Copia las estadísticas actuales de ladder a las estadísticas de ladder activas. %ladderinfo -------------------------------------------------------- /ladderinfo [SiglasJuego] Muestra información de ladder para en [SiglasJuego] Ejemplo: /ladderinfo 1000 SEXP %timer -------------------------------------------------------- /timer [mensaje] Muestra [mensaje] tras segundos. Example: /timer 60 ha pasado un minuto %netinfo -------------------------------------------------------- /netinfo [jugador] Muestra la información de red de [jugador]. Ejemplo: /netinfo nomad %addacct -------------------------------------------------------- /addacct Crea una nueva cuenta con el nombre con el . Ejemplo: /addacct nomad password %chpass -------------------------------------------------------- /chpass [] - cambia el password de un jugador Cambia el password de [] por . Si [] está vacio, entonces se cambia el password de su cuenta. Ejemplo: /chpass nomad password %quota -------------------------------------------------------- /quota Muestra su cuota de mensajes. %shutdown -------------------------------------------------------- /shutdown [duración] Establece la secuencia de apagado en [duración] segundos o la cancela si se introduce a cero. Ejemplo: /shutdown 300 %lock lockacct -------------------------------------------------------- /lock [horas] [razón] (también se puede usar: /lockacct) Bloquea la cuenta de previnendole de que se conecte con tal cuenta. Establecer [horas] = 0 para banear permanentemente. Ejemplo: /lock nomad 0 ¡bye noob! %unlock unlockacct -------------------------------------------------------- /unlock (también se puede usar: /unlockacct) Desbloquea la cuenta de permitiendo poder volver a conectarse con ella. Ejemplo: /unlock nomad %mute muteacct -------------------------------------------------------- /mute [horas] [razón] (también se puede usar: /muteacct) Silencia la cuenta de previniendole de que pueda hablar en los canales. Establecer [horas] = 0 para silenciar permanentemente. Ejemplo: /mute nomad 6 ¡Basta con el spam! %unmute unmuteacct -------------------------------------------------------- /unmute (también se puede usar: /unmuteacct) Devuelve la voz a la cuenta de permitiendole volver a hablar en los canales. Ejemplo: /unmute nomad %friends f -------------------------------------------------------- /friends [opciones] (también se puede usar: /f) -------------------------------------------------------- /friends add Añade a a su lista de amigos. /friends del Elimina a de su lista de amigos. /friends promote Asciende a una línea arriba en su lista de amigos. /friends demote Desciende a una línea abajo en su lista de amigos. /friends list Muestra su lista de amigos. /friends online Muestra la lista de sus amigos online. /friends msg Susurra a todos sus amigos online. %mail -------------------------------------------------------- /mail [opciones] -------------------------------------------------------- /mail s[end] Envia mail a con . /mail r[ead] [índice] Leer mail [índice] /mail del[ete] {all|} Deletes mail or [all] mail. %flag -------------------------------------------------------- /flag Una herramienta de depuración para iconos de estado. %tag -------------------------------------------------------- /tag Una herramienta de depuración para etiquetas de clientes. %help -------------------------------------------------------- /help [comando] Muestra ayuda sobre [comando] Ejemplo: /help whisper %ipban -------------------------------------------------------- /ipban [opción] [tiempo] -------------------------------------------------------- /ipban l[ist] Muestra una lista de las direcciones IP baneadas /ipban c[heck] Comprueba si la dirección IP ha sido baneada o no. /ipban d[el] Elimina la dirección IP o el <índice> /ipban a[dd] [time] Banea la dirección IP durante [tiempo] minutos. [tiempo] = 0 - ban permanente %set -------------------------------------------------------- /set [valor] Establece o retorna el valor de para la cuenta . Establece [valor] = nulo para eliminar valor. Ejemplo: /set nomad BNET\auth\botlogin true Ejemplo: /set nomad Record\SEXP\0_wins 999 %motd -------------------------------------------------------- /motd Muestra el MOTD (Mensaje del Dia). %tos -------------------------------------------------------- /tos /warranty /license Muestra los Términos de Servicio. %admin -------------------------------------------------------- /admin [+|-] Asciende/Degrada a a ser /no ser administrador del servidor. Ejemplo: /admin +nomad %rehash -------------------------------------------------------- /rehash Fuerza al servidor a recargar una config específica. Establecer "all" para recargar todas las configs. = all | i18n | channels | realms | autoupdate | news | versioncheck | ipbans | helpfile | banners | tracker | commandgroups | aliasfile | transfile | tournament | icons | anongame | topiclist | lua %clan -------------------------------------------------------- /clan [opción] -------------------------------------------------------- /clan create Crea un nuevo clan (longitud max = 4; no se permiten espacios en ) Commands for clan members: /clan m[sg] (También se puede usar [w]hisper) Susurra un mensaje a todos los compañeros del clan /clan inv[ite] Invita a a su clan /clan inv[ite] get Muestra el nombre del clan al cual ha sido invitado /clan inv[ite] accept Acepta invitación al clan /clan inv[ite] decline Declina invitación al clan Comandos para jefe del clan: /clan motd Actualiza el Mensaje del Dia del clan a . /clan pub[lic] (también se puede usar: pub) Abre el canal del clan al público para que cualquiera pueda entrar. /clan priv[ate] (también se puede usar: priv) Cierra el canal al público de manera que solamente miembros del clan pueden entrar. /clan dis[band] Disuelve su clan. %ping p latency -------------------------------------------------------- /ping (también se puede usar: /p /latency) Muestra su ping en el servidor. %command_groups cg -------------------------------------------------------- /command_groups [grupos] (también se puede usar: /cg) -------------------------------------------------------- /cg list Muestra los grupos de comandos de . /cg add Añade grupos de comandos al . /cg del Elimina grupos de comandos del . %operator -------------------------------------------------------- /operator [+|-] Asciende/degrada a a ser/no ser operador del servidor. Ejemplo: /operator +nomad %aop -------------------------------------------------------- /aop Asciende a a ser administrador del canal. Ejemplo: /aop nomad %op -------------------------------------------------------- /op Asciende a a ser operador del canal. Ejemplo: /op nomad %deop -------------------------------------------------------- /deop Degrada a a no ser operador o administrador del canal. Ejemplo: /deop nomad %tmpop -------------------------------------------------------- /tmpop Asciende a operador del canal temporal. Ejemplo: /tmpop %vop -------------------------------------------------------- /vop Añade a a la lista VOP. Ejemplo: /vop nomad %voice -------------------------------------------------------- /voice Se dan privilegios de voz temporales a . Ejemplo: /voice nomad %devoice -------------------------------------------------------- /devoice Elimina a de la lista VOP y elimina los privilegios de voz temporales. Ejemplo: /devoice nomad %topic -------------------------------------------------------- /topic [mensaje] Establece o muestra el tema del canal actual. Ejemplo: /topic En este canal se ofrece ayuda...\n(texto en una nueva línea) %moderate -------------------------------------------------------- /moderate Activa/desactiva el estado de la moderación del canal actual. %clearstats -------------------------------------------------------- /clearstats Resetea las estadísitcas del para , donde puede ser cualquier cliente válido o ALL para todos. Clientes soportados: DRTL|DSHR|W2BN|STAR|SEXP|SSHR|WAR3|W3XP Ejemplo: /clearstats nomad SEXP %find -------------------------------------------------------- /find Encuentra usuarios con en su nombre de usuario. ¡ DEBE estar en minúsculas! %save -------------------------------------------------------- /save Fuerza al servidor a guardar todos los cambios en cuentas y clanes a la base de datos. %icon -------------------------------------------------------- /icon [nombre] Establece un icono personalizado para un usuario normal. Usar /icon sin [nombre] para mostrar una lista de los iconos disponibles en su reserva. -------------------------------------------------------- Sintaxis para operador/admin: /icon a[dd] Añadir icono a la reserva del usuario /icon d[el] Elimina icono de la reserva del usuario /icon s[et] Establece el icono para el usuario sin añadirlo en su reserva /icon l[ist] Muestra lista de los iconos en la reserva del usuario /icon l[ist] Muestra los iconos disponibles en la reserva del servidor para asginar a los usuarios %language /lang ------------------------------------------------------- /lang [código] Establece su idioma para obtener otra traducción: %quiz -------------------------------------------------------- /quiz [opción] Trivia Quiz Game -------------------------------------------------------- /quiz start Comienza un juego con el nombre de diccionario introducido en el canal actual /quiz stop Finaliza el juego por la fuerza /quiz stats [nombreusuario] Muestra los records estadísticas del jugador /quiz stats Muestra los Top Records pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/bnmotd.txt000066400000000000000000000006471462640461400205760ustar00rootroot00000000000000%EHola %l, bienvenido a %s! %IEn %h se usa la versión %v %IEste servidor está alojado por %H %I %IEn este servidor hay %a cuentas registradas. %IActualmente hay %U usuarios en %G partidas de %N, %Iy %u usuarios jugando %g partidas y conversando en %c salas. %I¿Tiene problemas con algun comando? Teclee "/help" para %Iaprender más sobre los comandos. %IEscribir /language para cambiar el idioma de traducción. %I %M%m pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/chathelp-war3.txt000066400000000000000000000007431462640461400217520ustar00rootroot00000000000000Comandos de Ayuda para el Chat de la Emulación de Bnet PvPGN Nuevos Comandos: usar /f help para ayuda sobre la lista de amigos usar /users para ver usuarios online usar /w o /msg para sususrrar a gente usar /join para unirse a un canal Para Operadores de Canal: usar /ban prohibe permanentenmente al usuario usar el canal usar /kick expulsa al usuario del canal Para una ayuda más detallada escribir: /help pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/common.xml000066400000000000000000003165421462640461400205700ustar00rootroot00000000000000 Spanish ESP ARG BOL CHL COL CRI DOM ECU SLV GTM HND MEX NIC PAN PRY PER PRI URY VEN by {} por {} for {} para {} permanently permanentemente with a reason "{}" con una razón "{}" No such alias. Use // to show the list. No existe tal alias. Usar // para mostrar la lista. Alias list: Lista de alias: you are now tempOP for this channel ahora eres OPtemp para este canal You can't talk on the channel. Your account has been muted No puede hablar en el canal. Su cuenta ha sido silenciada This channel is moderated Este canal está moderado. No one hears you. Nadie le oye. Account name contains some invalid symbol! ¡El nombre de cuenta contiene algun carácter inválido! Maximum password length allowed is {} La longitud máxima permitida para la contraseña es de {} Trying to add account "{}" with password "{}" Intentando añadir cuenta "{}" con contraseña "{}" Hash is: {} La etiqueta hash es: {} Failed to create account! ¡No ha sido posible crear la cuenta! Account {} created. Cuenta {} creada. You must supply a username. Se debe proporcionar un nombre de usuario. There's no account with username {}. No existe ninguna cuenta con el nombre de usuario {}. {} is already a Server Admin {} ya es Admin del Servidor {} has been promoted to a Server Admin {} ha sido ascendido a Admin del Servidor {} has promoted you to a Server Admin {} le ha ascendido a Admin del Servidor {} is not a Server Admin. {} no es un Admin del Servidor {} has been demoted from a Server Admin {} ha sido degradado a no ser Admin del Servidor {} has demoted you from a Server Admin {} le ha degradado a no ser Admin del Servidor Currently logged on Administrators: Actualmente conectado en Administradores: Your game client doesn't support MessageBox. El cliente no es compatible con cuadros de mensaje. \n\n***************************\nBy {} \n\n***************************\nBy {} Information from {} Información de {} for {} Announcement from {}: {} Notificación de {}: {} Could not broadcast message. No se ha podido transmitir el mensaje. This command can only be used inside a channel. Este comando solamente se puede usar dentro de un canal. You must be at least a Channel Admin to use this command. Se debe ser como mínimo Admin del Canal para poder usar este comando. There's no account with username {}. {} is already a Channel Admin {} ya es Administrador del Canal {} has been promoted to a Channel Admin {} ha sido ascendido a Administrador del Canal {} has promoted you to a Channel Admin for channel "{}" {} le ha ascendido a Administrador del Canal "{}" You are now marked as being away. Su estado ha sido marcado como ausente. You are no longer marked as away. Su estado ya no es ausente. This command can only be used inside a channel. You have to be at least a Channel Operator to use this command. Se debe ser como mínimo Operador de Canal para poder usar este comando. That account doesn't currently exist. Esta cuenta no existe actualmente. You cannot ban administrators. No se puede banear a los administradores. Audible notification on. Notificacíon acústica activada. Channel Arranged Teams is a RESTRICTED Channel! ¡El Canal Arranged Teams es un Canal RESTRINGIDO! Max channel name length exceeded (max {} symbols) Longitud máxima para el nombre del canal sobrepasada (máximo de {} carácteres) Command disabled while inside a game. Comando desactivado durante partidas. Currently accessible channels: Canales actualmente accesibles: All current channels: Todos los canales actuales: Current channels of type {} Canales de tipo {} actuales -----------name----------- users ----admin/operator---- -----------nombre----------- usuarios ----admin/operador---- Only admins may change passwords for other accounts. Sólo los administradores pueden cambiar la contraseña para otras cuentas. Account does not exist. La cuenta no existe. Maximum password length allowed is {} Trying to change password for account "{}" to "{}" Intentando cambiar contraseña para la cuenta "{}" por "{}" Unable to set password. Incapaz de establecer contraseña. Message was sent to all currently available clan members. El mensaje ha sido enviado a todos los miembros del clan actualmente disponibles. All fellow members of your clan are currently offline. Todos los miembros del clan se encuentran actualmente offline. Clan channel is opened up! ¡El canal del clan acaba de abrirse! Clan channel has already been opened up! ¡El canal del clan ya está abierto! Clan channel is closed! ¡El canal del clan se ha cerrado! Clan channel has already been closed! ¡El canal del clan ya está cerrado! Clan message of day is updated! ¡Los mensajes del dia del clan han sido actualizados! User {} was invited to your clan! ¡El usuario {} ha sido invitado al clan! You are invited to {} by {}! ¡Ha sido invitado a {} por {}! User {} is not online or is already member of clan! ¡El usuario {} no está online o ya es un miembro del clan! This is one-way action! If you really want ¡Esta acción no tiene marcha atrás! Si de verdad se desea to disband your clan, type /clan disband yes disolver el clan, escribir /clan disband yes Your clan was disbanded. El clan ha sido disuelto. You have been invited to {} Has sido invitado a {} You are now a clan member of {} Ahora eres un miembro del clan {} Clan {} was be created El clan {} ha sido creado You are no longer ivited to {} La invitación a {} ha expirado You are already in clan "{}" Ya se es miembro del clan "{}" Clan with your specified &lt;clantag&gt; already exist! ¡El clan con el &lt;clantag&gt; especificado ya existe! Please choice another one. Por favor, escoger otro distinto. Clan {} is created! El clan {} ha sido creado! Clan {} is pre-created, please invite El clan {} está pre-creado, por favor invitar at least {} players to your clan by using como mínimo {} jugadores a su clan usando /clan invite &lt;username&gt; command. el comando /clan invite &lt;username&gt; Invalid user. Invalid user. {}'s command group(s): {} Grupo(s) de comandos de {}: {} Got bad group: {} Grupo erróneo: {} Groups {} has been added to {} Se ha añadido los grupos {} a {} Groups {} has been deleted from {} Se han eliminado los grupos {} de {} Got unknown command: {} Comando introducido desconocido: {} This command is only enabled for admins. Este comando está solo activo para admins. Current connections: Conexiones actuales: -class -tag -----name------ -lat(ms)- ----channel---- --game-- -clase -etiqueta -----nombre------ -lat(ms)- ----canal---- --juego-- -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game-- -#- -clase ----estado--- -etiqueta-----nombre------ -sesión-- -condición- -lat(ms)- ----canal---- --juego-- -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game-- ---------addr-------- -#- -clase ----estado--- -etiqueta-----nombre------ -sesión-- -condición- -lat(ms)- ----canal---- --juego-- ---------dirección-------- Unknown option. Opción desconocida. (none) (Ninguno/a) none ninguno/a This command can only be used inside a channel. You must be at least a Channel Operator or tempOP to use this command. There's no account with username {}. You must be at least a Channel Admin to demote another Channel Admin Se debe ser Administrador del Canal como mínimo para degradar a otro Administrador del Canal {} has been demoted from a Channel Admin. {} ha sido degrado a no ser Administrador del Canal {} has demoted you from a Channel Admin of channel "{}" {} le ha degradado a no ser Administrador del Canal "{}" {} has been demoted from a Channel Operator {} ha sido degrado a no ser Operador del Canal {} has demoted you from a Channel Operator of channel "{}" {} le ha degradado a no ser Operador del Canal "{}" {} has been demoted from a tempOP of this channel {} ha sido degradado a no ser tempOP de este canal {} has demoted you from a tmpOP of channel "{}" {} le ha degradado a no ser tempOP del canal "{}" {} is no Channel Admin or Channel Operator or tempOP, so you can't demote him. {} no es ni Administrador ni Operador del Canala ni tempOP, así que no se le puede degradar. {} has demoted you from a tempOP of channel "{}" {} is no tempOP in this channel, so you can't demote him {} no es OPtemp en este canal, así que no se le puede degradar This command can only be used inside a channel. You must be at least a Channel Operator to use this command. There's no account with username {}. {} has been removed from VOP list. {} ha sido eliminado de la lista VOP. {} has removed you from VOP list of channel "{}" {} le ha eliminado de la lista VOP del canal "{}" You must be at least Channel Admin to remove {} from the VOP list Se debe ser como mínimo Administrador del canal para eliminar {} de la lista VOP Voice has been taken from {} in this channel La voz de {} ha sido denegada en este canal {} has taken your Voice in channel "{}" {} le ha denegado la voz en el canal "{}" {} has no Voice in this channel, so it can't be taken away {} tiene la Voz denegada en este canal, por lo tanto ya no se le puede denegar Do Not Disturb mode engaged. Modo No Molestar encendido. Not available No disponible Do Not Disturb mode canceled. Modo No Molestar cancleado. -- name -- similar to {} -- nombre -- similar a {} Invalid user. Login: {} {} Sex: {} Conexión: {} {} Sexo: {} Created: {} Creado: {} Clan: {} Clan: {} Rank: Chieftain Rango: Caudillo Rank: Shaman Rango: Chamán Rank: Grunt Rango: Bruto Rank: Peon Rango: Peón Location: {} Age: {} Localidad: {} Edad: {} Client: {} Ver: {} Country: {} Cliente: {} Ver: {} País: {} unknown desconocido/a Last login {} from Última conexión {} de On since {} from El dia desde {} de Operator: {}, Admin: {}, Locked: {}, Muted: {} Operador: {}, Admin: {}, Bloqueado: {}, Silenciado: {} Yes Sí Email: {} Email: {} Last login Owner: {} Última conexión del Dueño: {} Idle {} Parado {} Flags set to {}. Condiciones establecidas como {} That user does not exist. Este usuario no existe. Server error. Error del servidor. You can't add yourself to your friends list. No se puede añadir uno mismo a la lista de amgios. You can only have a maximum of {} friends. Solamente se pueden tener {} amigos como máximo. {} is already on your friends list! ¡{} ya está en su lista de amigos! Added {} to your friends list. {} ha sido añadido a la lista de amigos. {} added you to his/her friends list. {} le ha añadido en su lista de amigos. All of your friends are offline. Todos tus amigos están offline. {} was not found on your friends list. No se ha encontrado a {} en la lista de amigos. Removed {} from your friends list. Eliminado {} de la lista de amigos. Promoted {} in your friends list. Ascendido {} en la lista de amigos. Demoted {} in your friends list. Descendido {} en la lista de amigos. Your {} - Friends List Su {} - Lista de Amigos Your {} - Online Friends List Su {} - Lista de Amigos Conectados , offline , desconectado/a using {} usando {} , in game "{}" , dentro del juego "{}" , in game AT Preparation , dentro del juego en , in channel "{}", , en el canal "{}" , is in AT Preparation , está en la Antesala AT , is in a game , está en una partida , is in a chat channel , está en un canal de chat End of Friends List Final de la Lista de Amigos You are not in a game. No se está en una partida. That game does not exist. Ese juego no existe. Name: {} ID: {} ({}) Nombre: {} ID: {} ({}) none unknown Owner: {} Propietario: {} Address: {} Dirección {} Address: {} (trans {}) Dirección: {} (trans {}) Client: {} (version {}, startver {}) Cliente: {} (versión {}, startver {}) Created: {} Started: {} Iniciado: {} Status: {} Estado: {} Type: {} Tipo: {} Speed: {} Velocidad: {} Difficulty: {} Dificultad: {} Option: {} Opciones: {} Map: {} Mapa: {} Map Size: {}x{} Tamaño del Mapa {}x{} Map Tileset: {} Ambientación del Mapa:{} Map Type: {} Tipo de Mapa: {} Players: {} current, {} total, {} max Jugadores: {} actuales, {} total, {} max Description: {} Descripción: {} public público private privado Currently accessible games: Juegos actualmente accesibles: All current games: Todos los juegos actuales: Games in lobby: Juegos en lobby: Current games of type {} Juegos actuales de tipo {} Current games of type {} {} Juegos actuales de tipo {} {} ------name------ p -status- --------type--------- count ------nombre------ p -estado- --------tipo--------- cuenta ctag ctag --------addr-------- --------direc-------- Warning: That user is not online, using last known address. Atención: El usuario no está online, usando la última dirección conocida. Sorry, no IP address could be retrieved. Lo siento, pero no se ha podido recuperar ninguna dirección IP. Scanning online users for IP {}... Escaneando usuarios online para IP {}... There are no online users with that IP address No hay usuarios conectados con esa dirección IP This command can only be used inside a channel. You have to be at least a Channel Operator or tempOP to use this command. Se debe ser como mínimo Operador del Canal o OPtemp para usar este comando. That user is not logged in. Ese usuario no está conectado. That user is not in this channel. Ese usuario no está en este canal. That connection doesn't exist. Esa conexión no existe. That user is not logged in? Ese usuario no está conectado. An admin has closed your connection and banned your IP address. Un Admmin ha cerrado su conexión y baneado su dirección IP. Connection closed by admin. Conexión cerrada por admin. Operation successful. Operación con éxito. That is not a valid session. Esa no es una sesión válida. That session does not exist. Esa sesión no existe. Connection closed by admin and banned your IP's. Conexión cerrada por admin, su dirección IP ha sido baneada. Connection closed by admin. Copied current scores to active scores on all ladders. Se han copiado las puntuaciones actuales a las puntuaciones activas en todas las clasificaciones. This game does not support win/loss records. You must supply a rank and a valid program ID. Se debe proporcionar un rango y una ID de programa válidos. Example: /ladderinfo 1 STAR Ejemplo: /ladderinfo 1 STAR Unable to determine client game. Invalid user. Account {} is now locked La cuenta {} ha sido bloqueada Your account has just been locked Su cuenta acaba de ser bloqueada This command can only be used inside a channel. Banned users: Usuarios baneados: You are not in a channel. No se encuentra en un canal. This command can only be used inside a channel. You must be at least a Channel Operator to use this command. Unable to change channel flags. No ha sido posible cambiar las etiquetas del canal Channel is now unmoderated. El canal ahora está sin moderación. Channel is now moderated. El canal ahora está moderado. Unable to open motd. No es posible abrir motd (mensaje del dia) No motd. No hay motd. Invalid user. Account {} is now muted La cuenta {} ahora está silenciada. Your account has just been muted Su cuenta acaba de ser silenciada That user is not logged on. Address information for other users is only available to admins. La información sobre las direcciones de otros usuarios solo esta disponible para los administradores. Server TCP: {} (bind {}) TCP del Servidor: {} (enlazar {}) Client TCP: {} TCP del Cliente: {} Client UDP: {} UDP del Cliente: {} Client UDP: {} (trans {}) UDP del Cliente: {} (trans{}) Game UDP: {} UDP del Juego: {} Game UDP: {} (trans {}) UDP del Juego {} (trans {}) Game UDP: none UDP del Juego: No hay Audible notification off. Notificación acústica desactivada. This command can only be used inside a channel. You must be at least a Channel Operator or tempOP to use this command. Se debe ser como mínimo Operador del Canal o OPtemp para usar este comando. There's no account with username {}. {} is already a Channel Operator {} ya es un Operador del Canal. {} has been promoted to a Channel Operator {} ha sido ascendido a Operador del Canal {} has promoted you to a Channel Operator in channel "{}" {} le ha ascendido a Operador del Canal "{}" {} must be on the same channel to tempOP him {} debe estar en el mismo canal para darle el privilegio OPtemp {} already is operator or admin, no need to tempOP him {} ya es operador o administrador, no es necesario darle el privilegio de OPtemp {} has been promoted to a tempOP {} ha sido ascendido a OPtemp {} has promoted you to a tempOP in this channel {} le ha ascendido a ser OPtemp en este canal You must supply a username. There's no account with username {}. {} is already a Server Operator {} ya es un Operador del Servidor {} has been promoted to a Server Operator {} ha sido ascendido a Operador del Servidor {} has promoted you to a Server Operator {} le ha ascendido a Operador del Servidor {} is no Server Operator, so you can't demote him {} no es Operador del Servidor, así que no se le puede degradar {} has been demoted from a Server Operator {} ha sido degradado a no ser Operador del Servidor {} latency: {} {} latencia: {} Your latency {} Su latencia {} {} latency () {} latencia () Invalid user. Connection closed. Conexión cerrada. Your quota allows you to write {} line(s) per {} second(s). Su cuota le permite escribir {} línea(s) por {} segundo(s). Long lines will be wrapped every {} characters. Las líneas demasiado extensas serán comprimidas cada {} carácteres. You are not allowed to send lines with more than {} characters. No le está permitido enviar líneas con más de {} carácteres. You must join a realm first Primero se debe unir a un reino Announcement from {}@{}: {} Notificación de {}@{}: {} Invalid mode. Modo no válido. Rehash of "{}" is complete! ¡Restablecimiento de etiqueta hash de "{}" completado! You are not in a channel. No one messaged you, use /m instead Nadie le ha enviado ningún mensaje, en ese caso usar /m Pending changes has been saved into the database. Los cambios pendientes han sido guardados en la base de datos. That user is not logged on. Banning {} who is using IP address {} Baneando {} quién está usando la dirección IP {} User's account is also LOCKED! Only an admin can unlock it! ¡La cuenta del usuario también está BLOQUEADA! ¡Solamente un administrador puede desbloquearla! /ipban a {} /ipban a {} You have been banned by Admin: {} Ha sido baneado por el Administrador: {} Your account is also LOCKED! Only an admin can UNLOCK it! ¡Su cuenta también está BLOQUEADA! ¡Solamente un administrador puede desbloquearla! Access denied due to security reasons. Acceso denegado debido a razones de seguridad. Invalid user. Current value of {} is "{}" El valor actual de {} es "{}" Value currently not set Valor actual no fijado Unable to set key for No es posible establecer tecla para Key set successfully for Tecla establecida con éxito para Invalid delay. Retraso no válido. You've initialized the shutdown sequence. Se ha iniciado la secuencia de cierre. You've canceled the shutdown sequence. Se ha cancelado la secuencia de cierre. No such user. No existe tal usuario. You can't squelch yourself. No se puede ignorar a uno mismo. Could not squelch user. No se ha podido ignorar al usuario. {} has been squelched. {} esta siendo ignorado. Invalid user. Usuario no válido. Unable to determine client game. No es posible determinar el cliente del juego. This game does not support win/loss records. Este juego no soporta récords de victoria/derrota. You must supply a user name and a valid program ID. Se debe subministrar un nombre de usuario y una ID de programa válidos. Example: /stats joe STAR Ejemplo: /stats joe STAR {}'s record: Récord de {} level: {} nivel: {} class: {} clase: {} stats: {} str {} mag {} dex {} vit {} gld estaditicas: {} str {} mag {} dex {} vit {} gld Diablo kills: {} Muertes de Diablo: {} Normal games: {}-{}-{} Juegos en normal: {}-{}-{} Ladder games: {}-{}-{} (valuación {}) Juegos en ladder: {}-{}-{} (valuación {}) Ladder games: 0-0-0 Juegos en ladder: 0-0-0 IronMan games: {}-{}-{} (rating {}) Juegos IronMan: {}-{}-{} (puntuación {}) IronMan games: 0-0-0 Juegos IronMan: 0-0-0 {}'s Ladder Record's: Récord en ladder de {} Users Solo Level: {}, Experience: {} Nivel de los usuarios en Solitario : {}, Experiencia: {} SOLO Ladder Record: {}-{}-0 Récord en Ladder en Solitario: {}-{}-0 SOLO Rank: {} Rango en Solitario: {} Users Team Level: {}, Experience: {} Nivel de los usuarios en Equipos: {}. Experiencia: {} TEAM Ladder Record: {}-{}-0 Récord en Ladder en Equipos: {}-{}-0 TEAM Rank: {} Rango en Equipos: {} Users FFA Level: {}, Experience: {} Nivel de los usuarios en TCT (Todos Contra Todos): {}, Experiencia: {} FFA Ladder Record: {}-{}-0 Récord en Ladder para TCT (Todos Contra Todos): {}-{}-0 FFA Rank: {} Rango TCT (Todos Contra Todos): {} Users AT Team No. {} Usuarios en Equipo AT No. {} Users AT TEAM Level: {}, Experience: {} Usuarios Equipo AT Nivel: {}, Experiencia: {} AT TEAM Ladder Record: {}-{}-0 Récord en Ladder Equipo AT: {}-{}-0 AT TEAM Rank: {} Equipo AT Rango: {} Ladder games: {}-{}-{} (rating {}) Juegos en ladder: {}-{}-{} (puntuación {}) There are currently {} user(s) in {} games of {} Actualmente hay {} usuario(s) en {} juegos de {} There are currently {} users online, in {} games, and in {} channels. Actualmente hay {} usuarios conectados, en {} juegos, y en {} canales. Client tag set to {}. Etiqueta del cliente establecida en {} Invalid clienttag {} specified Etiqueta del cliente especificada {} no válida Server Time: {} Hora del Servidor: {} Your local time: {} Hora local: {} Invalid duration. Duración no válida Your timer has expired. El temporizador ha expirado. Could not set timer. No se ha podido establecer el temporizador. Timer set for {} second(s) Temporizador establecido para {} segundo(s) This command can only be used inside a channel. You must be at least a Channel Operator or tmpOP to use this command. Se debe ser como mínimo Operador de Canal o OPtemp para usar este comando. There's no account with username {}. {} has already tmpOP in this channel {} ya es OPtemp en este canal {} must be on the same channel to tempOP him {} already is operator or admin, no need to tempOP him {} has been promoted to tmpOP in this channel {} ha sido ascendido a OPtemp en este canal {} has promoted you to a tempOP in this channel This command can only be used inside a channel. Max topic length exceeded (max {} symbols) Longitud máxima del hilo excedida ( max {} cáracteres) You must be at least a Channel Operator of {} to set the topic Se debe ser como mínimo Operador del Canal {} para establecer el hilo {} topic: no topic {} hilo: sin hilo Unable to send TOS (Terms of Service). No ha sido posible enviar los TDS (Términos del Servicio) This command can only be used inside a channel. You are not a channel operator. No se es un operador de canal. That user is not banned. Ese usuario no está baneado. {} is no longer banned from this channel. {} ya no está baneado en este canal. Invalid user. Your account has just been unlocked by {} Su cuenta acaba de ser desbloqueada por {} That user's account is now unlocked. La cuenta de ese usuario ahora está desbloqueada. Invalid user. Your account has just been unmuted by {} Acaban de devolverle la voz a su cuenta por {} That user's account is now unmuted. Esa cuenta de usuario vuelve a tener voz. No such user. User was not being ignored. El usuario no estaba siendo ignorado. No longer ignoring. Ya no se le ignora más. That user does not exist. Removal from watch list failed. Eliminación fallida de la lista de vigilancia. User {} removed from your watch list. Usuario {} eliminado de la lista de vigilancia. Removal from watch list failed. All {} users removed from your watch list. Todos los {} usuarios han sido eliminados de la lista de vigilancia. All users removed from your watch list. Todos los usuarios han sido eliminados de la lista de vigilancia. Uptime: {} Tiempo de actividad: {} This command can only be used inside a channel. You must be at least a Channel Operator to use this command. Se debe ser como mínimo Operador del Canal para poder usar este comando. There's no account with username {}. {} is already on VOP list, no need to Voice him {} ya se encuentra en la lista VOP, no es necesario darle Voz. {} must be on the same channel to voice him {} debe estar en el mismo canal para darle voz {} already has Voice in this channel {} ya tiene Voz en en este canal {} is already an operator or admin. {} ya es un operador o admin. {} has been granted Voice in this channel Se le ha concedido la Voz a {} en este canal {} has granted you Voice in this channel {} le ha concedido la Voz en este canal This command can only be used inside a channel. You must be at least a Channel Admin to use this command. There's no account with username {}. {} is already on VOP list {} ya se encuentra en la lista VOP {} has been added to the VOP list Se ha añadido a {} a la lista VOP {} has added you to the VOP list of channel "{}" {} le ha añadido a la lista VOP del canal "{}" That user does not exist. Add to watch list failed. Inclusión fallida a la lista de vigilancia. User {} added to your watch list. Usuario {} incluido en la lista de vigilancia. Add to watch list failed. All {} users added to your watch list. Todos los {} usuarios han sido incluidos a la lista de vigilancia. All users added to your watch list. Todos los usuarios han sido incluidos a la lista de vigilancia. That channel does not exist. Ese canal no existe. (If you are trying to search for a user, use the /whois command.) (Si se está intentando buscar a un usuario, usar el comando /whois) You are banned from that channel. Está expulsado de ese canal. Users in channel {}: Usuarios en el canal {}: Unable to obtain your account name. No es posible obtener su nombre de cuenta. (invalid date) (Fecha no válida) Reset {}'s {} stats Reiniciar los stats de {} en {} Reset {}'s {} stats Reset {}'s {} stats Created: {} Clan: {} Rank: Chieftain Rank: Shaman Rank: Grunt Rank: Peon Client: {} Ver: {} Country: {} unknown Last login {} from On since {} from Yes No No Operator: {}, Admin: {}, Locked: {}, Muted: {} Email: {} Last login Owner: {} Idle {} You cannot kick administrators. No se puede echar a administradores. You cannot kick operators. No se puede echar a operadores. {} has been kicked by {} ({}). {} ha sido echado por {} ({}). {} has been kicked by {}. {} ha sido echado por {}. You cannot ban operators. No se puede banear a operadores. Unable to ban {}. No es posible banear a {}. {} has been banned by {} ({}). {} ha sido baneado por {} ({}). {} has been banned by {}. {} ha sido baneado por {}. Password for account {} updated. Contraseña para la cuenta {} actualizada. Hash is: {} Your account has been muted, you can't whisper to other users. Su cuenta ha sido silenciada, no se puede susurrar a otros usuarios. That user is not logged on. Ese usuario no está conectado. {} is unavailable ({}) {} no está disponible ({}) {} is away ({}) {} está ausente ({}) Unknown user. Usuario desconocido. User was last seen on: {} Usuario última vez visto el: {} User is offline El usuario está desconectado You Usted are está is está {} {} using {} and {} currently in {} game "{}". {} {} usando {} y {} actualmente dentro de {} juego "{}". {} {} using {} and {} currently in channel "{}". {} {} usando {} y {} actualmente dentro del canal "{}". {} {} using {}. {} {} usando {}. {} {} refusing messages ({}) {} {} denegando mensajes ({}) {} away ({}) {} ausente ({}) You are sending commands to {} too quickly and risk being disconnected for flooding. Please slow down. Se están mandando comandos a {} demasiado rápido con el riesgo a ser desconectado por sobrecarga. Por favor introducir comandos más depspacio. This command has been deactivated Este comando ha sido desactivado This command is reserved for admins. Este comando está reservado para admins. Unknown command. Comando desconocido. Your line length quota has been exceeded! ¡Su cuota de longitud de línea ha sido sobrepasada! Your message quota has been exceeded! ¡Su cuota de mensajes ha sido sobrepasada! This is a private clan channel, unable to join! ¡No es posible unirse, este es un canal de clan privado! You are banned from that channel. That channel is for Admins/Operators only. Este canal es solamente para Administradores/Operadores. The channel is currently full. Este canal está actualmente lleno. This channel does not have chat privileges. Este canal no tiene privilegios de chat. This channel is moderated. Este canal está moderado. Created ladder game Ha creado un juego en ladder Joined ladder game Se ha unido a un juego en ladder This account has been locked Esta cuenta ha sido bloqueada This account has been locked No help available for that command No hay ayuda disponible para ese comando Oops ! There is a problem with the help file. Please contact the administrator of the server. ¡Ups! Hay un problema con el archivo de ayuda. Por favor contactar con el administrador del servidor. Chat commands : Comandos de chat : Bad language code. Código de idioma incorrecto Set your language to {} Estabelcer su idioma en {} This command can only be used inside a channel. This command can only be used from the game. Este comando tan solo se puede usar dentro del juego. Bad icon. Icono incorrecto. Set default icon. Establecer icono por defecto. Set new icon is succeed. Nuevo icono establecido con éxito. You have {} icons in stash: Tiene {} iconos en su reserva: Available icons in server stash: Iconos disponibles en la reserva del servidor: Invalid user. Set default icon for {} Establecer icono por defecto para {} That icon doesn't exist in server stash. Ese icono no existe en la reserva del servidor. Set new icon is succeed for {} Nuevo icono establecido con éxito para {} {} has {} icons in stash : {} tiene {} iconos en su reserva : User already has that icon in stash. El usuario tiene ese icono en su reserva. Add new icon to {}'s stash. Añadir nuevo icono a la reserva de {}. That icon doesn't exist in user stash. Ese icono no existe en la reserva del usuario. Delete icon from {}'s stash. Eliminar icono de la reserva de {} IP not banned. La IP no está baneada. Error occured. Ha ocurrido un error. IP banned by rule #{}. IP baneado debido a la regla #{}. Illegal IP entry. IP introducida ilegal. No matching entry. No se encuentran entradas que coincidan. Wrong entry number. Número de entrada incorrecto. Entry deleted. Entrada eliminada. Banned IPs: IPs baneadas: (perm) (perm) none {} banned permamently by {}. {} baneado permanentemente por {} {} banned permamently. {} baneado permanentemente. {} banned for {} by {}. {} baneado por {} por {} {} banned for {}. {} baneado por {}. Bad IP. IP errónea. Banning only for: {} minutes. Baneando tan solo por: {} minutos. This server has NO mail support. Este servidor NO soporta correos. Please specify which message to delete. Use the following syntax: /mail delete {&lt;index&gt;|all} . Por favor especificar cual mensaje se va a eliminar. Utilizar la siguiente sintaxis: /mail delete {&lt;index&gt;|all} . Successfully deleted messages. Mensajes eliminados satisfactoriamente. Invalid index. Please use /mail delete {&lt;index&gt;|all} where &lt;index&gt; is a number. Ã�ndice no válido. Por favor usar /mail delete {&lt;index&gt;|all} donde &lt;index&gt; es un número. Succesfully deleted message. Mensaje eliminado satisfactoriamente. You have no mail. No hay correo. There was an error completing your request. Ha ocurrido un error mientras se completaba su petición. ID Sender Date ID Remitente Fecha Use /mail read &lt;ID&gt; to read the content of any message Usar /mail read &lt;ID&gt; para leer el contenido de cualquier mensaje Invalid index. Please use /mail read &lt;index&gt; where &lt;index&gt; is a number. Ã�ndice no válido. Por favor usar /mail read &lt;index&gt; donde &lt;index&gt; es un número. Receiver UNKNOWN! ¡Destinatario DESCONOCIDO! Receiver has reached his mail quota. Your message will NOT be sent. El destinatario ha alcanzado su cuota de correos. El mensaje NO será enviado. Your mail has been sent successfully. El correo ha sido enviado satisfactoriamente. There was an error completing your request! ¡Ha ocurrido un error mientras se completaba la petición! You must be at least a Channel Operator to use this command. This command can only be used inside a channel. Quiz has already ran in channel "{}". Use /quiz stop to force finish. El Quiz ya está en marcha en el canal "{}". Usar /quiz stop para forzar el apagado. Available Quiz dictionaries: Diccionarios para Quiz disponibles: Quiz is not running. El Quiz no está en marcha. {}'s Quiz record: Récord en Quiz de {} {} has never played Quiz. {} nunca ha jugado al Quiz. Top {} Quiz records: Top {} Quiz récords: points puntos This command is reserved for admins. User "{}" is offline El usuario "{}" está offline pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/newaccount.txt000066400000000000000000000030431462640461400214520ustar00rootroot00000000000000Actualmente está conectado al Servidor PvPGN ( Player -vs- Player Gaming Network) TÉRMINOS DE USO del PvPGN: PVPGN es una emulación del servidor de Battle.net(r) y de ninguna manera afiliado con bnetd, warforge o Blizzard Entertainment (R). PVPGN está disponible al público "tal y como es" sin ningún tipo de garantias. No apoyamos la idea de software Pirata y incitamos a todos a obtener una copia legal de cualquiera de los juegos disponibles en nuestros servidores. 1. Información sobre PvPGN. PvPGN no aprueba ni se apela a la precisión, veracidad o fiabilidad de cualquier información ( incluídas declaraciones sobre opinión o consejos) dada en o por cualquier medio de PvPGN. Cualquiera y todas las declaraciones hechas en foros, chat o durante juegos reflejan solamente el parecer de su autor. 2. Exclusión de Garantía. PvPGN está disponible al público "tal y como es" sin ningún tipo de garantias. PvPGN no garantiza que su programa sea ininterrumpido o libre de errores. 3. Limitaciones de Responsabilidad. PvPGN es de ninguna manera responsable por pérdidas o daños de cualquier tipo al usar PvPGN, incluyendo pero no limitándose a, pérdida de libre voluntad, paro laboral, fallos o defectos en los ordenadores, o cualquier y todos otros tipos de daños o perdidas, comerciales o de cualquier otro tipo. Battle.net(R) y Blizzard Entertainment(R) son marcas registrada de Blizzard Entertainment en los E.U. y/o otros países. Aparte de todo esto... Disfrutar jugando.. PvPGN Project Page: http://pvpgn.berlios.de/ pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/termsofservice.txt000066400000000000000000000030161462640461400223440ustar00rootroot00000000000000Actualmente está conectado al Servidor PvPGN ( Player -vs- Player Gaming Network) TÉRMINOS DE USO del PvPGN: PVPGN es una emulación del servidor de Battle.net(r) y de ninguna manera afiliado con bnetd, warforge o Blizzard Entertainment (R). PVPGN está disponible al público "tal y como es" sin ningún tipo de garantias. No apoyamos la idea de software Pirata y incitamos a todos a obtener una copia legal de cualquiera de los juegos disponibles en nuestros servidores. 1. Información sobre PvPGN. PvPGN no aprueba ni se apela a la precisión, veracidad o fiabilidad de cualquier información ( incluídas declaraciones sobre opinión o consejos) dada en o por cualquier medio de PvPGN. Cualquiera y todas las declaraciones hechas en foros, chat o durante juegos reflejan solamente el parecer de su autor. 2. Exclusión de Garantía. PvPGN está disponible al público "tal y como es" sin ningún tipo de garantias. PvPGN no garantiza que su programa sea ininterrumpido o libre de errores. 3. Limitaciones de Responsabilidad. PvPGN es de ninguna manera responsable por pérdidas o daños de cualquier tipo al usar PvPGN, incluyendo pero no limitándose a, pérdida de libre voluntad, paro laboral, fallos o defectos en los ordenadores, o cualquier y todos otros tipos de daños o perdidas, comerciales o de cualquier otro tipo. Battle.net(R) y Blizzard Entertainment(R) son marcas registrada de Blizzard Entertainment en los E.U. y/o otros países. Aparte de todo esto... Disfrutar jugando.. PvPGN Project Page: http://pvpgn.berlios.de/ pvpgn-1.99.7.2.1+dfsg/conf/i18n/esES/w3motd.txt000066400000000000000000000004371462640461400205250ustar00rootroot00000000000000|c00FF0000Bienvenido |c00FF9933to |c0000CCFFthe |c00FF66FF%s |c0000CC00Versión |c009999FF%v |c00FFFFFF Actualmente hay %U usuario(s) en %G partidas de %N, y %u usuario(s) jugando %g partidas y chateando en %c canales. Este texto es invisible, debido a limitación de 11 lineas. pvpgn-1.99.7.2.1+dfsg/conf/i18n/frFR/000077500000000000000000000000001462640461400165435ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/frFR/bnmotd.txt000066400000000000000000000006351462640461400205730ustar00rootroot00000000000000%EBonjour %l, bienvenue sur %s! %IUtilisant la version %v sur %h. %ICe serveur est hébergé par %H %I %IIl y à %a comptes utilisateur sur ce serveur. %IIl y à en ce moment %U ultilisateurs dans %G parties de %N, %Iainsi que %u utilisateurs jouant dans %g parties et discutant dans %c canaux. %IUn problème avec une commande ? Tappez /help pour %Ien savoir plus. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/frFR/chathelp-war3.txt000066400000000000000000000010371462640461400217470ustar00rootroot00000000000000Commandes d'aides du salon de discussions de PvPGN(serveur Emulateur Bnet) Nouvelles commandes: utiliser /f pour afficher la liste d'amis utiliser /users pour voir les utilisateur en ligne utiliser /w ou /msg pour chuchoter a une personne utiliser /join pour joindre un canal de discussions Pour les opйrateurs du canal: utiliser /ban banni l'utilisateur du canal de discussions utiliser /kick pour sortir un utilisateur du canal de discussions pour une aide plus dйtaillй taper: /helppvpgn-1.99.7.2.1+dfsg/conf/i18n/frFR/common.xml000066400000000000000000000006611462640461400205600ustar00rootroot00000000000000 French FRA BEL CAN FRA LUX MCO CHE pvpgn-1.99.7.2.1+dfsg/conf/i18n/itIT/000077500000000000000000000000001462640461400165555ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/itIT/bnmotd.txt000066400000000000000000000005511462640461400206020ustar00rootroot00000000000000%EHello %l, welcome to %s! %IRunning version %v on %h. %IThis server is hosted by %H %I %IThere are %a user accounts on this server. %IThere are currently %U users in %G games of %N, %Iand %u users playing %g games and chatting in %c channels. %IHaving trouble with a command? Type /help to %Ilearn more about it. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/itIT/common.xml000066400000000000000000000004411462640461400205660ustar00rootroot00000000000000 Italian ITA CHE pvpgn-1.99.7.2.1+dfsg/conf/i18n/jpJA/000077500000000000000000000000001462640461400165305ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/jpJA/bnmotd.txt000066400000000000000000000010301462640461400205460ustar00rootroot00000000000000%E%l ã�•ã‚“, PvPGN サーãƒ�ã�¸ã‚ˆã�†ã�“ã��ï¼� %I%h ã�•ã‚“ã�Œ %v ã‚’é�‹ç”¨ã�—ã�¾ã�™ã€‚ %Iã�“ã�®ã‚µãƒ¼ãƒ�ã�¯ä»Š %H ã�•ã‚“ã�Œãƒ›ã‚¹ãƒ†ã‚£ãƒ³ã‚°ä¸­ã�§ã�™ã€‚ %I %I登録ユーザã�®æ•°ã�¯ %a å��ã�§ã�™ã€‚ %I今 %U å��ã�®ãƒ¦ãƒ¼ã‚¶ã�•ã‚“ã�Œ %N ã�« %G 個ã�®ã‚²ãƒ¼ãƒ ã‚’プレー中ã�§ã�™ã€‚ %Iã�™ã�¹ã�¦ã�¯ %u å��ã�®ãƒ¦ãƒ¼ã‚¶ã�•ã‚“ã�Œ %g 個ã�®ã‚²ãƒ¼ãƒ ã‚’プレーã�—ã�¾ã�™ã€‚ %I%c 個ã�®ãƒ�ャンãƒ�ã�Œç”Ÿæˆ�ã�—ã�¾ã�—ã�Ÿã€‚ %Iコマンド・リストã�¯ã€Œ/helpã€�をタイプã�—ã�¦ä¸‹ã�•ã�„。 %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/jpJA/common.xml000066400000000000000000000004051462640461400205410ustar00rootroot00000000000000 Japanese JPN pvpgn-1.99.7.2.1+dfsg/conf/i18n/koKR/000077500000000000000000000000001462640461400165525ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/koKR/bnmotd.txt000066400000000000000000000011021462640461400205700ustar00rootroot00000000000000%E안녕하세요 %l 님, PvPGN 서버ì—� 오신것ì�„ 환ì˜�합니다! %I%h 님ì�´ %v 를 구ë�™í•˜ê³  계십니다. %I본 서버는 %H 님ì�´ 호스팅하고 계십니다. %I %I현재 %a ê°œì�˜ 계정ì�´ 저장ë�˜ì–´ 있습니다. %I현재 %U 명ì�˜ 유저들ì�´ %N ì—�서 %G ê°œì�˜ 게임ì�„ ì¦�기시고 계십니다. %I모ë‘� %u 명ì�˜ 유저가 %g ê°œì�˜ 게임ì�„ ì¦�기고 있으며 %c ê°œì�˜ 채ë„�ì�´ 있습니다. %I명령어ì�˜ 대해 ê¶�금한 사항ì�´ 있으시다구요? %I/help 명령어를 ì�´ìš©í•´ 보세요. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/koKR/common.xml000066400000000000000000000004031462640461400205610ustar00rootroot00000000000000 Korean KOR pvpgn-1.99.7.2.1+dfsg/conf/i18n/koKR/termsofservice.txt000066400000000000000000000026531462640461400223610ustar00rootroot00000000000000현재 PvPGN 서버를 연걸했습니다. (플레ì�´ì–´ 대 플레ì�´ì–´ 게임잉 네트워í�¬) PvPGN ì¡°ê±´: PvPGNì�€ 배틀넷 (Battle.net®) ì—�물레ì�´ì…˜ 서버 ë�¼ê³  bnetd, warforge, Blizzard Entertainmentê³¼ 제휴하고 있지 않다. PvPGNì�´ 그대로 제공하고 ë³´ì¦�서를 없다. 불법 복제ë�œ 소프트웨어ì�˜ 사용ì�„ 지지하지 않다. 우리ì�˜ 서버들ì—�서 게임ì�„ 하려고 합법ì �ì�¸ 버전ì�„ 구입하세요. 1. Information found on PvPGN. PvPGN does not endorse or stand behind the accuracy, truthfulness or reliability of any information (including statements of opinion or advice) provided on or by means of PvPGN. Any and all statements made in forums, chat or during game play reflect only the views of their author. 2. Disclaimer of Warrenty. PvPGN is provided to you "as is" without warrenty of any kind. PvPGN does not warrent that their programm will be uninterrupted or error free. 3. Limitations of Liability PvPGN is in no way liable for loss or damage of any kind resulting from the use of PvPGN, including but not limited to, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other damages or losses, commercial or otherwise. Battle.net(R) and Blizzard Entertainment(R) are trademarks or registered trademarks of Blizzard Entertainment in the U.S. and/or other countries. Besides all this... Happy gaming.. PvPGN Project Page: http://pvpgn.berlios.de/ pvpgn-1.99.7.2.1+dfsg/conf/i18n/newaccount.txt000066400000000000000000000026171462640461400206210ustar00rootroot00000000000000You are currently connected to a PvPGN Server (Player -vs- Player Gaming Network) PvPGN TERMS OF USE: PvPGN is a Battle.net(R) emulation server and in no way affiliated with bnetd, warforge, or Blizzard Entertainment(R). PvPGN is provided "as is" without warrenties of any kind. We do not support the idea of Pirated software and urge you to purchase a legit copy of any games that you play on our servers. 1. Information found on PvPGN. PvPGN does not endorse or stand behind the accuracy, truthfulness or reliability of any information (including statements of opinion or advice) provided on or by means of PvPGN. Any and all statements made in forums, chat or during game play reflect only the views of their author. 2. Disclaimer of Warrenty. PvPGN is provided to you "as is" without warrenty of any kind. PvPGN does not warrent that their programm will be uninterrupted or error free. 3. Limitations of Liability PvPGN is in no way liable for loss or damage of any kind resulting from the use of PvPGN, including but not limited to, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other damages or losses, commercial or otherwise. Battle.net(R) and Blizzard Entertainment(R) are trademarks or registered trademarks of Blizzard Entertainment in the U.S. and/or other countries. Besides all this... Happy gaming.. PvPGN Project Page: http://pvpgn.berlios.de/ pvpgn-1.99.7.2.1+dfsg/conf/i18n/news.txt000066400000000000000000000016111462640461400174200ustar00rootroot00000000000000{03/29/2014} Note that text of news is cached in Warcraft 3 client. Be careful when deleting existing news - it remain on a client side until bncache.dat won't be removed or outdated! A file news.txt is loaded once when the server starts. Use command "/rehash news" to reload the file. New support channel at: http://pvpgn.pro {10/01/2004} Welcome To The Player-vs-Player Gaming Network! Bringing Emulation To New Levels! =-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-= ** IF YOU EXPERIENCE PROBLEMS USING THIS SERVER PLEASE REPORT IT TO THE ** ********* ADMINISTRATOR OF THE SERVER NOT TO THE PVPGN TEAM ************ Project development homepage: http://pvpgn.berlios.de Support channels (ONLY for the admins): ======================================== 1. Forums: http://forums.pvpgn.org 2. IRC live support: #pvpgn on irc.pvpgn.org Enjoy! The PvPGN Team pvpgn-1.99.7.2.1+dfsg/conf/i18n/nlNL/000077500000000000000000000000001462640461400165475ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/nlNL/bnmotd.txt000066400000000000000000000006011462640461400205700ustar00rootroot00000000000000%IHallo %l, welom bij %s! %EWe draaien versie %v op %h. %IDeze server wordt gehost bij %H %I %IEr zijn %a gebruikers accounts op deze server. %IEr zijn momenteel %U gebruikers in %G games van %N, %Ien %u gebruikers spelen %g spellen en chatten in %c channels. %IProblemen met een commando? Type /help om %Ier meer te weten over te komen. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/nlNL/common.xml000066400000000000000000000004371462640461400205650ustar00rootroot00000000000000 Dutch NLD BEL pvpgn-1.99.7.2.1+dfsg/conf/i18n/plPL/000077500000000000000000000000001462640461400165535ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/plPL/bnmotd.txt000066400000000000000000000006361462640461400206040ustar00rootroot00000000000000%ECześć %l, witaj w %s! %IWersja serwera %v uruchomiona na %h. %ISerwer obsÅ‚uguje %H %I %IDo tej pory zarejestrowaliÅ›my %a kont. %IAktualnie jest %U użytkowników zalogowanych, w %G grach, z poÅ›ród %N, %I%u użytkowników gra w %g gier, i rozmawia na %c kanaÅ‚ach. %IJeÅ›li masz jakieÅ› problemy z poleceniami, to napiszsz /help aby %Idowiedzieć siÄ™ wiecej. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/plPL/common.xml000066400000000000000000000004031462640461400205620ustar00rootroot00000000000000 Polish POL pvpgn-1.99.7.2.1+dfsg/conf/i18n/ptBR/000077500000000000000000000000001462640461400165535ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/ptBR/bnmotd.txt000066400000000000000000000005611462640461400206010ustar00rootroot00000000000000%EOlá %l, Bem vindo a %s! %IRodando a versão %v em %h. %IEste servidor é hosteado por %H %I %IExistem %a contas criadas nesse servidor. %IAtuamente existe %U usuarios %G jogos de %N, %IE %u usuários jogando %g jogos e conversando em %c canais. %ITendo algum problema com comando? Digite /help ra %Aprender mais sobre. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/ptBR/common.xml000066400000000000000000000004441462640461400205670ustar00rootroot00000000000000 Portuguese BRA PRT pvpgn-1.99.7.2.1+dfsg/conf/i18n/ruRU/000077500000000000000000000000001462640461400166015ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/ruRU/bnmotd.txt000066400000000000000000000011741462640461400206300ustar00rootroot00000000000000%EПривет %l, добро пожаловать на %s! %IСервер верÑ�ии %v на %h. %IСервер раÑ�положен на %H (%h) %I %IÐ�а Ñ�ервере зарегиÑ�трировано %a аккаунтов. %IСейчаÑ� %G игр и %U игроков в %N %Iиз них %u находÑ�Ñ‚Ñ�Ñ� в %g играх и общаютÑ�Ñ� на %c каналах. %IПроблемы Ñ� иÑ�пользованием команды? Введите /help, чтобы %Iузнать больше о командах чата. %IÐ�аберите /language, чтобы переключить Ñ�зык перевода. %I %M%m pvpgn-1.99.7.2.1+dfsg/conf/i18n/ruRU/chathelp-war3.txt000066400000000000000000000015001462640461400220000ustar00rootroot00000000000000PvPGN Bnet Emulation Chat Help Commands Ð�овые команды: наберите /f help длÑ� вызова Ñ�правки по Ñ�пиÑ�ку друзей наберите /users длÑ� проÑ�мотра пользоватей в онлайне наберите /w или /msg <имÑ�_пользователÑ�> длÑ� личного Ñ�ообщениÑ� наберите /join <канал> длÑ� приÑ�оединениÑ� к каналу ДлÑ� операторов каналов: наберите /ban <имÑ�_пользователÑ�>, чтобы забанить игрока на данном канале наберите /kick <имÑ�_пользователÑ�>, чтобы кикнуть пользователÑ� Ñ� Ñ�того канала ДлÑ� более подробной Ñ�правки наберите: /help pvpgn-1.99.7.2.1+dfsg/conf/i18n/ruRU/common.xml000066400000000000000000000006251462640461400206160ustar00rootroot00000000000000 Russian RUS UZB TTT UKR AZE ARM pvpgn-1.99.7.2.1+dfsg/conf/i18n/svSE/000077500000000000000000000000001462640461400165645ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/svSE/bnmotd.txt000066400000000000000000000006161462640461400206130ustar00rootroot00000000000000%EHej %l, välkommen till %s! %IKör version %v pÃ¥ %h. %IVärden för den här servern är %H %I %IDet finns %a användarkonton pÃ¥ denna server. %IDet finns förnärvarande %U användare i %G spel av %N, %Ioch %u användare som spelar %g spel och chattar i %c kanaler. %IHar du problem med ett kommando? Skriv /help för %Iatt lära dig mer om det. %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/svSE/common.xml000066400000000000000000000004411462640461400205750ustar00rootroot00000000000000 Swedish SWE FIN pvpgn-1.99.7.2.1+dfsg/conf/i18n/termsofservice.txt000066400000000000000000000025721462640461400215130ustar00rootroot00000000000000You are currently connected to a PvPGN Server (Player -vs- Player Gaming Network) PvPGN TERMS OF USE: PvPGN is a Battle.net(R) emulation server and in no way affiliated with bnetd, warforge, or Blizzard Entertainment(R). PvPGN is provided "as is" without warrenties of any kind. We do not support the idea of Pirated software and urge you to purchase a legit copy of any games that you play on our servers. 1. Information found on PvPGN. PvPGN does not endorse or stand behind the accuracy, truthfulness or reliability of any information (including statements of opinion or advice) provided on or by means of PvPGN. Any and all statements made in forums, chat or during game play reflect only the views of their author. 2. Disclaimer of Warrenty. PvPGN is provided to you "as is" without warrenty of any kind. PvPGN does not warrent that their programm will be uninterrupted or error free. 3. Limitations of Liability PvPGN is in no way liable for loss or damage of any kind resulting from the use of PvPGN, including but not limited to, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other damages or losses, commercial or otherwise. Battle.net(R) and Blizzard Entertainment(R) are trademarks or registered trademarks of Blizzard Entertainment in the U.S. and/or other countries. Besides all this... Happy gaming.. PvPGN Project Page: http://pvpgn.berlios.de/ pvpgn-1.99.7.2.1+dfsg/conf/i18n/w3motd.txt000066400000000000000000000004211462640461400176570ustar00rootroot00000000000000|c00FF0000Welcome |c00FF9933to |c0000CCFFthe |c00FF66FF%s |c0000CC00Version |c009999FF%v |c00FFFFFF There are currently %U user(s) in %G games of %N, and %u user(s) playing %g games and chatting in %c channels. This text invisible, because of limitation of 11 linespvpgn-1.99.7.2.1+dfsg/conf/i18n/zhCN/000077500000000000000000000000001462640461400165465ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/zhCN/bnmotd.txt000066400000000000000000000006101462640461400205670ustar00rootroot00000000000000%E%l,你好。欢迎æ�¥åˆ° %sï¼� %I现在 %h 正在è¿�行的版本为 %v %I本æœ�务器由 %H 管ç�†ç»´æŠ¤ %I %I本æœ�务器现在有 %a 个用户账å�· %I当å‰�有 %U 人登陆到 %G 个 %N 游æˆ�中 %I有 %u 人在玩 %g 个游æˆ�,以å�Šåœ¨ %c 个频é�“中è�Šå¤© %I还ä¸�了解å�¯ä»¥ä½¿ç”¨å“ªäº›å‘½ä»¤ï¼Ÿ %I输入 /help å°±å�¯ä»¥å­¦ä¹ äº† %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/zhCN/chathelp-war3.txt000066400000000000000000000006661462640461400217610ustar00rootroot00000000000000PvPGN模拟战网è�Šå¤©å‘½ä»¤å¸®åŠ© 新命令: 使用 /f help 显示朋å�‹å‘½ä»¤å¸®åŠ© 使用 /users 显示在线玩家数 使用 /w 或者 /msg <玩家å��ç§°> å�‘é€�消æ�¯ç»™çީ家 使用 /join <频é�“> 加入指定的频é�“ 频é�“管ç�†å‘˜ï¼š 使用 /ban <玩家å��ç§°> 把玩家从该频é�“中踢出,并ç¦�止玩家å†�次进入此频é�“ 使用 /kick <玩家å��ç§°> 把玩家从该频é�“中踢出 更多的帮助:输入/help pvpgn-1.99.7.2.1+dfsg/conf/i18n/zhCN/common.xml000066400000000000000000000004541462640461400205630ustar00rootroot00000000000000 Chinese Simplified CHN SGP pvpgn-1.99.7.2.1+dfsg/conf/i18n/zhTW/000077500000000000000000000000001462640461400166005ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/conf/i18n/zhTW/bnmotd.txt000066400000000000000000000005731462640461400206310ustar00rootroot00000000000000%E哈囉! %l, 歡迎蒞臨 %s! %I本伺æœ�器由 %H 架設於 %h. %Iç�¾è¡Œçš„伺æœ�器版本是 %v. %I %I本伺æœ�器已有 %a 個使用者帳號. %I共有 %U 個玩家在玩 %N çš„ %G 個é�Šæˆ², %I其中有 %u 個玩家一邊在玩 %g 個é�Šæˆ²ä¸€é‚Šåœ¨ %c 個è�Šå¤©é »é�“. %Iä¸�懂使用指令? 請輸入 /help 來了解更多... %IType /language to switch translation. %I %M%mpvpgn-1.99.7.2.1+dfsg/conf/i18n/zhTW/common.xml000066400000000000000000000005121462640461400206100ustar00rootroot00000000000000 Chinese Traditional TWN MCO HKG pvpgn-1.99.7.2.1+dfsg/conf/icons.conf.in000066400000000000000000000164761462640461400175320ustar00rootroot00000000000000############################################################################## # icons.conf - Custom icons configuration file # #----------------------------------------------------------------------------# # # # Allowed clients: # # W3XP, WAR3, STAR, SEXP, JSTR, SSHR, W2BN, DRTL, DSHR # # # # W3XP: It also disables icon selection from user portrait # # # # [icons] table format (first variable always corresponds to icon_key: # # icon_key | rank | icon_code # # # # [stats] output format: # # initialize variables under a client tag # # band variables with figure brackets {{var}} # # use {{variable->rank}} to display an icon rank for a custom variable # # # # [iconstash] list of available icons that admins can set to user with /icon # # icon_alias | icon_code # # /icon can be set by alias or code, and it's not necessary to add alias # # in the table. icon_alias or icon_code can not equal "default" # # # ############################################################################## ############################################################################## # General settings # #----------------------------------------------------------------------------# # Enable icon sets below custom_icons = true # Command groups for master users who can manage icons of other users (allowed: 12345678) master_commandgroups = 678 ############################################################################## # Warcraft 3 icon set # # Use MPQ editor to edit icons-WAR3.bni (just change extension bni->mpq) # #----------------------------------------------------------------------------# [W3XP] solo_level = "Record\W3XP\solo\level" solo_xp = "Record\W3XP\solo\xp" solo_wins = "Record\W3XP\solo\wins" solo_losses = "Record\W3XP\solo\losses" team_level = "Record\W3XP\team\level" team_xp = "Record\W3XP\team\xp" team_wins = "Record\W3XP\team\wins" team_losses = "Record\W3XP\team\losses" ffa_level = "Record\W3XP\ffa\level" ffa_xp = "Record\W3XP\ffa\xp" ffa_wins = "Record\W3XP\ffa\wins" ffa_losses = "Record\W3XP\ffa\losses" username = "BNET\acct\username" [icons] 0 Beginner KBKB 1 Dungeon KBKD 2 Expert KBKE 3 Mid KBKM 4 Pro KBKP 5 World KBKW 6 Universe WCYB [/icons] [stats] {{username}}'s record: Solo games: [{{solo_level->rank}}] {{solo_xp}} xp ({{solo_wins}} - {{solo_losses}}) Team games: [{{team_level->rank}}] {{team_xp}} xp ({{team_wins}} - {{team_losses}}) FFA games: [{{ffa_level->rank}}] {{ffa_xp}} xp ({{ffa_wins}} - {{ffa_losses}}) [/stats] [iconstash] smile BOMB starcraft SEXP warcraft WAR3 diablo DRTL [/iconstash] ############################################################################## # Starcraft icon set # # Use Bni Icon Builder to edit icons.bni # # https://github.com/pvpgn/bni-icon-builder # #----------------------------------------------------------------------------# [SEXP] ladder_rating = "Record\SEXP\1\rating" ladder_wins = "Record\SEXP\1\wins" ladder_losses = "Record\SEXP\1\losses" ladder_disconnects = "Record\SEXP\1\disconnects" normal_rating = "Record\SEXP\0\rating" normal_wins = "Record\SEXP\0\wins" normal_losses = "Record\SEXP\0\losses" normal_disconnects = "Record\SEXP\0\disconnects" username = "BNET\acct\username" [icons] 1000 n00b NOOB 1250 Chobo+ CHO1 1500 Chobo++ CHO2 1750 Chobo+++ CHO3 2000 Hasu+ HAS1 2300 Hasu++ HAS2 2650 Hasu+++ HAS3 3000 Gosu+ GOS1 3300 Gosu++ GOS2 3650 Gosu+++ GOS3 4000 Mega Gosu GOSU 5500 Father MEGA [/icons] [stats] {{username}}'s record: Ladder games: [{{ladder_rating->rank}}] {{ladder_rating}} pts ({{ladder_wins}}/{{ladder_losses}}/{{ladder_disconnects}}) Normal games: [{{normal_rating->rank}}] {{normal_rating}} pts ({{normal_wins}}/{{normal_losses}}/{{normal_disconnects}}) [/stats] [iconstash] demon EYES warcraft WAR3 diablo DRTL [/iconstash] ############################################################################## # Warcraft II icon set # # Use Bni Icon Builder to edit icons.bni # # https://github.com/pvpgn/bni-icon-builder # #----------------------------------------------------------------------------# [W2BN] ironman_rating = "Record\W2BN\3\rating" ironman_wins = "Record\W2BN\3\wins" ironman_losses = "Record\W2BN\3\losses" ironman_disconnects = "Record\W2BN\3\disconnects" ladder_rating = "Record\W2BN\1\rating" ladder_wins = "Record\W2BN\1\wins" ladder_losses = "Record\W2BN\1\losses" ladder_disconnects = "Record\W2BN\1\disconnects" normal_rating = "Record\W2BN\0\rating" normal_wins = "Record\W2BN\0\wins" normal_losses = "Record\W2BN\0\losses" normal_disconnects = "Record\W2BN\0\disconnects" username = "BNET\acct\username" [icons] 1000 n00b NOOB 1250 Chobo+ CHO1 1500 Chobo++ CHO2 1750 Chobo+++ CHO3 2000 Hasu+ HAS1 2300 Hasu++ HAS2 2650 Hasu+++ HAS3 3000 Gosu+ GOS1 3300 Gosu++ GOS2 3650 Gosu+++ GOS3 4000 Mega Gosu GOSU 5500 Father MEGA [/icons] [stats] {{username}}'s record: Ladder games: [{{ladder_rating->rank}}] {{ladder_rating}} pts ({{ladder_wins}}/{{ladder_losses}}/{{ladder_disconnects}}) Normal games: [{{normal_rating->rank}}] {{normal_rating}} pts ({{normal_wins}}/{{normal_losses}}/{{normal_disconnects}}) IronMan games: [{{ironman_rating->rank}}] {{ironman_rating}} pts ({{ironman_wins}}/{{ironman_losses}}/{{ironman_disconnects}}) [/stats] [iconstash] demon EYES diablo DRTL [/iconstash] ############################################################################## # Diablo: Retail icon set # # Use Bni Icon Builder to edit icons.bni # # https://github.com/pvpgn/bni-icon-builder # #----------------------------------------------------------------------------# [DRTL] class = "Record\DRTL\0\class" level = "Record\DRTL\0\level" dexterity = "Record\DRTL\0\dexterity" magic = "Record\DRTL\0\magic" vitality = "Record\DRTL\0\vitality" strength = "Record\DRTL\0\strength" gold = "Record\DRTL\0\gold" diablo_kills = "Record\DRTL\0\diablo kills" username = "BNET\acct\username" [icons] 0 warrior DRTL 1 rogue DRTL 2 sorcerer DRTL [/icons] [stats] {{username}}'s record: level: {{level}} class: {{class->rank}} stats: {{strength}} str / {{magic}} mag / {{dexterity}} dex / {{vitality}} vit / {{gold}} gold Diablo kills: {{diablo_kills}} [/stats] [iconstash] demon EYES [/iconstash] pvpgn-1.99.7.2.1+dfsg/conf/realm.conf.in000066400000000000000000000025521462640461400175050ustar00rootroot00000000000000################################################################################# # realm.list - List of Diablo II Realms # #-------------------------------------------------------------------------------# # # # Realms are areas that hold closed characters and games. # # Users are given this list of realms to choose from when # # creating a new character. Next time the character is # # used, they will automatically join the same realm. # # # # The realm server runs on port 6113 by default. # # If you do not specify a port number, it will use this by default. # # # # : the realm name (mandatory; must start and end with " ) # # : the realm description (optional; must start and end with " ) # # ip:port - actual ip the d2cs server is running on (mandatory) # # # # --- realm name --- --- description --- --- real address --- # # (mandatory) (optional) (mandatory) # #-------------------------------------------------------------------------------# # "" [""] # # # ################################################################################# # WARNING!! DO NOT USE "127.0.0.1" or "localhost" FOR ANY IP ADDRESS # example (having a d2cs server running on IP 1.2.3.4): #"D2CS" "PvPGN Closed Realm" 1.2.3.4:6113 pvpgn-1.99.7.2.1+dfsg/conf/sql_DB_layout.conf.in000066400000000000000000000133441462640461400211470ustar00rootroot00000000000000# this file determines the layout of the PvPGN SQL database # the server will create the tables with their columns as specified in here # don't forget to create the DB yourself # no empty lines allowed, no whitespaces in front of any entries allowed # file format: # [name_of_a_table] # "column_name with format infos","default value" [&& "sqlcommand to execute on success"] [|| "sqlcommand to execute on failure"] # :"sqlcommand to execute" [&& "sqlcommand to execute on success"] [|| "sqlcommand to execute on failure"] # you might also notice some variables of the form ${variable}. these are # replaced with their meaning when the server starts # ${prefix} is replaced with the configured table prefix from bnetd.conf/storage_path # # For the "sql" SQL layout # [${prefix}BNET] "uid int NOT NULL PRIMARY KEY","'0'" "acct_username varchar(32)","NULL" "username varchar(32)","NULL" && "UPDATE ${prefix}BNET SET username = lower(acct_username)" "acct_userid int","NULL" "acct_passhash1 varchar(40)","NULL" "acct_email varchar(128)","NULL" "auth_admin varchar(6)","'false'" "auth_normallogin varchar(6)","'true'" "auth_changepass varchar(6)","'true'" "auth_changeprofile varchar(6)","'true'" "auth_botlogin varchar(6)","'false'" "auth_operator varchar(6)","'false'" "new_at_team_flag int","'0'" "auth_lock varchar(6)","'false'" "auth_locktime int","'0'" "auth_lockreason varchar(128)","NULL" "auth_mute varchar(6)","'false'" "auth_mutetime int","'0'" "auth_mutereason varchar(128)","NULL" "auth_command_groups varchar(16)","'1'" "acct_lastlogin_time int","'0'" "acct_lastlogin_owner varchar(128)","NULL" "acct_lastlogin_clienttag varchar(4)","NULL" "acct_lastlogin_ip varchar(16)","NULL" :"DROP INDEX username" :"CREATE UNIQUE INDEX username2 ON ${prefix}BNET (username)" [${prefix}WOL] "uid int NOT NULL PRIMARY KEY","'0'" "auth_apgar varchar(8)","NULL" "acct_locale int","'0'" [${prefix}friend] "uid int NOT NULL PRIMARY KEY","'0'" [${prefix}profile] "uid int NOT NULL PRIMARY KEY","'0'" "sex varchar(8)","NULL" "location varchar(128)","NULL" "description varchar(256)","NULL" "age varchar(16)","NULL" "clanname varchar(48)","NULL" [${prefix}Record] "uid int NOT NULL PRIMARY KEY","'0'" "WAR3_solo_xp int","'0'" "WAR3_solo_level int","'0'" "WAR3_solo_wins int","'0'" "WAR3_solo_rank int","'0'" "WAR3_solo_losses int","'0'" "WAR3_team_xp int","'0'" "WAR3_team_level int","'0'" "WAR3_team_rank int","'0'" "WAR3_team_wins int","'0'" "WAR3_team_losses int","'0'" "WAR3_ffa_xp int","'0'" "WAR3_ffa_rank int","'0'" "WAR3_ffa_level int","'0'" "WAR3_ffa_wins int","'0'" "WAR3_ffa_losses int","'0'" "WAR3_orcs_wins int","'0'" "WAR3_orcs_losses int","'0'" "WAR3_humans_wins int","'0'" "WAR3_humans_losses int","'0'" "WAR3_undead_wins int","'0'" "WAR3_undead_losses int","'0'" "WAR3_nightelves_wins int","'0'" "WAR3_nightelves_losses int","'0'" "WAR3_random_wins int","'0'" "WAR3_random_losses int","'0'" "WAR3_teamcount int","'0'" "W3XP_solo_xp int","'0'" "W3XP_solo_level int","'0'" "W3XP_solo_wins int","'0'" "W3XP_solo_rank int","'0'" "W3XP_solo_losses int","'0'" "W3XP_team_xp int","'0'" "W3XP_team_level int","'0'" "W3XP_team_rank int","'0'" "W3XP_team_wins int","'0'" "W3XP_team_losses int","'0'" "W3XP_ffa_xp int","'0'" "W3XP_ffa_rank int","'0'" "W3XP_ffa_level int","'0'" "W3XP_ffa_wins int","'0'" "W3XP_ffa_losses int","'0'" "W3XP_orcs_wins int","'0'" "W3XP_orcs_losses int","'0'" "W3XP_humans_wins int","'0'" "W3XP_humans_losses int","'0'" "W3XP_undead_wins int","'0'" "W3XP_undead_losses int","'0'" "W3XP_nightelves_wins int","'0'" "W3XP_nightelves_losses int","'0'" "W3XP_random_wins int","'0'" "W3XP_random_losses int","'0'" "W3XP_teamcount int","'0'" "W3XP_userselected_icon varchar(4)","NULL" "W3XP_iconstash varchar(1024)","NULL" "STAR_0_wins int","'0'" "STAR_0_losses int","'0'" "STAR_0_disconnects int","'0'" "STAR_1_wins int","'0'" "STAR_1_losses int","'0'" "STAR_1_disconnects int","'0'" "STAR_0_last_game int","'0'" "STAR_0_last_game_result varchar(128)","NULL" "STAR_1_last_game int","'0'" "STAR_1_last_game_result varchar(128)","NULL" "STAR_1_rating int","'0'" "STAR_1_high_rating int","'0'" "STAR_1_rank int","'0'" "STAR_1_high_rank int","'0'" "SEXP_0_wins int","'0'" "SEXP_0_losses int","'0'" "SEXP_0_disconnects int","'0'" "SEXP_1_wins int","'0'" "SEXP_1_losses int","'0'" "SEXP_1_disconnects int","'0'" "SEXP_0_last_game int","'0'" "SEXP_0_last_game_result varchar(128)","NULL" "SEXP_1_last_game int","'0'" "SEXP_1_last_game_result varchar(128)","NULL" "SEXP_1_rating int","'0'" "SEXP_1_high_rating int","'0'" "SEXP_1_rank int","'0'" "SEXP_1_high_rank int","'0'" "SEXP_userselected_icon varchar(4)","NULL" "SEXP_iconstash varchar(1024)","NULL" "TSUN_solo_rank int","'0'" "TSUN_solo_points int","'0'" "TSUN_solo_wins int","'0'" "TSUN_solo_losses int","'0'" "TSUN_solo_disconnects int","'0'" "TSXP_solo_rank int","'0'" "TSXP_solo_points int","'0'" "TSXP_solo_wins int","'0'" "TSXP_solo_losses int","'0'" "TSXP_solo_disconnects int","'0'" "RAL2_solo_rank int","'0'" "RAL2_solo_points int","'0'" "RAL2_solo_wins int","'0'" "RAL2_solo_losses int","'0'" "RAL2_solo_disconnects int","'0'" "YURI_solo_rank int","'0'" "YURI_solo_points int","'0'" "YURI_solo_wins int","'0'" "YURI_solo_losses int","'0'" "YURI_solo_disconnects int","'0'" [${prefix}clan] "cid int NOT NULL PRIMARY KEY","'0'" "short int","'0'" "name varchar(32)","NULL" "motd varchar(255)","NULL", "creation_time int","'0'" [${prefix}clanmember] "uid int NOT NULL PRIMARY KEY","'0'" "cid int","'0'" "status int","'0'" "join_time int","'0'" :"CREATE INDEX cid ON ${prefix}clanmember (cid)" [${prefix}arrangedteam] "teamid int NOT NULL PRIMARY KEY","'0'" "size int","0" "clienttag varchar(8)","NULL" "lastgame int","'0'" "member1 int","'0'" "member2 int","'0'" "member3 int","'0'" "member4 int","'0'" "wins int","'0'" "losses int","'0'" "xp int","'0'" "level int","'0'" "rank int","'0'" pvpgn-1.99.7.2.1+dfsg/conf/supportfile.conf.in000066400000000000000000000005211462640461400207530ustar00rootroot00000000000000# this file contains the names of all files that need to be installed in the filedir # some lines are commented out, cause they weren't installed by default in older versions IX86ver1.mpq ver-IX86-1.mpq PMACver1.mpq XMACver1.mpq bnserver-D2DV.ini bnserver-D2XP.ini bnserver-WAR3.ini bnserver.ini icons-WAR3.bni icons_STAR.bni icons.bni pvpgn-1.99.7.2.1+dfsg/conf/topics.conf.in000066400000000000000000000001631462640461400177020ustar00rootroot00000000000000Chat This is the public chat channel. Feel free to chat... Moderated Support Support is provided in this channel...pvpgn-1.99.7.2.1+dfsg/conf/tournament.conf.in000066400000000000000000000061171462640461400206020ustar00rootroot00000000000000# Tournament Settings # # time format: quotes required: # # "MM/DD/YYYY HH:MM:SS" start_preliminary = "08/01/2003 08:00:00" end_signup = "08/01/2003 09:00:00" end_preliminary = "08/01/2003 10:00:00" start_round_1 = "08/01/2003 11:00:00" start_round_2 = "08/01/2003 12:00:00" start_round_3 = "08/01/2003 13:00:00" start_round_4 = "08/01/2003 14:00:00" tournament_end = "08/01/2003 15:00:00" # game selection: # # PG = 1 # AT = 2 game_selection = 1 # game type: # # 1v1 = 1 # 2v2 = 2 # 3v3 = 3 # 4v4 = 4 game_type = 1 # game client: # # due to the fact RoC and FT clients can not play # games together, tournaments must be specified # for one or the other but not both # # RoC = 1 # FT = 2 game_client = 2 # available races: quotes required: # # H = Humans # O = Orcs # N = Night Elves # U = Undead # R = Random races = "HONUR" # Notice Format: quotes required: format = "PvPGN 1v1 Tournament" # Notice Sponsor: quotes required: sponsor = "The PvPGN Team" # Notice Icon: quotes required: #-----------------------------------------------------------------# # Frozen Throne # Reign of Chaos # # R = Ramdom # R = Ramdom # # 2 = Myrmidon # 2 = Green Dragon Whelp # # 3 = Siren # 3 = Blue Dragon # # 4 = Dragon Turtle # 4 = Red Dragon # # 5 = Sea Witch # 5 = Deathwing # # 6 = Illidan # # # H = Human # H = Human # # 2 = Rifleman # 2 = Footman # # 3 = Sorceress # 3 = Knight # # 4 = Spellbreaker # 4 = Archmage # # 5 = Blood Mage # 5 = Medivh # # 6 = Jaina # # # O = Orcs # O = Orcs # # 1 = Peon # 1 = Peon # # 2 = Troll Headhunter # 2 = Grunt # # 3 = Shaman # 3 = Tauren # # 4 = Spirit Walker # 4 = Far Seer # # 5 = Shadow Hunter # 5 = Thrall # # 6 = Rexxar # # # U = Undead # U = Undead # # 2 = Crypt Fiend # 2 = Ghoul # # 3 = Banshee # 3 = Abomination # # 4 = Destroyer # 4 = Lich # # 5 = Crypt Lord # 5 = Tichondrius # # 6 = Sylvanas # # # N = Night Elves # N = Night Elves # # 2 = Huntress # 2 = Archer # # 3 = Druid of the Talon # 3 = Druid of the Claw # # 4 = Dryad # 4 = Priestess of the Moon # # 5 = Keeper of the Grove # 5 = Furion Stormrage # # 6 = Maiev # # # D = Demons # # # 2 = Felguard # # # 3 = Infernal # # # 4 = Doomguard # # # 5 = Pit Lord # # # 6 = Archimonde # # # # # # W3XP = Frozen Throne Icon # WAR3 = Reign of Chaos Icon # #-----------------------------------------------------------------# icon = "R4" # Thumbs down limit: thumbs_down = 0 # Maps section: # # all maps used for tournaments must be included between [MAPS] & [ENDMAPS] # DO NOT use quotes around client tag or map name in this section [MAPS] WAR3 Maps\(8)Battleground.w3m WAR3 Maps\(8)PlainsOfSnow.w3m WAR3 Maps\(8)BloodvenomFalls.w3m WAR3 Maps\(8)GardenOfWar.w3m W3XP Maps\FrozenThrone\(8)Mur'gulOasis.w3x W3XP Maps\FrozenThrone\(8)PlainsOfSnow.w3x W3XP Maps\FrozenThrone\(8)RockQuarry.w3x W3XP Maps\FrozenThrone\(8)Sanctuary.w3x [ENDMAPS] pvpgn-1.99.7.2.1+dfsg/conf/versioncheck.json.in000066400000000000000000002471271462640461400211250ustar00rootroot00000000000000{ "WAR3": { "IX86": { "0x1c": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=1239576727 C=1604096186 B=4198521212 4 A=A+S B=B-C C=C^A A=A+B", "entries": [ { "title": "Warcraft III - ROC 1.28.2.", "version": "1.28.2.227", "hash": "0x2dc96bf5", "fileMetadata": "War3.exe 05/20/17 13:25:29 558568", "versionTag": "WAR3_1282" }, { "title": "Warcraft III - ROC 1.28.1.", "version": "1.28.1.197", "hash": "0x1fbde0fd", "fileMetadata": "War3.exe 04/17/17 12:06:57 536040", "versionTag": "WAR3_1281" }, { "title": "Warcraft III - ROC 1.28", "version": "1.28.0.37", "hash": "0xe00c3096", "fileMetadata": "War3.exe 04/17/17 12:06:57 536040", "versionTag": "WAR3_128" } ] }, "0x1b": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "B=454282227 C=2370009462 A=2264812340 4 A=A^S B=B-C C=C-A A=A+B", "entries": [ { "title": "Warcraft III - ROC 1.27b", "version": "1.27.1.173", "hash": "0x4026b5b0", "fileMetadata": "War3.exe 12/09/16 06:05:09 515048", "versionTag": "WAR3_127B" }, { "title": "Warcraft III - ROC 1.27a", "version": "1.27.0.16", "hash": "0x960947fd", "fileMetadata": "War3.exe 08/05/16 03:15:27 514536", "versionTag": "WAR3_127A" } ] }, "0x1a": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.26a", "version": "1.26.0.1", "hash": "0xf2e7cec2", "fileMetadata": "war3.exe 03/18/11 20:03:55 471040", "versionTag": "WAR3_126A" } ] }, "0x19": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.25b", "version": "1.25.1.253", "hash": "0x41a8664c", "fileMetadata": "war3.exe 02/12/11 03:32:48 471040", "versionTag": "W3XP_125B" } ] }, "0x18": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.24e", "version": "1.24.4.243", "hash": "0xde4443c5", "fileMetadata": "war3.exe 02/26/10 20:41:08 471040", "versionTag": "W3XP_124E" }, { "title": "Warcraft III - ROC 1.24d", "version": "1.24.3.240", "hash": "0x2a7b4a96", "fileMetadata": "war3.exe 01/13/10 00:35:02 471040", "versionTag": "WAR3_124D" }, { "title": "Warcraft III - ROC 1.24c", "version": "1.24.2.234", "hash": "0x307c4598", "fileMetadata": "war3.exe 10/21/09 19:14:23 471040", "versionTag": "WAR3_124C" }, { "title": "Warcraft III - ROC 1.24b", "version": "1.24.1.230", "hash": "0xcaa59e30", "fileMetadata": "war3.exe 08/07/09 19:20:53 471040", "versionTag": "WAR3_124B" }, { "title": "Warcraft III - ROC 1.24a", "version": "1.24.0.228", "hash": "0xf3c22f0b", "fileMetadata": "war3.exe 07/23/09 20:15:12 471040", "versionTag": "WAR3_124A" } ] }, "0x17": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.23a", "version": "1.23.0.208", "hash": "0xfad9bc05", "fileMetadata": "war3.exe 03/12/09 23:23:40 471040", "versionTag": "WAR3_123A" } ] }, "0x16": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.22a", "version": "1.22.0.184", "hash": "0x909998db", "fileMetadata": "war3.exe 06/27/08 00:01:35 471040", "versionTag": "WAR3_122A" } ] }, "0x15": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.21b", "version": "1.21.1.156", "hash": "0x1b735294", "fileMetadata": "war3.exe 07/19/07 18:41:12 409660", "versionTag": "WAR3_121B" }, { "title": "Warcraft III - ROC 1.21a", "version": "1.21.0.119", "hash": "0xab35b5f1", "fileMetadata": "war3.exe 12/28/06 20:35:21 1572307", "versionTag": "WAR3_121A" } ] }, "0x14": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.20e", "version": "1.20.4.186", "hash": "0x8771b225", "fileMetadata": "war3.exe 06/13/06 06:13:05 1572307", "versionTag": "WAR3_120E" }, { "title": "Warcraft III - ROC 1.20d", "version": "1.20.3.182", "hash": "0xbbd3e672", "fileMetadata": "War3.exe 04/06/06 00:19:58 1572307", "versionTag": "WAR3_120D" }, { "title": "Warcraft III - ROC 1.20c", "version": "1.20.2.177", "hash": "0x97c8e59b", "fileMetadata": "War3.exe 01/05/06 02:50:54 1572307", "versionTag": "WAR3_120C" }, { "title": "Warcraft III - ROC 1.20b", "version": "1.0.20.168", "hash": "0x57e714c1", "fileMetadata": "War3.exe 12/07/05 23:59:26 1572307", "versionTag": "WAR3_120B" }, { "title": "Warcraft III - ROC 1.20a", "version": "1.0.20.160", "hash": "0xdfe00da5", "fileMetadata": "War3.exe 09/23/05 03:16:24 1572307", "versionTag": "WAR3_120A" } ] }, "0x13": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.19b", "version": "1.0.19.158", "hash": "0x9c73744d", "fileMetadata": "war3.exe 09/20/05 04:03:24 1572307", "versionTag": "WAR3_119B" }, { "title": "Warcraft III - ROC 1.19a", "version": "1.0.19.153", "hash": "0x892fa500", "fileMetadata": "war3.exe 09/07/05 18:58:53 1572307", "versionTag": "WAR3_119A" } ] }, "0x12": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.18a", "version": "1.0.18.142", "hash": "0x087fb830", "fileMetadata": "war3.exe 02/18/05 04:38:43 1572307", "versionTag": "WAR3_118A" } ] }, "0x11": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.17a", "version": "1.0.17.100", "hash": "0xd1d89b0e", "fileMetadata": "war3.exe 09/15/04 22:42:18 1568211", "versionTag": "WAR3_117A" } ] }, "0x10": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.16a", "version": "1.0.16.38", "hash": "0xcc882269", "fileMetadata": "War3.exe 06/28/04 23:37:02 1568211", "versionTag": "WAR3_116A" } ] }, "0x0f": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.15a", "version": "1.0.15.156", "hash": "0x39b3974b", "fileMetadata": "war3.exe 05/04/04 01:18:12 1568211", "versionTag": "WAR3_115A" } ] }, "0x0e": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.14b", "version": "1.0.14.152", "hash": "0x36fd6e73", "fileMetadata": "War3.exe 01/09/04 06:11:44 1568211", "versionTag": "WAR3_114B" }, { "title": "Warcraft III - ROC 1.14a", "version": "1.0.14.151", "hash": "0xa842edef", "fileMetadata": "War3.exe 01/06/04 03:44:50 1568211", "versionTag": "WAR3_114A" } ] }, "0x0d": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.13b", "version": "1.0.13.149", "hash": "0x0bb5600d", "fileMetadata": "War3.exe 12/18/03 00:53:06 1568211", "versionTag": "WAR3_113B" } ] }, "0x0c": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - ROC 1.12a", "version": "1.0.12.148", "hash": "0xa9c022bd", "fileMetadata": "war3.exe 07/22/03 22:43:15 1568211", "versionTag": "WAR3_112A" } ] }, "0x0a": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - RoC 1.10a", "version": "1.0.10.146", "hash": "0x75d1c16f", "fileMetadata": "war3.exe 06/17/03 16:37:46 1568211", "versionTag": "WAR3_110A" } ] }, "0x07": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - RoC 1.07 (Used for TFT 1.07a in RoC Mode)", "version": "1.0.7.142", "hash": "0xde80b55b", "fileMetadata": "NULL", "versionTag": "WAR3_107A" } ] }, "0x06": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "WarCraft III - RoC 1.06a", "version": "1.0.6.0", "hash": "0x302dca19", "fileMetadata": "war3.exe 05/23/03 19:50:08 1461715", "versionTag": "WAR3_106A" } ] }, "0x05": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "WarCraft III - RoC 1.05a", "version": "1.0.5.0", "hash": "0xbe284ba9", "fileMetadata": "war3.exe 01/30/03 03:08:10 1170899", "versionTag": "WAR3_105A" } ] }, "0x04": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "WarCraft III - RoC 1.04c", "version": "1.0.4.1", "hash": "0x3f06d9fa", "fileMetadata": "war3.exe 01/16/03 02:44:48 1166807", "versionTag": "WAR3_104C" }, { "title": "WarCraft III - RoC 1.04b", "version": "1.0.3.0", "hash": "0x56f4cc37", "fileMetadata": "war3.exe 10/31/02 17:29:22 1068499", "versionTag": "WAR3_104B" } ] }, "0x03": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - RoC 1.03a", "version": "1.0.3.0", "hash": "0xae4ac7fa", "fileMetadata": "war3.exe 10/08/02 20:54:49 713495", "versionTag": "WAR3_103A" } ] }, "0x02": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - RoC 1.02a", "version": "1.0.1.0", "hash": "0xa67d9a0e", "fileMetadata": "war3.exe 09/06/02 02:41:02 713495", "versionTag": "WAR3_102A" } ] }, "0x01": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - RoC 1.01b", "version": "1.0.1.1", "hash": "0x062ef7dc", "fileMetadata": "war3.exe 07/10/02 18:59:10 713495", "versionTag": "WAR3_101B" }, { "title": "Warcraft III - RoC 1.01", "version": "1.0.1.0", "hash": "0x62c2e1a2", "fileMetadata": "war3.exe 06/28/02 18:41:18 713495", "versionTag": "WAR3_101" } ] }, "0x00": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - RoC 1.00", "version": "1.0.0.0", "hash": "0xf891f129", "fileMetadata": "NULL", "versionTag": "WAR3_100" } ] } } }, "W3XP": { "IX86": { "0x1c": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=1239576727 C=1604096186 B=4198521212 4 A=A+S B=B-C C=C^A A=A+B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.28.5.", "version": "1.28.5.0", "hash": "0x60743fc9", "fileMetadata": "Warcraft III.exe 07/07/17 20:15:59 562152", "versionTag": "W3XP_1285" }, { "title": "Warcraft III - TFT (Expansion) 1.28.4.", "version": "1.28.4.184", "hash": "0xce7bb7ee", "fileMetadata": "Warcraft III.exe 06/21/17 16:24:03 561128", "versionTag": "W3XP_1284" }, { "title": "Warcraft III - TFT (Expansion) 1.28.2.", "version": "1.28.2.227", "hash": "0x2dc96bf5", "fileMetadata": "War3.exe 05/20/17 13:25:29 558568", "versionTag": "W3XP_1282" }, { "title": "Warcraft III - TFT (Expansion) 1.28.1.", "version": "1.28.1.197", "hash": "0x1fbde0fd", "fileMetadata": "War3.exe 04/28/17 19:10:24 558568", "versionTag": "W3XP_1281" }, { "title": "Warcraft III - TFT (Expansion) 1.28.", "version": "1.28.0.37", "hash": "0xe00c3096", "fileMetadata": "War3.exe 04/17/17 12:06:57 536040", "versionTag": "W3XP_128" } ] }, "0x1b": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "B=454282227 C=2370009462 A=2264812340 4 A=A^S B=B-C C=C-A A=A+B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.27b", "version": "1.27.1.173", "hash": "0x4026b5b0", "fileMetadata": "War3.exe 12/09/16 06:05:09 515048", "versionTag": "W3XP_127B" }, { "title": "Warcraft III - TFT (Expansion) 1.27a", "version": "1.27.0.16", "hash": "0x960947fd", "fileMetadata": "War3.exe 08/05/16 03:15:27 514536", "versionTag": "W3XP_127A" } ] }, "0x1a": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.26a", "version": "1.26.0.1", "hash": "0xf2e7cec2", "fileMetadata": "war3.exe 03/18/11 20:03:55 471040", "versionTag": "W3XP_126A" } ] }, "0x19": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.25b", "version": "1.25.1.253", "hash": "0x41a8664c", "fileMetadata": "war3.exe 02/12/11 03:32:48 471040", "versionTag": "W3XP_125B" } ] }, "0x18": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.24e", "version": "1.24.4.243", "hash": "0xde4443c5", "fileMetadata": "war3.exe 02/26/10 20:41:08 471040", "versionTag": "W3XP_124E" }, { "title": "Warcraft III - TFT (Expansion) 1.24d", "version": "1.24.3.240", "hash": "0x2a7b4a96", "fileMetadata": "war3.exe 01/13/10 00:35:02 471040", "versionTag": "W3XP_124D" }, { "title": "Warcraft III - TFT (Expansion) 1.24c", "version": "1.24.2.234", "hash": "0x307c4598", "fileMetadata": "war3.exe 10/21/09 19:14:23 471040", "versionTag": "W3XP_124C" }, { "title": "Warcraft III - TFT (Expansion) 1.24b", "version": "1.24.1.230", "hash": "0xcaa59e30", "fileMetadata": "war3.exe 08/07/09 19:20:53 471040", "versionTag": "W3XP_124B" }, { "title": "Warcraft III - TFT (Expansion) 1.24a", "version": "1.24.0.228", "hash": "0xf3c22f0b", "fileMetadata": "war3.exe 07/23/09 20:15:12 471040", "versionTag": "W3XP_124A" } ] }, "0x17": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.23a", "version": "1.23.0.208", "hash": "0xfad9bc05", "fileMetadata": "war3.exe 03/12/09 23:23:40 471040", "versionTag": "W3XP_123A" } ] }, "0x16": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.22a", "version": "1.22.0.184", "hash": "0x909998db", "fileMetadata": "war3.exe 06/27/08 00:01:35 471040", "versionTag": "W3XP_122A" } ] }, "0x15": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.21b", "version": "1.21.1.156", "hash": "0x1b735294", "fileMetadata": "war3.exe 07/19/07 18:41:12 409660", "versionTag": "W3XP_121B" }, { "title": "Warcraft III - TFT (Expansion) 1.21a", "version": "1.21.0.119", "hash": "0xab35b5f1", "fileMetadata": "war3.exe 12/28/06 20:35:21 1572307", "versionTag": "W3XP_121A" } ] }, "0x14": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.20e", "version": "1.20.4.186", "hash": "0x8771b225", "fileMetadata": "war3.exe 06/13/06 06:13:05 1572307", "versionTag": "W3XP_120E" }, { "title": "Warcraft III - TFT (Expansion) 1.20d", "version": "1.20.3.182", "hash": "0xbbd3e672", "fileMetadata": "War3.exe 04/06/06 00:19:58 1572307", "versionTag": "W3XP_120D" }, { "title": "Warcraft III - TFT (Expansion) 1.20c", "version": "1.20.2.177", "hash": "0x97c8e59b", "fileMetadata": "War3.exe 01/05/06 02:50:54 1572307", "versionTag": "W3XP_120C" }, { "title": "Warcraft III - TFT (Expansion) 1.20b", "version": "1.0.20.168", "hash": "0x57e714c1", "fileMetadata": "War3.exe 12/07/05 23:59:26 1572307", "versionTag": "W3XP_120B" }, { "title": "Warcraft III - TFT (Expansion) 1.20a", "version": "1.0.20.160", "hash": "0xdfe00da5", "fileMetadata": "War3.exe 09/23/05 03:16:24 1572307", "versionTag": "W3XP_120A" } ] }, "0x13": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.19b", "version": "1.0.19.158", "hash": "0x9c73744d", "fileMetadata": "war3.exe 09/20/05 04:03:24 1572307", "versionTag": "W3XP_119B" }, { "title": "Warcraft III - TFT (Expansion) 1.19a", "version": "1.0.19.153", "hash": "0x892fa500", "fileMetadata": "war3.exe 09/07/05 18:58:53 1572307", "versionTag": "W3XP_119A" } ] }, "0x12": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.18a", "version": "1.0.18.142", "hash": "0x087fb830", "fileMetadata": "war3.exe 02/18/05 04:38:43 1572307", "versionTag": "W3XP_118A" } ] }, "0x11": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.17a", "version": "1.0.17.100", "hash": "0xd1d89b0e", "fileMetadata": "war3.exe 09/15/04 22:42:18 1568211", "versionTag": "W3XP_117A" } ] }, "0x10": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.16a", "version": "1.0.16.38", "hash": "0xcc882269", "fileMetadata": "War3.exe 06/28/04 23:37:02 1568211", "versionTag": "W3XP_116A" } ] }, "0x0f": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.15a", "version": "1.0.15.156", "hash": "0x39b3974b", "fileMetadata": "war3.exe 05/04/04 01:18:12 1568211", "versionTag": "W3XP_115A" } ] }, "0x0e": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.14b", "version": "1.0.14.152", "hash": "0x36fd6e73", "fileMetadata": "War3.exe 01/09/04 06:11:44 1568211", "versionTag": "W3XP_114B" }, { "title": "Warcraft III - TFT (Expansion) 1.14a", "version": "1.0.14.151", "hash": "0xa842edef", "fileMetadata": "War3.exe 01/06/04 03:44:50 1568211", "versionTag": "W3XP_114A" } ] }, "0x0d": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.13b", "version": "1.0.13.149", "hash": "0x0bb5600d", "fileMetadata": "War3.exe 12/18/03 00:53:06 1568211", "versionTag": "W3XP_113B" } ] }, "0x0c": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.12a", "version": "1.0.12.148", "hash": "0xa9c022bd", "fileMetadata": "war3.exe 07/22/03 22:43:15 1568211", "versionTag": "W3XP_112A" } ] }, "0x0b": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.11a", "version": "1.0.11.147", "hash": "0x5fce4f04", "fileMetadata": "war3.exe 07/01/03 18:22:52 1568211", "versionTag": "W3XP_111A" } ] }, "0x0a": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.10a", "version": "1.0.10.146", "hash": "0x75d1c16f", "fileMetadata": "war3.exe 06/17/03 16:37:46 1568211", "versionTag": "W3XP_110A" } ] }, "0x07": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft III - TFT (Expansion) 1.07a", "version": "1.0.7.142", "hash": "0xde80b55b", "fileMetadata": "NULL", "versionTag": "W3XP_107A" } ] } } }, "W2BN": { "IX86": { "0x4f": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Warcraft II BNE 2.02b", "version": "2.0.2.1", "hash": "0xff0d4c4a", "fileMetadata": "Warcraft II BNE.exe 05/21/01 21:52:22 712704", "versionTag": "W2BN_202B" }, { "title": "Warcraft II BNE 2.02a", "version": "2.0.2.0", "hash": "0xb52bad87", "fileMetadata": "Warcraft II BNE.exe 05/15/01 20:53:19 712704", "versionTag": "W2BN_202A" } ] }, "0x4b": { "checkRevisionFile": "IX86ver4.mpq", "equation": "A=2383634235 B=711286254 C=3710735432 4 A=A+S B=B^C C=C+A A=A^B", "entries": [ { "title": "Warcraft II BNE v2.01", "version": "2.0.0.154", "hash": "0x0", "fileMetadata": "Warcraft II BNE.exe ??/??/?? ??:??:?? 704512", "versionTag": "W2BN_201" } ] } } }, "STAR": { "IX86": { "0xd6": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "StarCraft 1.18.0", "version": "1.18.0.65", "hash": "0x7ab2ce34", "fileMetadata": "StarCraft.exe 10/20/17 01:12:49 8568296", "versionTag": "STAR_1180" } ] }, "0xd5": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "StarCraft: Brood War 1.17.0 - (StarCraft mode)", "version": "1.17.0.1", "hash": "0xcb82b07c", "fileMetadata": "StarCraft.exe 07/11/16 15:59:46 1243112", "versionTag": "STAR_1170" }, { "title": "StarCraft 1.17.0", "version": "1.17.0.0", "hash": "0xa5e7b4cc", "fileMetadata": "StarCraft.exe 07/11/16 15:59:51 1243112", "versionTag": "STAR_1170" } ] }, "0xd3": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.16.1 - (Starcraft mode)", "version": "1.16.1.1", "hash": "0x0a85372d", "fileMetadata": "StarCraft.exe 01/09/09 22:57:43 1220608", "versionTag": "STAR_1161" }, { "title": "Starcraft (Standalone) 1.16.1", "version": "1.16.1.0", "hash": "0x02623d85", "fileMetadata": "StarCraft.exe 01/08/09 05:23:44 1220608", "versionTag": "STAR_1161" }, { "title": "Starcraft - Broodwar (Expansion) 1.16 - (Starcraft mode)", "version": "1.16.0.1", "hash": "0xd206c475", "fileMetadata": "StarCraft.exe 11/06/08 01:10:59 1220608", "versionTag": "STAR_116" }, { "title": "Starcraft (Standalone) 1.16", "version": "1.16.0.0", "hash": "0xcab35ce0", "fileMetadata": "StarCraft.exe 11/06/08 01:14:14 1220608", "versionTag": "STAR_116" } ] }, "0xd1": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.15.3 - (Starcraft mode)", "version": "1.15.3.1", "hash": "0x2abff892", "fileMetadata": "StarCraft.exe 07/25/08 23:34:58 1220608", "versionTag": "STAR_1153" }, { "title": "Starcraft - Broodwar (Expansion) 1.15.2 - (Starcraft mode)", "version": "1.15.2.1", "hash": "0x8fbdf18d", "fileMetadata": "StarCraft.exe 01/10/08 20:23:42 1220608", "versionTag": "STAR_1152" }, { "title": "Starcraft - Broodwar (Expansion) 1.15.1 - (Starcraft mode)", "version": "1.15.1.0", "hash": "0x99e92077", "fileMetadata": "starcraft.exe 07/19/07 02:30:05 1220608", "versionTag": "STAR_1151" }, { "title": "Starcraft - Broodwar (Expansion) 1.15 - (Starcraft mode)", "version": "1.15.0.1", "hash": "0x1eec4ebc", "fileMetadata": "starcraft.exe 05/08/07 19:13:26 1220608", "versionTag": "STAR_115" }, { "title": "Starcraft (Standalone) 1.15.3", "version": "1.15.3.0", "hash": "0xc9fc7927", "fileMetadata": "StarCraft.exe 07/25/08 23:36:39 1220608", "versionTag": "STAR_1153" }, { "title": "Starcraft (Standalone) 1.15.2", "version": "1.15.2.0", "hash": "0x91c0f907", "fileMetadata": "StarCraft.exe 01/08/08 23:45:36 1220608", "versionTag": "STAR_1152" }, { "title": "Starcraft (Standalone) 1.15", "version": "1.15.0.0", "hash": "0xd892b992", "fileMetadata": "starcraft.exe 05/08/07 19:14:08 1220608", "versionTag": "STAR_115" } ] }, "0xcf": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.14 - (Starcraft mode)", "version": "1.14.0.1", "hash": "0xf5280a9e", "fileMetadata": "starcraft.exe 05/10/06 00:13:58 1216512", "versionTag": "STAR_114" }, { "title": "Starcraft (Standalone) 1.14", "version": "1.14.0.0", "hash": "0x1f109657", "fileMetadata": "starcraft.exe 05/19/06 00:05:06 1216512", "versionTag": "STAR_114" } ] }, "0xcd": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.13f - (Starcraft mode)", "version": "1.1.3.11", "hash": "0x5c5e4509", "fileMetadata": "starcraft.exe 01/13/06 05:42:48 1146939", "versionTag": "STAR_113F" }, { "title": "Starcraft - Broodwar (Expansion) 1.13e - (Starcraft mode)", "version": "1.1.3.9", "hash": "0xdee278b8", "fileMetadata": "starcraft.exe 09/07/05 19:06:42 1093632", "versionTag": "STAR_113E" }, { "title": "Starcraft - Broodwar (Expansion) 1.13d - (Starcraft mode)", "version": "1.1.3.7", "hash": "0x5b3cbe07", "fileMetadata": "starcraft.exe 08/25/05 22:23:54 1093632", "versionTag": "SEXP_113D_STAR" }, { "title": "Starcraft - Broodwar (Expansion) 1.13c - (Starcraft mode)", "version": "1.1.3.5", "hash": "0xbd7aca6e", "fileMetadata": "starcraft.exe 08/18/05 23:42:08 1093632", "versionTag": "SEXP_113C_STAR" }, { "title": "Starcraft - Broodwar (Expansion) 1.13b - (Starcraft mode)", "version": "1.1.3.3", "hash": "0x02cfab16", "fileMetadata": "starcraft.exe 08/10/05 22:19:41 1093632", "versionTag": "SEXP_113B_STAR" }, { "title": "Starcraft - Broodwar (Expansion) 1.13 - (Starcraft mode)", "version": "1.1.3.1", "hash": "0x02bb0696", "fileMetadata": "starcraft.exe 06/24/05 17:19:30 1093632", "versionTag": "SEXP_113_STAR" }, { "title": "Starcraft (Standalone) 1.13f", "version": "1.1.3.10", "hash": "0x0e3d3732", "fileMetadata": "starcraft.exe 01/13/06 05:46:36 1146939", "versionTag": "STAR_113F" }, { "title": "Starcraft (Standalone) 1.13e", "version": "1.1.3.8", "hash": "0xa21f8b5c", "fileMetadata": "starcraft.exe 09/07/05 19:31:20 1093632", "versionTag": "STAR_113E" }, { "title": "Starcraft (Standalone) 1.13d", "version": "1.1.3.6", "hash": "0xdef3af2b", "fileMetadata": "starcraft.exe 08/25/05 22:36:16 1093632", "versionTag": "STAR_113D" }, { "title": "Starcraft (Standalone) 1.13c", "version": "1.1.3.4", "hash": "0xe888a7ce", "fileMetadata": "starcraft.exe 08/18/05 23:52:34 1093632", "versionTag": "STAR_113C" }, { "title": "Starcraft (Standalone) 1.13b", "version": "1.1.3.2", "hash": "0x934ce3ac", "fileMetadata": "starcraft.exe 08/10/05 22:24:39 1093632", "versionTag": "STAR_113B" }, { "title": "Starcraft (Standalone) 1.13", "version": "1.1.3.0", "hash": "0x3462622c", "fileMetadata": "starcraft.exe 06/24/05 17:26:11 1093632", "versionTag": "STAR_113" } ] }, "0xcb": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.12b - (Starcraft mode)", "version": "1.1.2.3", "hash": "0x0eea30bc", "fileMetadata": "starcraft.exe 02/10/05 02:14:10 1093632", "versionTag": "SEXP_112B_STAR" }, { "title": "Starcraft - Broodwar (Expansion) 1.12 - (Starcraft mode)", "version": "1.1.2.1", "hash": "0x796786fe", "fileMetadata": "starcraft.exe 02/10/05 05:36:14 1093632", "versionTag": "SEXP_112_STAR" }, { "title": "Starcraft (Standalone) 1.12b", "version": "1.1.2.2", "hash": "0x90abd67f", "fileMetadata": "starcraft.exe 02/18/05 02:18:55 1093632", "versionTag": "STAR_112B" }, { "title": "Starcraft (Standalone) 1.12", "version": "1.1.2.0", "hash": "0x2132ae0b", "fileMetadata": "starcraft.exe 02/10/05 05:37:38 1093632", "versionTag": "STAR_112" } ] }, "0xc9": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.11b - (Starcraft mode)", "version": "1.1.1.3", "hash": "0xd1ef9ca4", "fileMetadata": "starcraft.exe 05/26/04 00:46:00 1048576", "versionTag": "SEXP_111B_STAR" }, { "title": "Starcraft - Broodwar (Expansion) 1.11 - (Starcraft mode)", "version": "1.1.1.1", "hash": "0x706c67e6", "fileMetadata": "starcraft.exe 04/27/04 05:12:02 1048576", "versionTag": "SEXP_111_STAR" }, { "title": "Starcraft 1.11b", "version": "1.1.1.2", "hash": "0x7e9ce2f1", "fileMetadata": "starcraft.exe 05/26/04 01:25:14 1048576", "versionTag": "STAR_111B" } ] }, "0xc7": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.10 - (Starcraft mode)", "version": "1.1.0.1", "hash": "0xe20a2ca1", "fileMetadata": "starcraft.exe 03/28/03 04:21:58 1064960", "versionTag": "SEXP_110_STAR" }, { "title": "Starcraft 1.10", "version": "1.1.0.0", "hash": "0x8515bf6a", "fileMetadata": "starcraft.exe 03/27/03 04:30:39 1064960", "versionTag": "STAR_110" } ] }, "0xc5": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.09 - (StarCraft mode)", "version": "1.0.9.1", "hash": "0x9333e69f", "fileMetadata": "starcraft.exe 02/05/02 07:17:26 1083392", "versionTag": "SEXP_109_STAR" }, { "title": "Starcraft 1.09b", "version": "1.0.9.2", "hash": "0x96d28df0", "fileMetadata": "starcraft.exe 02/21/02 21:48:41 1083392", "versionTag": "STAR_109B" }, { "title": "Starcraft 1.09", "version": "1.0.9.0", "hash": "0x251098be", "fileMetadata": "starcraft.exe 02/05/02 07:37:20 1083392", "versionTag": "STAR_109" } ] }, "0xc3": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.08b - (StarCraft mode)", "version": "1.0.8.11", "hash": "0x95b346ec", "fileMetadata": "starcraft.exe 05/19/01 02:28:22 1082880", "versionTag": "SEXP_108B_STAR" }, { "title": "Starcraft - Broodwar (Expansion) 1.08 - (StarCraft mode)", "version": "1.0.8.1", "hash": "0x5063884e", "fileMetadata": "starcraft.exe 05/17/01 03:51:08 1082880", "versionTag": "SEXP_108_STAR" }, { "title": "Starcraft 1.08b", "version": "1.0.8.10", "hash": "0x7859c173", "fileMetadata": "starcraft.exe 05/19/01 02:01:50 1082880", "versionTag": "STAR_108B" }, { "title": "Starcraft 1.08", "version": "1.0.8.0", "hash": "0x7b58b906", "fileMetadata": "starcraft.exe 05/17/01 04:12:40 1082880", "versionTag": "STAR_108" } ] }, "0xc1": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.07 - (StarCraft mode)", "version": "1.0.7.1", "hash": "0xacf13dce", "fileMetadata": "starcraft.exe 10/30/99 03:54:30 1043456", "versionTag": "SEXP_107_STAR" }, { "title": "Starcraft 1.07", "version": "1.0.7.0", "hash": "0x79126108", "fileMetadata": "starcraft.exe 10/30/99 02:20:12 1043456", "versionTag": "STAR_107" } ] }, "0xbd": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.06 - (StarCraft mode)", "version": "1.0.6.1", "hash": "0x1acce912", "fileMetadata": "starcraft.exe 09/28/99 19:41:34 1042432", "versionTag": "SEXP_106_STAR" }, { "title": "Starcraft - Broodwar (Expansion) 1.05 - (StarCraft mode)", "version": "1.0.5.1", "hash": "0x9a2d64e6", "fileMetadata": "starcraft.exe 03/08/99 22:37:32 1042432", "versionTag": "SEXP_105_STAR" }, { "title": "Starcraft 1.06", "version": "1.0.6.0", "hash": "0xfea28485", "fileMetadata": "starcraft.exe 09/25/99 22:40:10 1042432", "versionTag": "STAR_106" }, { "title": "Starcraft 1.05", "version": "1.0.5.0", "hash": "0xc243123c", "fileMetadata": "starcraft.exe 03/08/99 22:41:50 1042432", "versionTag": "STAR_105" } ] }, "0xbb": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.04 - (StarCraft mode)", "version": "1.0.4.1", "hash": "0xfd581427", "fileMetadata": "NULL", "versionTag": "SEXP_104_STAR" }, { "title": "# Starcraft 1.04", "version": "1.0.4.0", "hash": "0x2779bdc8", "fileMetadata": "starcraft.exe 12/16/98 22:24:18 1041408", "versionTag": "STAR_104" } ] }, "0xa9": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft 1.03", "version": "1.0.3.0", "hash": "0x1309eaad", "fileMetadata": "starcraft.exe 10/09/98 23:18:22 987648", "versionTag": "STAR_103" } ] } } }, "SEXP": { "IX86": { "0xd6": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "StarCraft: Brood War 1.18.0", "version": "1.18.0.65", "hash": "0x7ab2ce34", "fileMetadata": "StarCraft.exe 10/20/17 01:12:49 8568296", "versionTag": "SEXP_1180" } ] }, "0xd5": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "StarCraft: Brood War 1.17.0 - (Brood War mode)", "version": "1.17.0.1", "hash": "0xcb82b07c", "fileMetadata": "StarCraft.exe 07/11/16 15:59:46 1243112", "versionTag": "SEXP_1170" } ] }, "0xd3": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.16.1 - (Broodwar mode)", "version": "1.16.1.1", "hash": "0x0a85372d", "fileMetadata": "StarCraft.exe 01/09/09 22:57:43 1220608", "versionTag": "SEXP_1161" }, { "title": "Starcraft - Broodwar (Expansion) 1.16 - (Broodwar mode)", "version": "1.16.0.1", "hash": "0xd206c475", "fileMetadata": "StarCraft.exe 11/06/08 01:10:59 1220608", "versionTag": "SEXP_116" } ] }, "0xd1": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.15.3 - (Broodwar mode)", "version": "1.15.3.1", "hash": "0x2abff892", "fileMetadata": "StarCraft.exe 07/25/08 23:34:58 1220608", "versionTag": "SEXP_1153" }, { "title": "Starcraft - Broodwar (Expansion) 1.15.2 - (Broodwar mode)", "version": "1.15.2.1", "hash": "0x8fbdf18d", "fileMetadata": "StarCraft.exe 01/10/08 20:23:42 1220608", "versionTag": "SEXP_1152" }, { "title": "Starcraft - Broodwar (Expansion) 1.15.1 - (Broodwar mode)", "version": "1.15.1.0", "hash": "0x99e92077", "fileMetadata": "starcraft.exe 07/19/07 02:30:05 1220608", "versionTag": "SEXP_1151" }, { "title": "Starcraft - Broodwar (Expansion) 1.15 - (Broodwar mode)", "version": "1.15.0.1", "hash": "0x1eec4ebc", "fileMetadata": "starcraft.exe 05/08/07 19:13:26 1220608", "versionTag": "SEXP_115" } ] }, "0xcf": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft Broodwar (Expansion) 1.14 - (Broodwar mode)", "version": "1.14.0.1", "hash": "0xf5280a9e", "fileMetadata": "starcraft.exe 05/10/06 00:13:58 1216512", "versionTag": "SEXP_114" } ] }, "0xcd": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.13f - (Broodwar mode)", "version": "1.1.3.11", "hash": "0x5c5e4509", "fileMetadata": "starcraft.exe 01/13/06 05:42:48 1146939", "versionTag": "SEXP_113F" }, { "title": "Starcraft - Broodwar (Expansion) 1.13e - (Broodwar mode)", "version": "1.1.3.9", "hash": "0xdee278b8", "fileMetadata": "starcraft.exe 09/07/05 19:06:42 1093632", "versionTag": "SEXP_113E" }, { "title": "Starcraft - Broodwar (Expansion) 1.13d - (Broodwar mode)", "version": "1.1.3.7", "hash": "0x5b3cbe07", "fileMetadata": "starcraft.exe 08/25/05 22:23:54 1093632", "versionTag": "SEXP_113D" }, { "title": "Starcraft - Broodwar (Expansion) 1.13c - (Broodwar mode)", "version": "1.1.3.5", "hash": "0xbd7aca6e", "fileMetadata": "starcraft.exe 08/18/05 23:42:08 1093632", "versionTag": "SEXP_113C" }, { "title": "Starcraft - Broodwar (Expansion) 1.13b - (Broodwar mode)", "version": "1.1.3.3", "hash": "0x02cfab16", "fileMetadata": "starcraft.exe 08/10/05 22:19:41 1093632", "versionTag": "SEXP_113B" }, { "title": "Starcraft - Broodwar (Expansion) 1.13 - (Broodwar mode)", "version": "1.1.3.1", "hash": "0x02bb0696", "fileMetadata": "starcraft.exe 06/24/05 17:19:30 1093632", "versionTag": "SEXP_113" } ] }, "0xcb": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.12b - (Broodwar mode)", "version": "1.1.2.3", "hash": "0x0eea30bc", "fileMetadata": "starcraft.exe 02/18/05 02:14:10 1093632", "versionTag": "SEXP_112B" }, { "title": "Starcraft - Broodwar (Expansion) 1.12 - (Broodwar mode)", "version": "1.1.2.1", "hash": "0x796786fe", "fileMetadata": "starcraft.exe 02/10/05 05:36:14 1093632", "versionTag": "SEXP_112" } ] }, "0xc9": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.11b - (Broodwar mode)", "version": "1.1.1.3", "hash": "0xd1ef9ca4", "fileMetadata": "starcraft.exe 05/26/04 00:46:00 1048576", "versionTag": "SEXP_111B" }, { "title": "Starcraft - Broodwar (Expansion) 1.11 - (Broodwar mode)", "version": "1.1.1.1", "hash": "0x706c67e6", "fileMetadata": "starcraft.exe 04/27/04 05:12:02 1048576", "versionTag": "SEXP_111" } ] }, "0xc7": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.10 - (Broodwar mode)", "version": "1.1.0.1", "hash": "0xe20a2ca1", "fileMetadata": "starcraft.exe 03/28/03 04:21:58 1064960", "versionTag": "SEXP_110" } ] }, "0xc5": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.09b - (Broodwar mode)", "version": "1.0.9.3", "hash": "0xf76df1ed", "fileMetadata": "starcraft.exe 02/21/02 20:54:04 1083392", "versionTag": "SEXP_109B" }, { "title": "Starcraft - Broodwar (Expansion) 1.09 - (Broodwar mode)", "version": "1.0.9.1", "hash": "0x9333e69f", "fileMetadata": "starcraft.exe 02/05/02 07:17:26 1083392", "versionTag": "SEXP_109" } ] }, "0xc3": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.08b - (Broodwar mode)", "version": "1.0.8.11", "hash": "0x95b346ec", "fileMetadata": "starcraft.exe 05/19/01 02:28:22 1082880", "versionTag": "SEXP_108B" }, { "title": "Starcraft - Broodwar (Expansion) 1.08 - (Broodwar mode)", "version": "1.0.8.1", "hash": "0x5063884e", "fileMetadata": "starcraft.exe 05/17/01 03:51:08 1082880", "versionTag": "SEXP_108" } ] }, "0xc1": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.07 - (Broodwar mode)", "version": "1.0.7.1", "hash": "0xacf13dce", "fileMetadata": "starcraft.exe 10/30/99 03:54:30 1043456", "versionTag": "SEXP_107" } ] }, "0xbd": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.06 - (Broodwar mode)", "version": "1.0.6.1", "hash": "0x1acce912", "fileMetadata": "starcraft.exe 09/28/99 19:41:34 1042432", "versionTag": "SEXP_106" }, { "title": "Starcraft - Broodwar (Expansion) 1.05 - (Broodwar mode)", "version": "1.0.5.1", "hash": "0x9a2d64e6", "fileMetadata": "starcraft.exe 03/08/99 22:37:32 1042432", "versionTag": "SEXP_105" } ] }, "0xbb": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B", "entries": [ { "title": "Starcraft - Broodwar (Expansion) 1.04 - (Broodwar mode)", "version": "1.0.4.1", "hash": "0xfd581427", "fileMetadata": "NULL", "versionTag": "SEXP_104" } ] } } }, "DRTL": { "IX86": { "0x2a": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo 109b", "version": "1.0.9.2", "hash": "0x23135c73", "fileMetadata": "Diablo.exe 05/18/01 23:10:57 757760", "versionTag": "DRTL_109B" }, { "title": "Diablo 109", "version": "1.0.9.1", "hash": "0x0dcb0513", "fileMetadata": "Diablo.exe 05/12/01 00:53:17 757760", "versionTag": "DRTL_109" } ] }, "0x28": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo 108", "version": "1.0.8.1", "hash": "0x09eb1213", "fileMetadata": "Diablo.exe 05/24/00 01:16:01 764928", "versionTag": "DRTL_108" } ] } } }, "D2XP": { "IX86": { "0x0e": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II - LoD (Expansion) 1.14d", "version": "1.14.3.0", "hash": "0x43f51a83", "fileMetadata": "Game.exe 05/31/16 19:02:24 3618792", "versionTag": "D2XP_114D" }, { "title": "Diablo II - LoD (Expansion) 1.14c", "version": "1.14.2.0", "hash": "0x6123b0f2", "fileMetadata": "Game.exe 05/06/16 18:15:44 3586024", "versionTag": "D2XP_114C" }, { "title": "Diablo II - LoD (Expansion) 1.14b", "version": "1.14.1.0", "hash": "0x1373c295", "fileMetadata": "Game.exe 03/31/16 01:09:01 3590120", "versionTag": "D2XP_114B" }, { "title": "Diablo II - LoD (Expansion) 1.14a", "version": "1.14.0.0", "hash": "0xbcb5b3bb", "fileMetadata": "Game.exe 02/29/16 17:34:46 3590120", "versionTag": "D2XP_114A" } ] }, "0x0d": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II - LoD (Expansion) 1.13c", "version": "1.0.13.0", "hash": "0x7686beca", "fileMetadata": "Game.exe 03/09/10 04:10:51 61440", "versionTag": "D2XP_113C" } ] }, "0x09": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II - LoD (Expansion) 1.09b", "version": "1.0.9.0", "hash": "0x1436c138", "fileMetadata": "Game.exe 08/31/01 22:40:56 428163", "versionTag": "D2XP_109B" } ] }, "0x08": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II - LoD (Expansion) 1.08", "version": "1.0.8.0", "hash": "0x3a8664ff", "fileMetadata": "Game.exe 06/19/01 02:24:32 428163", "versionTag": "D2XP_108" } ] }, "0x07": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II - LoD (Expansion) 1.07", "version": "1.0.7.0", "hash": "0x4f3079e6", "fileMetadata": "Game.exe 01/23/02 06:39:26 424067", "versionTag": "D2XP_107" } ] } } }, "D2DV": { "IX86": { "0x0e": { "checkRevisionFile": "ver-IX86-1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.14d", "version": "1.14.3.0", "hash": "0x6b6a21b5", "fileMetadata": "Game.exe 05/31/16 19:02:32 3614696", "versionTag": "D2DV_114D" }, { "title": "Diablo II 1.14c", "version": "1.14.2.0", "hash": "0x7d54f650", "fileMetadata": "Game.exe 05/06/16 18:15:49 3581928", "versionTag": "D2DV_114C" }, { "title": "Diablo II 1.14b", "version": "1.14.1.0", "hash": "0x77f3db54", "fileMetadata": "Game.exe 06/13/16 02:02:21 3586024", "versionTag": "D2DV_114B" }, { "title": "Diablo II 1.14a", "version": "1.14.0.0", "hash": "0xd9e9368a", "fileMetadata": "Game.exe 02/29/16 17:34:50 3586024", "versionTag": "D2DV_114A" } ] }, "0x0d": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.13c", "version": "1.0.13.0", "hash": "0xfc04b9f6", "fileMetadata": "Game.exe 03/09/10 04:11:00 57344", "versionTag": "D2DV_113C" } ] }, "0x09": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.09b", "version": "1.0.9.0", "hash": "0xb9cb2cb1", "fileMetadata": "Game.exe 08/31/01 22:40:35 424067", "versionTag": "D2DV_109B" } ] }, "0x08": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.08", "version": "1.0.8.0", "hash": "0x9e488d3d", "fileMetadata": "Game.exe 06/19/01 02:24:04 424067", "versionTag": "D2DV_108" } ] }, "0x06": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.06b", "version": "1.0.6.0", "hash": "0xb2fb38b7", "fileMetadata": "Game.exe 05/16/01 18:55:11 419971", "versionTag": "D2DV_106B" } ] }, "0x05": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.05b", "version": "1.0.5.1", "hash": "0x36869f98", "fileMetadata": "Game.exe 02/01/01 21:21:16 399491", "versionTag": "D2DV_105B" }, { "title": "Diablo II 1.05", "version": "1.0.5.0", "hash": "0xd6dec09b", "fileMetadata": "Game.exe 01/27/01 01:16:00 399491", "versionTag": "D2DV_105" } ] }, "0x04": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.04c", "version": "1.0.4.2", "hash": "0xc6d40aaf", "fileMetadata": "Game.exe 12/23/00 18:28:04 387107", "versionTag": "D2DV_104B" }, { "title": "Diablo II 1.04b", "version": "1.0.4.1", "hash": "0x3882960e", "fileMetadata": "Game.exe 12/22/00 18:08:34 403587", "versionTag": "D2DV_104B" } ] }, "0x03": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.03", "version": "1.0.3.0", "hash": "0x0120ed90", "fileMetadata": "Game.exe 08/01/00 00:34:00 346243", "versionTag": "D2DV_103" } ] }, "0x01": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.01", "version": "1.0.0.1", "hash": "0x5aef7e66", "fileMetadata": "Game.exe 06/26/00 22:31:00 346243", "versionTag": "D2DV_101" } ] }, "0x00": { "checkRevisionFile": "IX86ver1.mpq", "equation": "A=3845581634 B=880823580 C=1363937103 4 A=A-S B=B-C C=C-A A=A-B", "entries": [ { "title": "Diablo II 1.00 (installation)", "version": "1.0.0.1", "hash": "0xac5e46cb", "fileMetadata": "Game.exe 01/01/97 02:05:09 309379", "versionTag": "D2DV_100" } ] } } } } pvpgn-1.99.7.2.1+dfsg/config.h.cmake000066400000000000000000000046651462640461400167100ustar00rootroot00000000000000#ifndef PVPGN_CONFIG_H #define PVPGN_CONFIG_H #cmakedefine HAVE_FCNTL_H #cmakedefine HAVE_SYS_TIME_H #cmakedefine HAVE_SYS_SELECT_H #cmakedefine HAVE_UNISTD_H #cmakedefine HAVE_SYS_UTSNAME_H #cmakedefine HAVE_SYS_TIMEB_H #cmakedefine HAVE_SYS_SOCKET_H #cmakedefine HAVE_SYS_PARAM_H #cmakedefine HAVE_NETINET_IN_H #cmakedefine HAVE_ARPA_INET_H #cmakedefine HAVE_NETDB_H #cmakedefine HAVE_TERMIOS_H #cmakedefine HAVE_SYS_TYPES_H #cmakedefine HAVE_SYS_WAIT_H #cmakedefine HAVE_SYS_FILE_H #cmakedefine HAVE_POLL_H #cmakedefine HAVE_SYS_POLL_H #cmakedefine HAVE_SYS_STAT_H #cmakedefine HAVE_PWD_H #cmakedefine HAVE_GRP_H #cmakedefine HAVE_DIR_H #cmakedefine HAVE_DIRENT_H #cmakedefine HAVE_NDIR_H #cmakedefine HAVE_SYS_DIR_H #cmakedefine HAVE_SYS_NDIR_H #cmakedefine HAVE_DIRECT_H #cmakedefine HAVE_SYS_MMAN_H #cmakedefine HAVE_SYS_EVENT_H #cmakedefine HAVE_SYS_EPOLL_H #cmakedefine HAVE_SYS_RESOURCE_H #cmakedefine HAVE_WINDOWS_H #cmakedefine HAVE_WINSOCK2_H #cmakedefine HAVE_WS2TCPIP_H #cmakedefine HAVE_PROCESS_H #cmakedefine HAVE_CHDIR #cmakedefine HAVE_EPOLL_CREATE #cmakedefine HAVE_FORK #cmakedefine HAVE_FTIME #cmakedefine HAVE_GETGID #cmakedefine HAVE_GETGRNAM #cmakedefine HAVE_GETHOSTBYNAME #cmakedefine HAVE_GETHOSTNAME #cmakedefine HAVE_GETLOGIN #cmakedefine HAVE_GETOPT #cmakedefine HAVE_GETPID #cmakedefine HAVE_GETPWNAME #cmakedefine HAVE_GETRLIMIT #cmakedefine HAVE_GETSERVBYNAME #cmakedefine HAVE_GETTIMEOFDAY #cmakedefine HAVE_GETUID #cmakedefine HAVE_IOCTL #cmakedefine HAVE_KQUEUE #cmakedefine HAVE_MAKE_UNIQUE #cmakedefine HAVE__MKDIR #cmakedefine HAVE_MKDIR #cmakedefine HAVE_MMAP #cmakedefine HAVE_PIPE #cmakedefine HAVE_POLL #cmakedefine HAVE_RECV #cmakedefine HAVE_RECVFROM #cmakedefine HAVE_SELECT #cmakedefine HAVE_SEND #cmakedefine HAVE_SENDTO #cmakedefine HAVE_SETITIMER #cmakedefine HAVE_SETPGID #cmakedefine HAVE_SETPGRP #cmakedefine HAVE_SETSID #cmakedefine HAVE_SETUID #cmakedefine HAVE_SIGACTION #cmakedefine HAVE_SIGADDSET #cmakedefine HAVE_SIGPROCMASK #cmakedefine HAVE_SOCKET #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE_STRDUP #cmakedefine HAVE_STRICMP #cmakedefine HAVE_STRNCASECMP #cmakedefine HAVE_STRNICMP #cmakedefine HAVE_STRSEP #cmakedefine HAVE_UNAME #cmakedefine HAVE_WAIT #cmakedefine HAVE_WAITPID #cmakedefine MKDIR_TAKES_ONE_ARG #cmakedefine BNETD_DEFAULT_CONF_FILE "${BNETD_DEFAULT_CONF_FILE}" #cmakedefine D2CS_DEFAULT_CONF_FILE "${D2CS_DEFAULT_CONF_FILE}" #cmakedefine D2DBS_DEFAULT_CONF_FILE "${D2DBS_DEFAULT_CONF_FILE}" #endif pvpgn-1.99.7.2.1+dfsg/docs/000077500000000000000000000000001462640461400151305ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/docs/adbanners.md000066400000000000000000000037411462640461400174140ustar00rootroot00000000000000# Ad Banners --- ### Configuration File The configuration file consists of a single array named ````ads```` which can contain an unlimited number of elements. Each element contains 4 pairs in the following order: ````filename````, ````url````, ````client````, and ````lang````. - ````filename````: A string containing the filename of the ad banner, should not include a path. - ````url````: A string containing the URL that users should be directed to when clicking on the ad banner. - ````client````: A string containing the 4 character client tag that the ad banner should be shown to, a string of "NULL" will cause the ad banner to be shown to any client. - ````lang````: A string containing a 4 character language code that will be displayed to users who have enabled that particular language, a string of "NULL" will cause the ad banner to be shown to any user. - Valid ````lang```` tags: ````enUS````, ````deDE````, ````csCZ````, ````esES````, ````frFR````, ````itIT````, ````jaJA````, ````koKR````, ````plPL````, ````ruRU````, ````zhCN````, ````zhTW````, ````NULL````. ### File Formats | Client | Banner Format | |------------|---------------| | StarCraft, Warcraft 2, Diablo | PCX, SMK | | Diablo 2 | SMK | | WarCraft 3 | MNG, PNG | ### Banner Dimensions - The dimensions for ad banners are **468 x 60** pixels ### How To Create SMK Files 1. Download the [Old Smacker Tools](http://files.campaigncreations.org/resources/sc/programs/RADTools.zip) 2. Run *smackerw.exe* and then click on the *Smack (compress) a graphics file.* 3. On the left side, navigate to the folder where your ad banner is stored and select it. 4. Under *Options*, click on the *Palette* tab. Check *Total palette colors to use:*, set it to *64* and check *Starting palette index to use:*, set it to *32*. 5. Set *8-bit input palettes* to *Create new.* 5. Click on the *Frame* tab and check *Create ring?* 6. *Smack!* ### Notes - It is not known how to create PCX files that will display properly in PvPGNpvpgn-1.99.7.2.1+dfsg/docs/bnmotd.md000066400000000000000000000014731462640461400167420ustar00rootroot00000000000000# PvPGN Message Of The Day --- ### Notes - All lines must begin with **%I** ### Parameters Parameter | Description ----------- | ------------- %a | Number of registered accounts %c | Number of existing channels %g | Number of running games %G | Number of games with the same clienttag. %h | Hostname of the server %H | Contact name (as set in bnetd.conf) %i | The userid of the user %l | The username of the user %N | Name of the game the user has connected with %m | Check the user's mail %r | IP address of the user %s | Servername %t | The clienttag of the user %u | Number of users currently logged in %U | Number of users logged in with the same clienttag %v | Server versionpvpgn-1.99.7.2.1+dfsg/docs/compile.g++.md000066400000000000000000000000751462640461400174570ustar00rootroot00000000000000REFER TO http://harpywar.com/?a=articles&b=2&c=1&d=74&lang=enpvpgn-1.99.7.2.1+dfsg/docs/compile.visualstudio2015.md000066400000000000000000000045041462640461400221470ustar00rootroot00000000000000# A step-by-step guide for building PvPGN using Visual Studio 2015 --- ##### Requirements - [Visual Studio 2015 Community](https://www.visualstudio.com/products/visual-studio-community-vs) - [CMake](https://cmake.org/download/) - [zlib](http://zlib.net/) - [PvPGN](https://github.com/pvpgn/pvpgn-server) ##### Optional - [MySQL](http://dev.mysql.com/downloads/mysql/) - [PostgreSQL](http://www.postgresql.org/download/windows/) - [SQLite](https://sqlite.org/download.html) --- ##### Instructions - Install *Visual Studio 2015* - Install *CMake* - Download zlib - Download PvPGN source - Extract PvPGN source to C:\pvpgn\ - Create a folder called *zlib* inside your PvPGN base folder ( i.e. C:\pvpgn\zlib\ ) - Extract the following files from *zlib128-dll.zip\include* into the *zlib* folder: - zconf.h, zlib.h - Extract the following files from the *zlib128-dll.zip\lib* into the *zlib* folder: - zdll.lib - Install *MySQL* if you intend to use it as a storage backend - Install *PostgreSQL* if you intend to use it as a storage backend - Install *SQLite* if you intend to use it as a storage backend - Create a folder called *build* inside your PvPGN base folder ( i.e. C:\pvpgn\build\ ) - Run *cmake-gui.exe* - *Where is the source code*: C:\pvpgn - *Where to build the binaries*: C:\pvpgn\build - Click the *Configure* button - Select *Visual Studio 14 2015* as the generator - Select *Use default native compilers* - Click the *Finish* button - Enable optional components if required ( *WITH_MYSQL*, *WITH_PGSQL*, *WITH_SQLITE3*, *WITH_LUA*, *WITH_WIN32_GUI* ) - Click the *Configure* button again - Click the *Generate* button - Close *CMake* - Open *C:\pvpgn\build\pvpgn.sln* with Visual Studio - Build the *ALL_BUILD* project - Build the *INSTALL* project - Open C:\Program Files (x86)\pvpgn - Extract the following files from *zlib128-dll.zip* into the *pvpgn* folder: - zlib1.dll - If required, extract the following files from C:\Program Files\MySQL\MySQL Server 5.6.23\lib\opt\ into the *pvpgn* folder: - libmysql.dll - If required, extract the following files from C:\Program Files\PostgreSQL\9.4.1\bin\ into the "pvpgn" folder: - libpq.dll, libintl-2.dll, libiconv-2.dll, krb5_32.dll, comerr32.dll - If required, extract the following files from sqlite-dll-win32-x86-3080803.zip into the "pvpgn" folder: - sqlite3.dll pvpgn-1.99.7.2.1+dfsg/docs/fdwatch.txt000066400000000000000000000172471462640461400173240ustar00rootroot00000000000000 PvPGN Abstract socket event notification API ie fdwatch v.01 (C) 2003 Dizzy <> 1. The problem: I wanted to design and implement a general API to have PvPGN inspect socket status (read/write availability) in the best possible way that the host OS may offer (select()/poll()/kqueue()/epoll()/etc..). Also the old PvPGN code to do such things was doing them in a very slow way, especially if the system was using poll() (which was the default with most Unices). When the server started to have lots of active connections the CPU used in PvPGN to inspect and handle them was increasing very much (the code complexity of the code executed each main loop run was of O(n^2) complexity, where n is the number of connections to the server, and the main loop is cycling at least 1000/BNETD_POLL_INTERVAL times per second ie at least 50 times per second). 2. The fdwatch API: I started by reading the fdwatch code from the thttpd project, I used the ideeas found on that code as a start point, but I got much far from those :). The fdwatch API is described in fdwatch.h as follows: extern int fdwatch_init(void); extern int fdwatch_close(void); extern int fdwatch_add_fd(int fd, t_fdwatch_type rw, fdwatch_handler h, void *data); extern int fdwatch_update_fd(int fd, t_fdwatch_type rw); extern int fdwatch_del_fd(int fd); extern int fdwatch(long timeout_msecs); extern int fdwatch_check_fd(int fd, t_fdwatch_type rw); extern void fdwatch_handle(void); The name of the functions should be self explanatory to what those functions do. 3. The changed code flow: A. the code flow before fdwatch - main() calls server_process() - server_process() after doing some single time initializations, entered the main loop - in the main loop after handing the events it starts to prepare the sockets for select/poll - it starts a loop cycling through each address configured in bnetd.conf to listen on them and adds their sockets to the socket inspection list - after this, it does a O(n) cycle where it populates the socket inspection list with the sockets of every t_connection the server has (read availability) - if any of this t_connections have packets in the outqueue (they need to send data) then the sockets are also added for write availability - then pvpgn calls select()/poll() on the socket inspection list - after the syscall returns, pvpgn cycles through each address configured in bnetd.conf and checks if they are read available (if a new connection is to be made) - pvpgn doesnt want to accept new connections when in shutdown phase but it did it the wrong way: it completly ignored the listening sockets if in shutdown phase, this made that once a connection was pending while in shutdown phase, select/poll imediatly returns because it has it in the read availability list and thus pvpgn was using 99% cpu while in shutdown phase - anyway, after this, pvpgn does a O(n) cycle through each t_connection to check if its socket is read/write available - problem is that when it was using poll() (the common case on Unices) to check if a socket was returned as read/write available by poll() it was using another O(n) function thus making the total cycle of O(n^2) - while cycling through each connection to inspect if its socket was returned available by select/poll , pvpgn also checks if the connection is in destroy state (conn_state_destroy) and if so it destroys it B. the code flow after fdwatch - I have tried to get every bit of speed I could from the design, so some things while it may look complex they have the reason of speed behind - just like the old code flow main calls server_process() - here pvpgn does some single time initializations - different than before, here, in the single time intializations code I also add the listening sockets to the fdwatch socket inspection list (also the code will need to update this list when receiving SIGHUP) - then pvpgn enters main server loop - the code first treats any received events (just like the old code) - then it calls fdwatch() to inspect the sockets state - then it calls conn_reap() to destroy the conn_state_destroy connections - then it calls fdwatch_handle() to cycle through each ready socket and handle its changed status This is it! :) No cycles, no O(n^2), not even a O(n) there (well in fact there is something similar to a O(n) inside fdwatch() but about that read bellow). FAQ: 1. Q: but where do the new connections get into the fdwatch inspection list ? A: they get in there when they are created, that means in the function sd_accept() from server.c the reason is: why add the connection sockets each time before poll() when the event of having a new connection, so a new socket to inspect is very very rare compared to the number of times we call select/poll). 2. Q: where are the connections removed from the fdwatch inspection list ? A: where they should be, in conn_destroy() just before calling close() on the socket 3. Q: where do we manifest our interest for write availability of a socket if we have data to send to it ? A: in conn_push_outuque. the ideea is if we got data to send, ok update fdwatch socket inspection list to look for write availability of the socket where we need to send data 4. Q: what does fdwatch() do ? A: depending on the chosen backend it calls select or poll, or kqueue etc... For some backends it has to do some work before calling the syscall. Ex. for select() and poll() it needs to copy from a template list of sockets to inspect to the actual working set. The reason why depends on the backend but it really is a limitation of the how the syscall works and there is nothing pvpgn that can be made to not do that. For example in the poll backend, one might argue that instead of updating a template and copy it to a working array before each poll(), we should update the working set. But that also means that before calling poll(), we must set all "revents" field of each fd_struct to 0 , and my tests show that a cycle through 1000 elements of poll fd structs setting revents to 0 is 5 times slower than using a memcpy() to copy the whole array from a source. 5. Q: what does conn_reap() do ? A: to get the maximum from each possible backend (kqueue was the main reason here) I moved the cycling through each ready socket and calling the handling function for it, outside server.c and inside fdwatch backends. Because the old code used that cycle from server.c to also check if connections are dead and need destroyed I had to find another way to do it. The best way I found was to have in connection.c besides the connlist, another list, conn_dead, which will contain the current connections which have the state set to conn_set_state. Then conn_reap() just cycles through each element of conn_dead and destroys them. This was the fastest solution I could find out. 6. Q: what does fdwatch_handle() do ? A: it calls the backend's handle function. To get the max from each backend I had to move the handling cycle as a backend specific function. In general this functions cycle through each socket which was returned ready by the last fdwatch() call, and calls the handler function (which was set when the socket was added to the socket inspection list) giving it arguments a void * parameter (also set when socket was added to the inspection list), and the type of readiness (read/write). Currently, pvpgn has 3 possible handlers: handle_tcp, handle_udp and handle_accept. Each of this calls acordingly sd_accept, sd_tcpinput, sd_tcpoutput, sd_udpinput (UDP sends are done directly, not queueing them and checking for socket readiness to write, maybe this is another bug ?) pvpgn-1.99.7.2.1+dfsg/docs/ports.md000066400000000000000000000026321462640461400166240ustar00rootroot00000000000000# PvPGN Porting Status Arch | Operating System | Status | Compiler | CMake ----- | ------------------------- | --------- | ----------------------------------- | --------- x86_64 | Arch Linux 2015.09.29 | OK | G++ 5.2.0 | 3.3.2 x86_64 | Arch Linux 2015.09.29 | OK | Clang 3.7.0 | 3.3.2 x86_64 | Arch Linux 2015.09.29 | NO | MinGW 5.2.0 | 3.3.2 x86_64 | Ubuntu Server 15.04 | OK | G++ 5.1.1 | 3.3.2 x86_64 | Ubuntu Server 15.04 | OK | G++ 5.1.0 | 3.4.0-rc1 x86_64 | Ubuntu Server 14.04 | OK | G++ 5.1.0 | 3.4.0-rc1 x86_64 | CentOS 7.3.1611 | OK | G++ 5.3.1 | 3.6.1 x86_64 | Fedora 25 | OK | G++ 6.2.1 | 3.6.2 x86_64 | Windows 10.0.10240 | OK | VC++ 14 | 3.3.1 x86_64 | Windows 10.0.10240 | OK | Clang r249555 | 3.3.1 x86_64 | OS X 10.11.6 | OK | Apple LLVM 7.3.0 (clang-703.0.31) | 3.6.1 # Notes - Any POSIX/WIN32 OS with a C++11 compliant compiler should work - The minimum required compiler version for G++ and Visual Studio is hardcoded pvpgn-1.99.7.2.1+dfsg/docs/readme.md000066400000000000000000000011021462640461400167010ustar00rootroot00000000000000# Index --- File | Description --------------- | ------------- adbanners.md | Instructions on creating SMK ad banners for PvPGN bnmotd.md | Documentation of parameters for bnmotd.txt compile.g++.md | Instructions for compiling with G++ compile.visualstudio2015.md | Instructions for compiling with Visual Studio 2015 fdwatch.txt | Documentation of the fdwatch API ports.md | Documentation on tested operating systems, compilers, and tools for compiling PvPGN readme.md | This file storage.txt | Documentation of PvPGN's storage systems pvpgn-1.99.7.2.1+dfsg/docs/storage.txt000066400000000000000000000163311462640461400173410ustar00rootroot00000000000000 PvPGN storage configuration README File ----------------------------------------- 1. What is this "storage" ? "storage" is what we call in PvPGN the way to save account files data (usernames, passwords, many other stuff). Thus it is a very important part of PvPGN and can drastically improve speed of the server depending on the chosen method. Because we wanted to be easy to add new storage types we designed a modular solution, like this: (mostly in bnetd/account.c) | | (mostly wrappers, in bnetd/storage.c) / \ / \ / \ (bnetd/storage_filc, bnetd/storage_sql.c) /\ /\ / \ / \ / \ / \ Thus PvPGN can be compiled with support for more than one storage type but (of course) at runtime it will only use one (as chosen by the server admin from the configuration file). 2. First you will need to make sure your PvPGN version has support compiled in for the type of storage you want to use. 2.1 file (plain and cdb) "file" storage support means to store accounts data directly in files in either plain text files or cdb files (but its easy to add any other support in the future) and is compiled by default, chosen by default (with the "plain" mode) in configuration file (and the oldest and most used storage type). The only advantages of the plain files storage type are that the account files beeing human readable text are easy to read/write. It has the drawback of beeing the slowest one. Starting with PvPGN version 1.6.0 (and their prereleases) we offer "cdb" userfiles support. For who dont know, cdb is a constant non-relational database. We included and adapted code from the tinycdb project to achieve this. Because cdb doesnt have any external dependencies and is pretty stable it is (as the "file" driver) compiled by default. It has the advantage of potentially beeing a lot faster than "file" storage driver (I say potentially because at this moment there are other things in the code which make cdb slow almost as plain files, but this things will get soon fixed). It has the drawback of having to use a separate tool to read/write this files outside PvPGN. 2.2 MySQL/PGSQL For unix if you compiled your own source you need to add --with-mysql or --with-pgsql to configure command line (read INSTALL.unix for general UNIX installation details). Even if you give configure --with-mysql/--with-pgsql parameter it will NOT compile MySQL/PGSQL support if it doesnt detect the MySQL/PGSQL includes and libraries. Check the configure output where it detects the mysql/pgsql paths, it should find the right locations of your MySQL/PGSQL includes and libs. If you got binary unix packages you will need to know if the packet you got was compiled with MySQL/PGSQL support (ex a MySQL rpm file should have "mysql" in the name of file). A more general way to tell if you have a PvPGN with MySQL/PGSQL on Unix is to use the "ldd" command on the "bnetd" file (the main PvPGN server daemon) and see if the output shows usage of the SQL library (libmysqlclient for MySQL, libpq for PGSQL). If you got a binary Windows package/installer just check from where you got it if it tells you if it has MySQL/PGSQL support or not. No matter on what OS you are and how you got PvPGN, when you run PvPGN it will log a message like this: Nov 26 16:07:20 [info ] storage_init: initializing storage layer (available drivers: file, cdb, mysql) If in the list of available drivers you see mysql/pgsql then all is fine. 3. Even if your PvPGN has support for your chosen storage driver compiled in (you see it in the list of available drivers from above) you will need to configure PvPGN and tell to use your chosen storage type (default PvPGN uses plain files, the "file" driver). You will need to locate the file bnetd.conf and edit it (note that in the configuration files all characters that follows a first # character on a line are ignored, they are comments). Find the line storage_path (which is not commented) in the bnetd.conf file. Edit it to use your chosen storage type as in the examples in the config file which I paste them here: Syntax: - for plain file driver: storage_path = file:dir=;clan=;default=/path/to/default/account - for cdb file driver: storage_path = cdb:dir=;clan=;default=/path/to/default/account - for sql driver: storage_path = sql:variable=value;...;default=0 (0 is the default uid) The "sql" variables can be: - "mode" : tells PVPGN the sql mode you will use (mysql/pgsql/etc..) - "host" : the database host - "port" : the TCP/IP port if needed - "socket" : the UNIX local socket if needed - "name" : database name - "user" : db username - "pass" : db password Examples: storage_path = file:dir=/usr/local/pvpgn/var/users;clan=/usr/local/pvpgn/var/clans;default=/usr/local/pvpgn/etc/bnetd_default_user storage_path = cdb:dir=/usr/local/pvpgn/var/userscdb;clan=/usr/local/pvpgn/var/clanscdb;default=/usr/local/pvpgn/etc/bnetd_default_user.cdb storage_path = sql:mode=mysql;host=127.0.0.1;name=PVPGN;user=pvpgn;pass=pvpgnrocks;default=0 storage_path = sql:mode=pgsql;host=127.0.0.1;name=pvpgn;user=pvpgn;pass=pvpgnrocks;default=0 3. Start PvPGN. If all is well you should see a line similare to the one bellow in your pvpgn logs: Nov 26 16:07:20 [info ] storage_init: using storage driver where is your chosen storage driver. 4. Troubleshooting: Q: I am compiling the UNIX source and using ./configure --with-mysql but somehow I dont see from configure output that it detects the MySQL libs and includes A. Did you installed MySQL devel package (on some systems is separate from the main MySQL package) ? locate the following files on your system $ locate mysql.h $ locate libmysqlclient.so $ locate libmysqlclient.a If you dont have mysql.h and one at least of the libmysqlclient.so or libmysqlclient.a then you should install them (ask your local linux community how). Q: I dont see mysql in the available drivers list in the log ? A: That means your PvPGN has no MySQL support compiled in. Go to step 2 of this document. Q: I see something like this in the logs "no known driver specified(sql)", what does it mean ? A: Just like the previous question, means your PvPGN has no MySQL support compiled in. Go to step 2 of this document. Q: I see something like this in the logs "no known driver specified()", where is not the driver I want, what should I do ? A: Somehow you did not configured in bnetd.conf to use your chosen driver . Please see step 3 of this document. Make sure to have a SINGLE UNCOMMENTED storage_path line (a SINGLE line which starts with storage_path and which DOESNT have a # character in front of it) Q: I see sql in the list of available drivers but I get an error "error connecting to database" in the logs. A: You did not configured properly the , , , from step 3 of this document OR your db server has a problem (is it started ? is it firewalled ? did you gave the right privileges to sql user ? etc) pvpgn-1.99.7.2.1+dfsg/docs/versioncheck.md000066400000000000000000000035721462640461400201440ustar00rootroot00000000000000# VersionCheck ### Configuration File The configuration file is in standard JSON format and consists of arrays for each Battle.net game client whose names are a four-letter string. Within the game client arrays are arrays for platform whose names are also a four-letter string (i.e. `IX86`, `PMAC`, `XMAC`). Within the platform arrays are arrays for the game client's version ID, traditionally known as a "version byte". The version ID is traditionally written in hex format, as indicated with a preceding "0x" or "0X", but can also be in decimal format. The version ID array consists of two pairs, `checkRevisionFile` and `equation`, and an array, `entries`. The `entries` array consists of five pairs: `title`, `version`, `hash`, `fileMetadata`, `versionTag`. - `title`: This is to assist the reader of the configuration file, it does not affect the entry in any way. - `version`: The version number returned by CheckRevision which is obtained from the [VERSIONINFO](https://msdn.microsoft.com/en-us/library/aa381058) resource of the game file. See sample implementation: https://github.com/pvpgn/CheckRevision - `hash`: The hash returned by CheckRevision which uses up to three files from the game to produce the hash. See sample implementation: https://github.com/pvpgn/CheckRevision - `fileMetadata`: The string returned by CheckRevision which consists of the game's filename, last modified date, last modified time, and filesize, all separated by one space (e.g. `war3.exe 08/16/09 19:21:59 471040`). See sample implementation: https://github.com/pvpgn/CheckRevision - Note: This pair is currently unused by PvPGN, but may be used in the future. - `versionTag`: An arbitrary string that must be unique from all other version tags. It is traditionally in the form of the four-letter game string, followed by an underscore and the version (e.g. `WAR3_1282` is used for WarCraft 3: Reign of Chaos 1.28.2).pvpgn-1.99.7.2.1+dfsg/files/000077500000000000000000000000001462640461400153025ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/files/CMakeLists.txt000066400000000000000000000027021462640461400200430ustar00rootroot00000000000000# these lines create the empty directory structure pvpgn needs installed # in ./var and then installs it portably using install(DIRECTORY...) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/users") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/userscdb") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/bnmail") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/clans") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/teams") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/reports") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/chanlogs") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/userlogs") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/charinfo") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/charsave") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/bak/charinfo") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/bak/charsave") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/ladders") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/var/status") install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/var/ DESTINATION ${LOCALSTATEDIR}) install(FILES ad000001.png ad000001.smk ad000002.mng ad000002.smk bnserver.ini bnserver-D2DV.ini bnserver-D2XP.ini bnserver-WAR3.ini icons.bni icons_STAR.bni icons-WAR3.bni IX86ExtraWork.mpq IX86ver1.mpq newbie.save PMACver1.mpq ver-IX86-1.mpq XMACver1.mpq DESTINATION ${LOCALSTATEDIR}/files) pvpgn-1.99.7.2.1+dfsg/files/ad000001.png000066400000000000000000000062721462640461400170440ustar00rootroot00000000000000‰PNG  IHDRÔ<P%Ü`tIMEÞ ::ô«1O pHYs ð ðB¬4˜gAMA±� üa IIDATxÚí�=lÛºÇõ^[€Bi)¤Ñ] gs¶ÅrÎcò˜·L§F;;Í´çç¾ß÷}™ÞwœáÖÖêÚm†u‚qò³ë8;®»gÛ¬†Ù ìÑH&ŒÝ¶Û™ooÃð<®’¦®OÏq¹†rG£²§`£ÑØÙÙY¬ t7¯¯¯éØjµšÍæbF|ß?™¬ƒô;i·ÛŽãÔ1âyÞÆRE@|׊ÅXD·ãºÙau㸥)&©g¯Ñ8ô ½ÜÕ¨UͲº®û§ëZ+°Ÿ¯ZÛ¶éŸßh�þ~ CŸó ¯;Š˜«¡H|ÕsQ=ë|$É#ñ�zÛn÷z½,Ðq4éºI'IÊ÷öö:�NMkt‡ßB ¾kE©ýð�Fz� :ò¼«ím¦e#yº ‚Q¥\Ò!/!;–E’­;LÿŽ=O—¾š±´o…�{‡®KG9¤XE‰ey¨Ä£Fã½ãxœËVõëIpþÆÍDŠš—Ìõñ±,X–u||L‘¸mÛz¶êË ÇR@‚9ßµÂãØHkåãã8ß�o¢èÌ÷ÕWÓÞNk³©Kí¡ï7G£ÆxìŽÇtlŒF]-H±DØËt·ãøsŒÓò½ÂzÉc‰¨‘ð]4›»®k›&WÕ\Ò“¢–¬ÈIE·lm}iµND[ÍøäŒTçÏ Ùº¹°ý$)ïîî.)ï—ƒBù®�iØKQìþ¶ Ef/ƒ cYÛÚ×0(^û˜L×’Kã^ÝòS˜»ŽÓ¤þ#-ÓÑ4)¸»‚b-\Q½Œi'Ο۷¬S)v²\Š|•Kz’¾œÔœUä”2qìŠ)Ýéô·ï_DQéDDÆ7zlÌ®ý´|ÏóºÝnuÎf³9 b¤¢«¡’ÂÓÓÓápXm¤Óéœ��iélµZoß¾UgÂ0$;£ÑhR4ñýþýû~¿å� Äw­ðtßûEWa¸ÀÐØ1Í?i¬­ý¾ õ†´’ô—z£>eK^3v7kŒì0Ö#̇|"ì½Ë…½ù©bræj2ñ„lÍ[»¶mÓ#c?S =N’Éå›JÞŠò2H¢àæpRåW4SR®¼Ô\Ǯ۱ía\å*o*i¢2¤tòzºÎ~[Ljʬº½½ýêÕ+y’øøñã(Y“¬62o¹Äw½¤{‘Çùùü‹æ†X…ï0æÖÈùy2Ù·ím­ÐU]×õ|¿ºCw)dN÷ÀÀ0ÈÛâ«Òõ¢$Ó7µ—¡tHw;éîš·BþÛF�(“‡žç‹õC£Z4륓ÙP)ƶÅZY3NóÍ•³VǺRÕê<ívÛu]™3ÐWDÓsÕìïï+Ñ$#rÝŒbjuòû÷ï···3�©_âÆñ]ô;Ž3“(Õ\N¸°£¬‘õÂà—¢¯ßŠ©^•™º×g˺) Í$$î=ÛNÙële¨ÅIM§s ‰o¦^f.°&ßZ¹<” ‘~×÷k®zÍE¦v¤ìV¢)fî[SÜúºçºtr0™¨F‹ÅW™K*"_©¹¦iÆI|MÊØív ÷¼|ùòÍ›7”“ò‡aH‘©j7SÌ�(#>| H¶p‰¬\O£ÌQ�ÇcåŒ2òíÛ·êæ’9•Ûạ̈ߑ߈ïzÑ$ršXfvRt-u=ݾ0#E 4 >bzc}ÛîEQaÔM±ç Pio¯8•ØWkbé¹ÒE0Sn¾�Ý eÏä¡:®Dy›ª¸�*·m»)ž,ý­î0�9,Ó<Ö•U+Cm0 À¶�Ûw¬#…ïZpßžê9aš2=Óˆd8ÞÜÜä�üý÷?Õ¶Z-¥¿tœ,4¶Û ¾ë#N" Ù—�í0FÃËÌeEgN&Û¦ÙR¥SŒÃXײN‹‚ß]ËjÊ€(ñÓëlÕ.©Ì¼d¿p¦Ó)b ª¢•Hy-Ñ’Fºú· *[2S‘k­Y2ÎpÑwœ_E=¥¶,‹†­´3z¢ÃØÀqŽ“ù^”­¥˜5òÔ0gggççç‘V…²ÍeÐø¡ßïgfuÕ%/^TýMŠÍwwwUfògæÅÆñ]/š,é�¢9SŽÓgb¹‰:R†(j‹¸R-ë“äüɘ—îF©5Ó±gÇW³t-Ði#kR¥¡ëÖR™’¨Ð�ã/™Þ+Î䜶í³Ì¥[Ò#€²—>¨å)¶¥¦£AC;ia½Ôì¤Î¡öÀ˜÷ ‹û›(˜·‚™87³ÛìÓ§OƒÁ€tSæ¤ÄÍÍMF1)ÚUFšu+Ê‚h²0,pG6¼d±Vî÷ÕË�üú„/uKµ�Vþ+Ió䣞 `m0+¾ˆÄ;ÇêB.¬ul»­u «ß'{yRôy½w·T~®*˜>_U/uUºvaŸ—LL§ŠSm¸é6IÃ3.ñ’iúédçÞž‡a�®”¼¼KºfY·¯Ò5%mÏž=[¬rϟϰ(^><Ì“7Т°’´‘|T‰[êóõ†vÓ�mQD!ØÃöÒ8î16NbÉ#˲“ó* ¾®©¼™ze^¤V{~ ë¥.׊Žãø”õ…{³Ò-0ý¸èþ ®yxïCnÛ\¦Á�ÊézÂùŒM[2Ý&”Ø*²V“'jÇØÏŸ?ç­š$¤‡�0bÒ€¦Äsß÷���å–2úøÇí~¿¯ÏH#jƒ¾C"Ï�ýñx|yy¹@x¾�@|×ÇýO_M¨2ë�AžM¾åBJògôDHÑ–a\Ì3©vÁùç[š‘cŒ�r¾+6¨*q¡ ç5lë—<$–¨%n£èBìÓ(mIÍν.dUx^xã›Ò#Œ"jÕ-5 ®¹šIT8MöúõëE*¥q*˜™�ôwoo¯¬µ)4^Ò �ÓkEnÚ—1CFyõ“ñ¬3É|¦.!"Ù¹6ò�­!ICÚìt²2yµA�§ż~m˺‡:X¯:ê 9Faø ŠúœW¿£‘±³Ìà6ëL¦ÜŒŸœÛ5^¸Ñq�*/á8øÿ‘ïú�qS¬åyŠ-ûù>¿2HÆBj•'4ÞÆ ×g]øRÝå)÷Õ]°}q�½éÂqœ·\TÃ…7LøØ…oºð„ º4_uaºîr°•p}bÙïîÿÞDÒw~VwùCDf™í@üœ8Uf�ÓÒÌ{3ÖkpªÜë-^çiã¿íÂUé/ u—¤`†:$œbO±#2[wy6±ÞìH‘Ï-ûÝäääUùø¤ ÷‡®wi.¹?_vá.üÓ…¯¸ðüNpª¼ÌíÂOv’¾”ÁÉ£áÂ/\XâÏu—§FÞuáë.\vr8`â¹�¼&Ÿ/×]H°q ³oïµl>ϰSð�Á€ùêll¥ ݞͪë¨Ù1ÝÈ,uª‚\:¼4,i— ÒòàåËRòŸq=ç†�Ǭ”µ-ò²\eù·"ªò™Ûs¿ §ûìGê.kMò™•úŸ•ÁEKtGõç¶ù¿|¸¸]u×cr9$õe™üX`MÕ%0qOHŸ°�Ü„ûd–óŒ}ÕѱÍË:íHÚÙaéºÅîH½c�ÚÊe.'¯†Éëd�ì.¸ð²¤]”{Œd6£ûÀ%Ó6üŽ-åå¥ì�¤¼<3�‘ï¦ÅPr½÷�¢ü[J¶–Ïã.\’ÏÓ’æ¸|ß“öÞvÎ!"uœ\çOE«”OGk ¿éQ8G( Ðu1Rÿ»¢'Û~e«1B÷”bîµâýŸ1ö:Ò#ºR1oÍk­„󸽤hœ …´]Ûvê¹ÊÔä”Èý^£d&Û3‡ª¶W…:–p;žÕ�…÷]�Ò™w#'ŸmåP)Y=ø–êyà{k ߥð`L"·ÆÙ1””ë~7à,؉0é$þ„|^±‰ù6¤ ¸-ÆjuÌTkVúÑq«îï.=a°§,�éå¤ÙíýFiàY‰4’Ž‚o›ÆÓO¥½-¹f±Œó�²_‘ôc=ªò nyßëe}àâ}¿Û|~ÖJ"÷bãzÁ„¶8ó®|©<)Ý+?ì¹çuóÝA“–ËõªÔinØ�¶À¾=%³ˆ¥Ëp )ןŒ±o®³�8TÎëuIsu;̈Œx°qoÉ×Ä�¦dE�áJ{½kB“†E ³F®K4„­®ëú ¶7¥[!Ó�t Ë¡�“_ÛØŸ�¼ï¾.aÌD¾[¾×å {©â=šöod÷‘”a`ƒGé~áºóÈI;362ÀØeef%ºœÓ�ÀË~ï‡ä'ò}UÊyÊèBCkO®;P¾ô…uã²/zqZ—ž„‹9×kZÖ‰åâ;V.ŸæÏÎ}†6ù ¥3pËuJfU=1„D�}n_'rîÁºÕ1s~ÀrŽÕ³Î”Xûз" ݧ òÉœc dH’÷‰Aúüê´ß´%IYÖí õ¶-܆1ÿ ö§C9ŽwÇ FùT代"¬ &îѶŰ4­Sò;®8“ïQ X¡Öy¬äQ 8TJ"|Héaƒ¡J—øq•sr¯·)Ð ¤Œ¾Ñ |ß4rîIÝ×Lg9jòP‡Úñò8,ñ/GÊÀ³¢—†%+É³ÏØ{ŽànεÖ�gÜC(Ÿæ¯+#gEŽÜŸ†²• y-h�¼ú©3mSÿ³’3FN‹ž�iÈs¨Ú§™nUuéKÉ@mÉØ† /}’Ùß6"ó×m¿ô‚N®•ÌOõD�öð׋£¨C  �Kûé@¸eÊCbüÉ…íky”6eWÉvö,Õ à-Šœt4‚Z0qS�Ü¢,§(ë¬BÆ1ã‡T—RF”<‡JÉ2Qzx…ù°j§5h.ð�&ݾ`âu_¦T͹'Qñ3©\ïóòw…²Žµc>Ïšë§LüBN9£ M~!�iRêÀŠö„K§°|œ¿î'_72:lÒì’t…NÄ¥ÙMæ€%ŽHùŒÿ7iµ½;9ùÙömFÒôÉ8�¦M9¶œëØØ}v¦HW8ø äDi[ìÌeÊ]¡œÜŽQ|ÕÒ1iºTÂ�PÖ1=MÉIÙi“—.WÚB�~Ý0ýŸÃ Ôo›Ù¾¬®oyuËÝÈ)G‹0K]†5–3Þw:cëš8�©Ì™¸ ”5À»÷ 6 Q¹S¨êÃùQêw¨úÿ5J÷”R bî¼Î(Ý…Àµ2T°�A��%o�áz³áÖåAîà ‘¼‹ ïPG¦¡{R…G�ü|Ä` í¥³~–_KâÞ²FR➈ÉFêrÖ“3ÿý�Ê”’½º.eW|töÔÌ)ß²MGáA^©v¥œ[Î5êL¯ÉÿÚÇ2§†)9Tæï/.šïµªŸ½2å®ÐŽë"MœÚ>.˃·Bý¬RêÌbK¢Ö––¸]F¦çMZ®Û|N>j¯ŽÉµ“·žÂe}ÜcäÕÊÙþ ”³Ô2H£t#ñÌiÇ·{¡g©Ä,.ֈʟB-Y—R×(ÑœüÿНT¢Œ-*Þ'át<³='å tŒ»^:V´ÕH…J[P휊/û”^5R—¨„ÛÞHÙ¥„Îpî�=æ¿?RÙì£'Ç%nŠƒ&÷ùf$5v‡<9ÿ(�–�ÕKæÿ l¼k¦M¯Px/µ”ÞQÖÙž/ dA”]fäú²s¼hÒ©¹-�˜òü\¾GtÓ&´=‡âP#å_’[pÿŸõ¾;-ß½eÊÛ‘´‡yEí‘‹{Óö7Jô| �êÊÓÿy4íÝÕv’¸�JèI©AW^>„YjF<ÚézqÓÆÈΘøõµvn\Ï ´(2Ë4mZ§×‘¾UuŒMìįvÒåHþÎ .È5•–-¥œ«äÍ:Ýÿ?ôë›§he”¶ mÊ!R‰�¥ûºÏò&ÁÁž‚:åA÷FÅñ€é ÿE"¡òñLaI>ï—d �tªô÷Å@<—±cþW9ž÷Òi|è#‡7ÒO*´ñë"W;�ÙKé¬îÍyèÀ ÖßÖí¥?Váo¹ð3Àv°¡íjKvÞvk`EÒÚC¥þ ë¨I—7KmR�]$Ê?˱#�Îf•œ;Ï]ê]²Ð Ÿ?�u΢N;ä:ÙÃV¾‚ªqñg¨öðÃù’÷‰ ÎXƒ[Æ™{t8‹’G%¢øü7´T=°Eé£Öp^™2DÊ¥÷ÕNYª~”¿¬:mêZé1 J«më¦èËT$}è�]3ÖüòR²wÝüÏÁßbQýxÉ‹·ûb³�{M«Ñ7ò˜¥t ÀKÎåú¾s’Æn¯ÍRsuؤ]‹æÌY Š/‡rüí@¼êãf8T=¬äoŸèÊ϶�ä¡:u¨æóú¾­‰ãˆ¿’ѦÀénJWÁBµÏ$ècq$z¥+üYg©»'"Py‡Ú)£'ÛÊ>¯¦‡XOÒG/æ{nP݇ 5x3ïúÔM;8Gé¾ —;jH×ð‰Jž=ô¨ø8üJÄØëÛ§üý±\cD%F€ê>Gƒí£Þ¡ì©ní¤TÕq™<>Y;J�Ç£Îj…Ì»}½tóÔ¿W�ºð]Jö¤ä­ú‰MÔSÃü¨ÓÀ�¿I>Ú§vžC!¨ãi‰ñ¸HZ&=#Fi…²\Úˆb½”³gåä‘åßM9CËu<îP2C?S�¿Ö{ÞÄ}AîwÃKÛgŒj’‰vð˜CÕÎ`—˜¸=/–¿Ëx úñõïÙÛº-‰³Ï–®Ÿð äÙoœ"�ûƒÕ~öù�@Š´(y…g[ê3CÙ}I%W—KÊ«O‡)yä¥éÙM·Ï»>£{”³JÙ·3ñ,í;"‡õC4£ÓŒB9°œyæÏƒª…‚´ÁþF�Á…Ä«®ðž¿>y`_ªúÈðÁǃ’ö"Nè”M�¿›ÅÛm‹e¯�ä©«dWê¶uµaIAd)�F�‡®÷«BèÛa8ð’Å–ú}IJö𔫔ž&¼’sM£L§¥t‰æ¥‡˜‘<›”³œ²IòÈ}x\tÁß;gÿiNžµÖiƒòØ“§¿Æ9øË”}íœc<ÙÈÜ4zz¹-Nd–ÒWR¶‹ ¶\«zÈ\¤×s{e/KÊ¿è9T­³ÿ´UêŸ]ê�¤¶wí\ä^=J—!ß6ñLw¢F(¸=R§u<îÅ³Ý -us{=OéJÆï{ÕÇ÷)] ìé e]jË-ë¿Â4DÙìÌ=TÂ-J××gßk#èÞÌXüú%#É6¥Gðùà“Â÷WÔ?c(ãPù�-:ãèÊ}†ú¶ ÊeO$¾Iáý½‘ô,ß×&¶)”.…ùU²Ê.êìÂ_¢Û'ñº´7pŸ.m`DOÉ,e(±à\w;cR‡aû“¾ZïQg÷‚æÚ¼�í ö¤:”袶2ÑA<;Ký1ƒ‹ž¸ð,ñŽèJè­[dSê0J]G)ûÛ®íQëÍŽƒÒgå˜Ø;}gÇÁ�FÊÞ ô—8üGx¿‡—Ùì›Z¢ûÀ‘üÇþ·UM]ÔèÙƒÚ‰õ�¬M •AØ–Z•²<ä|#ÉL~°__#§2êQÿa>}t†y8p�–ɺë;€|¦}§(qü–¤ÇŒ uàiŸ¥,ó“‚»Lm#Ç-ÿShd–´)}iëL‡"¿JÉ^'s+’§Ê ò³Ô4Æ+Ic…1•IbÜ ïçŠ(¼ß¤Î£ö}àär )üŽÓ±ú‘�ÈÄ|¶F²íÉ£K”ûŠÁØ)bÍgjßA&‘ѧ}yÊKÓ¤øÖòOò›²6¶…u…—Au•¬›7-� ëJ­ËÝ[•ɺ ’Ñ´ûó}¾(Qÿqá•ÉÉÉ÷ë.ÛVC:9ïÕ|Þ…ß9 ü(͸"Îã ž‘pDäñ± q2ùlÀ<á|Ýõ…Ï6Žž®pŸú³«Û'®ç™ìMÌeþæòù_Ýõ+YÕ•¯¹ð� |à‡û Û— ˜'¯š½ç®¶îúÀ† Ès“øPÁñùM ²Ô [Š<7 €O™=b0<î«»€òÈè3u—l}(99ûž° |®î*q¿ ¿žHöKÈãß.|099ùtݰ­ø?è(ÇPZÅIEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7pIDATxÚí�ˆ]GÇ_¤1²RR ÛJ$l(”èÖ²"‰µ¥ðZûG!VÖB¨H_ ¥Áü³­„Dkáµ FB1C¤R|)B«ˆBl(Š›Bh\ZšÖÄbÌKµ·Ö¬bš˜*çôžóî¹³3÷Þ}?öî�ï†÷ÞÌܹóóÌ™Ÿ¯V°4!¢Ï®îÌIg&«ŽK ×nî•v5ḛ́ç6Zuü}D{“ÚüÝs“NÕ,%pmåˆ33žÝjiw/:s ê8‚Zí#UG`1Áš`ÕqXЬZµê9ùzÈ™ ÷SòõMù|W¾ße:×zÕé`³Ù™×\;¹ÕØ 9sЙ­Î\vnUGr¥³¢:T™2™ôìntf„GVîç7«Žãæ�3çsÜÿéÌ#òý¯Î¬sæ˜v;&ëXÕ ±„êÌ<ž]±•{c¾a.G$/êò}Ø™�Ü.«Ž×bA¦uopæÏÎt”~§¨þ«–t´5±ÿdÕq] üX-õDgÆv9³i1Ô™kªŽÀs—oá*åŒ4æ Μwß¹‚yÞ®c73ÒÊEÖ9>� ¿"pi}Û¥ÛæÁÛ÷S’Ï<2ý¬3ÇœÝÝÎn¿ æ Î<íì~å>Ÿsî¯:]µ@�ñqñrqåÂH--°¢úéZRŸÎë^㋵¤~qÚ^2Óãï;óS›gËI7·‰óÎ\”ë1ñw‹û`…ŠÛ Ì�¹ÏOHÞüÐ=wµê´,P~±¢µÁX±Lú‡3_w†;�õ./þ"nwÕ’�À÷œùVÕqP~°bÊ2ãig>pæAgXɽÁxû“3÷p§Ê2½ê8¯"#T�î”ÑѹÀ:ßIY,½øÏ@4¨ºÙ�YGmùë¨ân×RýrØ/ë¯Ê4k¢‹ MÁª)_Ý,²Õ® ‹ûzQдž±òvÑ™K�zÖM{Io2‘üØ%é­K¾p~ÌêÔ¾?r7ypÕ™ûM>)- ³êô-`>Æ8ªíKÌmÎì·;«ŽwéÕY›áÐ,•—3"³‡e´Z÷Ü·V�ž%…˰Ÿt+xD¨¯5¿'¥Þ2‚ü!)¤®§)ÝÙ:ºÔA·™4p훡tÛ5Òp6Kƒ9G‘)SãW;è�Nõˆ‚äwZ¾ÚÀÙ¼J‘ux‚G¥Óдi:ej�i`þ°p;ã) “73b?AÞzy@XN�9iÜNT�Ö óø*ÍE;V®Kÿ“üZÛûÛž–Q�pf¥JI›ªž±m¤iìWËç:JFWl§*ÚÕ…þ:Ú‹dZ½ÀHX«$cÇÞ¸)°¾ï0á±f>^UþâµV;:I{S OÉ^’ެÙeØ *¥æ³³?íÌÙˆÛ°„}Ñ„3°U:€L�{çòÊÙÔ&~/ØÎTìGLyŒ‹;wÌ{•¦>çψԣ«Röþ±=òañ;T›�6oÆ5+ÏøÇ7:•0ðžºép'{­˜ž ãДᦿœ×·i¾P²Õ^a-ñ>g~©B«‡w4)g”êçs$Œ£‘<•†Â›�´¬D*QgHâs.' VbTT†y3,å°Wë{•Pªp±²5Es;K™rú[aí7y�X¸-ûg™wšNµÒч¤›Í‚쪥Tiaxd·×¸�2ùv™R93Š“º:Ô] æÏ-7ŽÿE‰ÛC”�%lPV©Š�Rö»&'Óâï&ZËF Š$þbŽû÷%sn3v«=?M/³G¤�¶‹ G;âèy”* Nà u“òž¾v&í#�­ùtIò}4ð<Åâ\òý Ê¥RÎ:ª‰ÿ7J× ß!~«¼ç¥Úí@:¡œ:Ãéè(M”Ý`Uä·*{~�¢JüöuŠ�Òéç”*\Û$�™©À3¿ÕvW"üɘ_O NRx¹`�¸/èèƒÒ)J®o<¡£ ê=ôà»v�§ŒÊûU6í2ö[œÙC¼¼�ÐöN}šÑÑp(é,[òZ%ÑóÇõô¢øÙ“žÊD¢À(5”¶H8*8_–ìºrW¸óNǽ·!É˜ÃÆ®a¤4øKTrMÕ”I¬Q玞晶¼Ž#(\¤r>N]©0›Ñ5—qã®-*�ÊVÚ‚xð{ÚLÞ´o(_¦BnÒ/Éw_¡–w䢈X�¡¬Ò4›÷~Ïo?—THn’<˜—°$3KBY…ëk^þjyÖ=AmO�wM›0r7ž”cK­$Î¶õ9~†äóñ«Çxìf³ã-�FOm$'ªp¶ÈŒ6%Ýý¼¦döU?Oâñh‰<âw¾gòæÎ€ŸÕR~œ�Ó&Ž\‡7ËwV@o4ÏƳe{’²´LüÆ=ÿºË7¦$k|�{aòÚ«®ßë®õÑHÜ�ê²[CžgÅëãgm¨Ý絟À{ìøÜ¥�œ0”#–-RJh=é"eµáI›Ñ”Lû1oIek·…§�& L+_×ëˆ�°¢S¾æ7k�çhîŽfÁ;†Cq5ÂÀVìN%£œð|@Nö]¡uTT³æu­P} ´Ãº¾×21a–ÉQþ:jîÔ~Ài¿%Ù¡ôèÓ¦ìèàœ[aœò~7m� ¼oŽ2 ïÕwï¡øšÖ‡NÛ£œQjNºK JJ:S– 'Ì{yúzØøÉ䣖©|êlÉ��°{jóÁÄ£#l»’4Nç<Ïi¼ê¥‘×1¯xíŒÝÛ�çwÈ{õY-ƒÙÈ»ü:R7q�NÕKœ¬ (Z¨ç„Uºžô“JoJ’‹ÉÏížó7œùcÁAwžÂáCÍÿu†…?QKnÜáMN?®%ëßñÞy·ùy,§WjÉÕx·ˆpêÇ‘—7v|YÄ#æ÷·�ù®3¯¹8¬ª%»9ON„ý¤3»öy£¹íµÀ�¡|éUð›t„x_ò†ß÷�ø;ïÌ=Zî’ÿ| ûMgw±Ö%”ŒÆT±ÐøTÙgm�ñóÿ¼ƒrÖQÅÿ+Æo?vdrùÿÍ™¯Ô’ú¿Á™I#¸xç1_Ž`¯¬Ót¼á…µÝ™_DÞcoº:æåÍ!gþPK.xŠ’YäÙ†�ÎðnþÿÈ£\¦×ÅU¢ÞÊs¤d½÷ß5¹âRòå÷Î|əРÍn½€ƒËTòïóµ¤ÞUzQ€{?ï¹ÅX=!éÙ.»}œâJ2_ŠðQgž4ׄþº–ÈÿËÆßKn@qeû®yVyºdü¹Î“Ÿ¬èôýP²¶úåZ"·Îûµ�°”c5�…Ò‘”]'ýœÕˆŒ½jÂ!ÍvXðFÊîŠ �*4œ˜Äë&�‰V×!\§yî”ÿ!-.3uMéVòyM±ª¶q›£¡‰]Ë·�<ß¡ËrmRdÕh™ûå÷~J7½§ã·Ë¸l4É™ È† nóƒJ®£š-ò›†ŽÚýé?Ý@´ÍØ] ì¨L7±Ø‘l£¨^P:Ò‹M >ï̳4wDÅõ€w�¿,ßõøP�æ¹ñDëpAÞhZ8«�½®O;uÏÂÈÔQÏÍN_/È<¸÷Ü)ï;Ké)Ë Ì<‘™ôìuvÏnpb™É;ìí2Ññ·ÑØi›Ì;gž‰‹)Ý•Ìeó‚„Râß–8LÚ¶x‡º÷e&cÙ! l\¾ë1™š{˜ž3òdÉ05C[XבB¬S¤·ã”=¥Â¸ì¨ *±!‰’©fÝ<ó-OðLÛwˆÝHÙŠFéTfß×Q)®h°Ðõ�f4©‡ŽGÂÐõ¡®wÄR:åÉŸc�ðG(]‹Û_Ö\D]ìV5B26å6k~¯¿ ïÙ¶÷œ–Uì(ÓSFnd¡tÇð¨|?Lé™o­ÿ¡�'*�cõº]¢îjø7ÜSªÈ�ÅÒ,ñ¸‘ÚÉä õ~BÉ’�nJ²�à>ʲÇ{®J#…§fÛº"v­@q] *5ÎþG¦žøK=›¥­°›î•à6s;e•;.cÝÜ4§½‰ŸÜ¼/+ç–”“ Ê.jï ø-µ‰RMzª!jÂ¥ì&�gJ<£‚+¯CÕµm ™uàïP¶xö::iÞ�ŠFƒ¿a(X©) h˜8Oyö-Z Q@�iÕ³~á�ã¿Õmƒ7Âp,æf~kûš–ß ù½É{NËŠ¤,®e#n=µ§€`m›öÝx°×¸—éP'ö™6Iù£Ðzì]”Œ²ÎÅâ9(Q~GËÇØ¿âÌq“îXÇYØ)Q²SœÑë2¹ó MÏjù�zö:Šæ)ógK¤iMÀŽ;Ý×)íPOPx$¬ÄDZŸ—Ô-u=CÉ¿½pC=#¿Û‘N(´Ã—…Ò.ÊN1©]_ïD¥ô®É²£�Šu¨æ7_¸`w2Ï8ssÁ;~&~yÊX¯àÒ«û˜��gF¨à`tóL+µ�vÎS4ÚZä÷õâwÉ]vMK”ž«%ɧÒå!åÛ<4GÈS2 |X¾s={5ðœ–Õ%iO·–¸•:~@É MI_h3Ïf‰[Âßð쨨üìÊ,åoh3¿g#a4½<`¿[$ož5í»][ ÈûOTiKÓÏÌ·®¼Géf§34Wj˜zÂuI;ßmbÏ�éÁZÎÊî2f;¾%ì7ÖÍ_¿}PàÖ»@ùžå^–H‡0-6våܤä_ݳãîaZ€Ë³)�åQÈ3~W›ÎÑŸº¾iðº\ꜛø¿@Ù5aeg­b"ª µP‡º× (ÊÎ^Œæ¼g 7À 0F¥Þ³À^#åY8k@‘s¬”*+û=ûW¥Óü íøí”•róÎWJ/ `¶‰�ý»­;(Ý7q�o/*¥·)…Ú×>ÊÇ .ÉxåÓ¶é¡ô2ÝùÚ´Ü1ñ™¡ì-OŸï+x†ëJLiðåé½b·OdÊ‘À3¶žøÇü%sONœØßA s†²ް3&%¬‰˜Œ¤KËJ5ÁÓ±ÊIé}»vsE%×JQÉŸ¡D›=îÙ½kðÆÏH‘ñ7D©öܤô°ö¢˜æ sóޱËëP¹\ ä!Qü¬òæ2e±X¡ì1�qònI’<¼Ýüî\a‹G0göºÎÆ£‡™Hmï\Þ¼nç�>¸Ž„¦l5Žö¨Ç&¯SP?û¼g¹C¿BéEÑ™’À{¹cõ¯ÊSìâ2—jP:{óXŽÿ¢5â“{.›7bi#O©éEÖPz«›Ê.Û˜Ï�‘çëVœ6Ž+“‘•ß_Úçt±Fû"¥.ðz‡j^±?Ÿ^#Ï­:þ}ÊnÔ¡Cñ¡u+nTg<;ÍðȃµÚ‡% �êŸ÷�R‹ J7е� ¹Õ¸·(~¦o§Ø­–0®Rø:CîtCÝP$6= "Iט˜6}z‹ŸßxvmDP~(tÍoí0ìò‰®Å·)Ý£Àq¹då å(v‘øhXª(pÇssѳý†Dù�4�ÌëÐMCK?<µû™€ýÛš¾H˜™µ�éÒv°‡Ò«S9þº ëäJîPõ†¢E1²êSšÖxB„yL¢-é†L8MËôŸH¦þ"n™)_cÏÓ¢h.Ëâ_GL§ÃõàÆ~Äú¦ŽèßÅéïÎè«Ç8´ú�¯”L%6Åè±w(ù•ÒKÒ™…–ü]¨G¤Õu=¦£ØŠÝù=”ó>Î û—t-I/ËTzé:%{Oê”QÂ¥ÿ䢻¼õèÐý‘gêy²xPj$}]õ&o–Mj™…öÝ”þï_fÃeo¸áʺ¬/sm?vŽQÿ,ݵ¢pè¤lºC¹¢¼ñ7€¨¦m¯§ÔNäyé`v‰Vß³Fþç#J§øÆ¥=”}„„$ɮԀ_Þm:%y³»[Eƒ’�µ\ïxÔ»¾›0ª‚Ì”6eï#fîí!ܦ´Ó:-⿊ËJÎŽP:�a5e Ûz ¹`ògÉ®‰öéDü3¯œ?=�Ë­š^F^8™õS�BÞ”¶´­Ç{UDщ– T°3†æ±F úG¥wùÊ#S²¥îO,<×T@iøBÿ'œù]ÕK†GzX¡ÈšéŽÞCæðæž`‹IEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7�IDATxÚí�„¥×Çw+cÖÔ2†—ü3 ±»VV¸TYa…®¡ª·¥ººÊ(#KToCX�Õùgm-•«„Õ¨M—‘•Û²6F+Óèv#mìMþéº�.+1Užž'÷yîû¼çžó¾ïýùÞ;óýp̼ç=ïyÏyÎ9Ïs~¾÷È³ÕØ•�Â)ý­€_Ź ÎÕ�»á܉²Ó9‹ˆüZÔ¡^vzF˜/ÔP:ÒiE]Ó�«Œ{¬éþ'œ«Š!Øu®YvZgiðu‘cã U©4|L ‡«†OIk:7Wvz@šÇÊN@ ü5äyôèÑ÷eºòÎ}͹¿³�õ‚ýÙ…û¼ì L3N>¿#ú±sßsîç~wÑ…{XvZûà?Îý1tC”ÚçþëÜC—¯{<Ûáþ?ïÜWœk³LÊÎ@89<áþ,;÷©“Á'îzÕý¿äÜ=–׌Õ�©ÀÉìoNŽ—Ý¿ Î}Õýàä(#pnWŸ¸¼}êù©«ËN#0èhÔ󻮣×òÜjÙé³lN� ;J­îW=ÙVyÏ„å²J§äeCòÁ£†ó&_‰6f!�#�Ñ’sÏIù7¼2>/òÑ Š?Âb—�‡YÀÉéOε�»ieæþݹ«e§¯�|p=Ù”º°,~×uF(£½UõêÌpe‘F9°QÅØ£È¥PW�»(÷î:÷)¼]óÌ�š –<׎oòþ V�CÄÛ5¨2b³÷Ô ¶½NÌT® IGŠBÓ¾jhM~ØÝáQ„gPê¹ñK=z[òß™-ɽš‘Í¿M5¦0§²”�ß~Üÿ "Dz3þK"×+e§9'?KRZÔËmSO² jJ_˜8+ý§è¢=YÓÃ=‘~ݹŠðk�û]÷´Àvå}º¶ªŽÇÖ‘EòsZÜë¶s!� 6¦�Äïç¾G jƒ¼Qª‘ñ}‘1»/äïæ4ž¬•×È?pî%iàgå>7ø 4£ëÉRWŽËÿ‹�ûœ¿mQ„œ[ „©I'ƒ ê~†ÂÜ…QMYÔÕ°Šþ©‰·M¸5‘é…²Óœ‘—Uc,ëRoåÞ²¸š§“BõÍ¢qrݺD8eG*Ðgæúj¨¡‰P7)½Ã”DÈçᛶм{UJF¤Mÿ}ÔÇ´oY†Á½w>â�’©¸GbÓÃõaÅ—ƒÀ›òvüz,éÔ:R¥ŒÉï^VXó¾÷œû…8ŸóG ¦0bʇÙòüR �:=¥Ô—²•4òîî(uÀüéZíõ1ËqÁ(eÎÓ)£„.Â×Mc¬åÄ=” ¸AÝ ^£t̤{Ý»gk[ ÇÍGN ZFä'¨D)½ŽºW$=¢”ìôWÅÔÉËÈ J;û£e¥3ÇÏÔ)2{“ÕžñtÃRx¢°|xOù¬ð#’GEôICë«ä·­í„:�:ö»åÜ[ö�Òh妴œû•”ÛX¦ÉÉl’k~篥žœ2ò»"÷øí^ô’ÒÌyߥ7°eÕ.¯ëROtö@÷Á¼8©²™ ¨ÓcÙ ø¯ˆ@^1~º1Ĭ ½Æ�Nüj”ì»IëV±AéQ*¿£ï5Sû|àˆ¡§$\sR�¹ÑÛ^µvVÚdF!rOg‹rÖô†Ub0¨”t4BráÆ±çùU(1<Û½Nr VgøžÝí›·Îß°²¡Ž�cÙð Ãvγ¹ç¥nTc²¡Îˆ¦f®µ¬˜…È3™³7æùÌ‘’Ä£m7KYrš¹¨’Ù¨ƒ’édÝ™Ý?Ö?ßVÝxÎv &~>™’NeÕÈêsJ¦^m¹VŠ´Š,eÉóÌ«r]3ï¶ïže‘2–i¤,º»}ý°^\¡Ù8î`¬9,ˆQæ�ÿœQ vw[O¤dêCydþ¿eâaÃz5�òã4÷´²qd<2§?um×�_�Ê”gð8üg‘{:jmQÚ qü £°*ñw°Œë~…Ï‘K#Ð5­wIC$]ÜèOÓ�š˜<<%›»T`D»düæDaå­1�TeFÉÈŠ7dœµJNêÿÛ�ç9ïíHYmåÔ‰ªQtoDÚ(¯Ág¿²í™â먧�ßãâ—ê<æ¼CÛÔ "WÖsæþ%»JÏÿ¹"õÞ¤sb;ÿ)é,¿,שýdfõÈÌ’Qg¯�ê—Z ÞµCeF�¥¹}s]ÏË3E#”îð‰‚µŒ8NÖ³–JéÑz%ðü¡3¨‹¢,žM„´ãùw ñ;.nM `Iþ_‘û\ùvŒàýBéH_•"=­‚ùÓʳåùo‰¿5Úu`fݯô‘wìÆy†ÓøWˆŠ}Ɉr¦æ È x·±yaŸÿÆoɼ Æaò«®aâ<9@|J¿É[îR�§púÚLEÉL ?kϼ¶ä¯ÝùùV }—pþ¦©¢u¢fÂòÿ«Þ=†7/ýˆdgp$ž÷$lhUYöüYÁÙ´¦»Ž•wåÙ;&Œ=>çË"XGïèÒOöQÖlç=?]æZ—kI¯iºùx�záúb×8„Ú˜ø7ÌuO›<£ïÝ£Þåîøé>��ÈósF¾:HŠÎdüµ–²‰­4\†ÿB½ë¤jW<ÿfL°ÞCå¢qöÄKéÇ 5´ñ†ß¹k®ZöOà~…:»¢Cy«“Ù�Kk©Ã( ÓøìT�L©3bR.³Ž[5që‘ v­7eɃ¼¥‚i¦£ÖYQrô€Ï¿Ú�ŸZÆv4Ò`%)+ ÿFäÝ‹¢èXÉÞ )DêtlÅî›x¸¾w7©d›ÁÈíXQbLŸ3~¿?Ù«ïR>Ô1`÷búcTPo‡ðžþÐÍ�¡ã%œ�} ¯ónôï˜w0±Á�¿¹èK½‘‘æ:%ë¨Áå-JFŸ¬ï¹óu:Çí,ùf´ÃÜ4HLÖ¥ì­@Øf^Å¥ôô“îNlS²ó,´þ–¥ » ‹ú\G¥ÀÆñ÷7Vi¾ú©Ólî/¨â Ü«QÁµTb”ªJ:"Sn€;�ç™�7[ˆ©GFú³Î¤6cï4aúN“÷þsÆ¿gmš’ÎÚ›FLhß‚­ªÙ8ê¦mOw²Ú“„mx ó¸wŸÓÁ�¢5§ê†¼e�õG�jxÔ°tË)CÆw)²Ö'ùJíÐî·,(ï–ÈŒËI�7q^‹<«3l=ÿË~Þ%îËæZ§½C†ŽÙм“ËöSö×2Âñý‡â¸ƒrVòËõç¦W«YG Ä­Ï:ƒª_ò®CÛF°vº´!ŠÃNí*¼u›·û¯f¤¡™Q0:½Æ=îÜ©&ó\pã …7Vqoz‹’-ðEׇ4Ý¡i»cöbäy®ˆ<+9ïHiPø£ºnÕ§é ­õ±pÑU¶Þ½¯›öÕΉ¿FTl·oŸéfŸBI‡¸%uWÛƒ]‰Ô\%I�c<Ì“ÆOÏj%#ËèY茸y}O—¦tdÚ¢dÓÏX¿ÊãâÙmò6#Qoç=XÉì¼ãisûé¨ÐˆÖ(©sê¢Ä· ³Ó›‘zREÚf©¨Ì/ËNK�´Öb•Ñ w;Çp,FžÓÑÚÌOW˜Fg�dlt¥rµgèŽCÀ�n�’iÖkâÿ|ÙùŒä]7BÅ>ªP%Üï&/U’zqe|JÒÈ­Èÿ:¥×­îÀRzÓÔ²ŸêLçízÏ„Œ@!%)íç�1|Ü)Ó�CWL¸“}ȃ;¯R/ìÿêLoŽý“$?>`®ïRög[µ®Tñ0OžáYŒÝ<¹XƒJC~Œž:#ï)½ÑÊÿY?­�=gž3Ò6TºfšÐèjDiÕé¼s9ḗwÛóÓOom¿ŸQï6þ�a”Ø´@Þ_Ê�·ËPüì9K¥% v>/ ÓŒ”s] ¢~ÐaÅܯ _w�A˜:Ó*b ¦KëøÐ I:BÝ5GïÞŠ¿Šç_hýÔtZìÂt÷*;Í<ðhÅ=û”üecý}*éÃëRæúcœ÷OrÂ[}R“ö§r<y¦�ö êÐÇ…(™Î&-SJFÞª7C§B³€u?,˜b¤°ózpºX¯=å5iûZ)YïQ…¸9*¥8 ˆBË\?õÂëæÿ¸Ã1J¾xs z�”üLžå²gPë¾¼Ä85¥£¡?^^£ }}€|ò¯´ü˜Ò£6<+uAê½ýJZ5àúg${ÂRòA•Í�ùÕJÏôðŽãµœð»bLýNiáÍBqWG-WiúKDMJ> <­@‘=/4Â¥ 0Pgš©E½¼& Q�¬ö°Ü×<(Ù\`GÜ`²vUòNígнá`@É/±¸ãÝ«Sú˜Gßë~Ó€7ú`x9ä%íDäQh“‰Œ©;R5¤ìµC¢mëÉŒðökZË”|rp¹ø['žÇeÑ—©þ@͹q¨l¦ö7aÁPú77¥ Kù�Ëi�&øSv³„Ô“gþ+^‡¤ê�Xg)û¥?ûx“ ë¢díÕÊbf–‰ÆåX^ëÊLɈ’ß6®“9­@f™¬@'¦¹ã˜(çÜä´#†Q•$/\>V‚"닳Œ#`äÈtØÌ¯!‹a=ëšÓ øµ#0+;€|Ž=zkøXÊÇå£=|, †Œè~ïÜÇe§@œÿ“ßF Ý�Î¥IEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7¬IDATxÚí�]h\ÇÇåáb0*a- Á ÁÁM1, ƒè)¸�’m `*" "J—>™ø!~ )†P(„¤!`šæij’І*‘´¤x „¨±kp“š>œÎÑž£{îÙ™{ï®vuWòÿ—Ý�û5sæÌ™™3;5ㄈ.‡£Ž[áhºs�p\G;'ëŽ+˜ D_øØð:Ì‘º#PlÃÇ¿w„päÈ£&|>||;? ÇŸÃñßpþµºã êE*ÑwÃñ)ÿ¶: Oé‰0�ºã`ò9P=Té=þ/ô>Ñóv\wáyš°cáã�©^/õ�r|®y®îô�›�öVø¸_~þ%¤ù÷uÇiÒˆé Èd4>Âñ­pü!È꫺ã& Ñ‘áøÁTÏæü'¿›êy·ëŽß¡'dÀo¤ÇXiüŠ+‡p¬Hos&q ùgÊê\8–äÜ�ÃÖKå±a‘KCljÃç:õ³ŽÇSò«ðž¶<§]wšG(»"áÓl$T®uÇs éf]Y¨p—�ípÜ ÇÅp¬Š¬F¹Hšçkxç��ß!:ò”è‰çc¹æ˜\×Öþ‡L¹&ß_–ßs‰kRh¹ðv#Õ’V‘½g—È{I&/iÅr(25¤ã¨©èºò�+Ó7T¶"ËMiLÜ‘ŠbàôÇ*Ôƒ.G£2�È9.ü×äsn˜çObÐ8×Lº?}y,¹ëgMeÊÇ]W_ÑJõ V®Ò�•ïœÏ±Ö>ÄcÕÛ³º±�.Ö�XüDGLeÚVû"òT{û¤Øáu Ÿ¨´8‚ŸÇ�Š×¶M¡m™ð“dza‘û:æ>ÛK�“ F¹.™:ñ­%›æ”ÑrFò_á¸ilœ dz¶â".š/ Öôòð™ódz�û�©F˜‹sÅäÇÄ÷&$žg$þ<ËýMIÇ'ržuê%Ñ•oä Ûp0ze+S5˜¹²&el~ÒË“¤ë|8ž7ef[â3f?ö!>JfLX-^ ʶ’$cЖ"×[¥Þ²Ší®ã–䪑ï@½ �S$®.4ß-yÖª3`$Fýj ²Þ�wÁùŽÑ–·zO&bÉ�ê±ùÝ2:qGòû¡È}3îZë}x˜zž¤®5˜d¼CÔs?)²Y¤ é}°þÅò'üþXÒò…üž•ë~èÊÛ¾õR¥²ùÒü渼<Æ÷Í›ï ɳ3’~nxÙF×E‘‘êÀzD¦›r®I w9e•ê¶¹vÆ]³«Ã$ò=‰y»à< sÝ…ub™`Â7|åAZÀæþX�£e Ì_ L€çŒrªküx8NÉ9…ìD”ºm B»ä=Êæ€ñëúgK>iœ:r ‡ý¸à9Ü+º+Ÿm9ÎÊç¾çC‘¥¬BåÊÿºT–ÚÇ“Åð1W]xÛÄs±äšÎŽ _ssÕ�ç2õ«p¼`Ë´9?CûÐè Þ˜wSdq>–?’Ö»� d°aî›.Ræ‘ÑžñU“æÒ˜Þ»aõÄèÈ×bßHÜgí çóesŽLøLÁ»[”u –#6LáñÖ'ä�… óC‰)pç"çNɹ† ×—[X/FvÕ÷º<¿k¶‹ Yª@H�Y–s›”õN›egdh[�íÄ5›F©YÙ¬;¼A' QA££àžVÄ@Í{9‡ï¯ùÆÆ\>Ç Jôb¨7+{ùiúË£|/µ9§FV[Ø-“gÏêûåsnØxÄO'G½—8IâwÒ…«>lTÐ •AÎXÊ9k0›‘{Ï‘ë}¸{7L:ÎM�‰?oìò‚ËŸ‡åûo©7^\ªó&½cë¥RÖ�|B^µ"á òûÌž�RÛgíFÛØ�[Ï´c¢·\™¯$/ê¹-© õùd[wh'¿ J�­¯4¹µÅ­¯˜ï½c •-€'ä“ë=1ÌìJ:+ak¦à¾M×H*ƒ�Rt\x2ÓŒA«dìŨþ‚²Ù�ËîY‘x])¸¦An’;¯çJ ú ƒâ.A•¥m­7ù³yÆ�ªï,‰�Ê¢MyWÇiàYš:cÇQ½1š‘ôž¤¼wA A¥aÕ«ðyZÒÃåé>w —…Ï÷wåü©ˆ¼ÕXê„7–”÷•Lc|yVX>Ñ�3„ÃõϘ{¹òCEËbkZ‘{gĆäÊ}â=öÚ±T¨FÖ¬'WåûªœãÆÑµŠÏa™?áÂøyÝÄõ¬ Û‘gô•í‚x/{9Rɰ‰{F'¦'rŽ$yhîZêºCõüï}~ÿÜ!snž".¢Šïã©ËB¸R}À�ßÍàȽz®²;GîÙHÄ£i 7e“?”�1¦ ïèT)À”¯@�áÓ‚±V¡p ¤¬”®PoE®ÕøùF SºT£B\N™øoPÞÓÀ•Ò03˜w zäœêÌvâÞe�ˆ®‰Û�’wN‹�êPÞ=Kò<«W-‘«wÒ ŸŸsçZUuÂéÕ% fIz”Xeì—TTr•S¯1{ÃܧC5\qhCDu+•?цtâÚF¦ãì¡vLš¶(kþ“œ÷ÉÈ�ÓÀ6‡pÿ0úf'’iúû5Mk‘¸ì–m*nt5(Qùº¼-C7×Å*T~þ"ecô§ öóPA½É š¹§$ìhÌ0¸ûT©š4Â^…= ¯T°Ü=Ÿ°¢ñŸ‘ßÜz¾NCLp2I)+ð,kî qKîmw®MùI«Âû* S�ì ßå„\ôÚËîÚ‘(c\Fº^pX�¡¼^]¿§(19,ò\ëFf㯽–s­æ«7”[r¤z%V'X�[Ô_ñî¸bÃñkJ ;TÑyþFä>7[rF²j/ì™ t©R¦Í5›Rng ®µk¸ÇÚ;¢þ‰yjG½ü5þ\ޏ‘>mÊë�Ϊå WglO›ûUGç"qÐç¬踮ËVÙÏ9[`;H…K€(>Ö®aLØ¢IßÄÏ)FÐêºØÍ¤‚{Tˆ}”K¦ü’œëŒ²e$sTÜ«Pb㨅…Ï]¯ã„—#çøýËæwßæüÈϲ�¸FG“%?i«E.á„ì©Pí´ø™”Å m™ß�Ad^)#Z¦3)='7�'r^Ÿ»”¿eÂNKئ c×ç~:ñ«:ñ„+[ä¼áÍ>xHå-g0­{[‡gØ7�xW¬õW×ÉTòY—øx÷®Î±¸\%\\Þ×8QVIs”z½¸uÊ\÷•ËÇ0Po3�ŒtB^·{¯¤‘+ß]87¾|#†mæEó{VmB"mÊ{VæD/ŽËù£ò[z,#^ƒ|Ì=§%60iÃä:µ=±¡?lÈyñÙÔ½en_í¡j=\pOÇTV»ë2åÝ"žÅÇ­4u¯®QÅ-÷+IW“ƒõd*P‰³$Š¥á•ÆŠ¤P¿%ÏÐBÖ¦¬UÏcuÜ;åÖ~_ejžÓ¦H�)qmeƒA®‚¦øøiƒ2ãí×3–¼º© 3EËj^*B½öØûŒAÓŠ`M ÎG%ï±³MýÃ\ž^¢¼Áì¸<´e56ÙLžÖH6m|M8ǹ¬±Ñ’{/EÎåÊ$U¯POQæž×Ù¦[Nœv®€F>†žsÔ|?&ÏÖÉH:±ìLâÞ膱pê- ºcôDeù´�ƒ“µ–Q¾ïÅwZSù¼ŽÇJò-:[Øä¿×•¾r'á¬ãµÏ¤ßw¨·“‰°Ô_ß-¹Çº bÓ¨ÕµÇ-.�äÓ¥<ë¢üÉõ‘‘ð[S¡´[Sw]ÒŠs�úÑq£*¾Ë¯÷òp¥½HÕÇ9 •1¥Ä‘ë´�Ä*T»›ÜýŸù’÷ 5ÃwTP¢×nÒ:¹':Pá]ɾœ/Zöµ®z ¿JÞÕ"7öEÙøª<ùenìz=õ>ÌHv©|Œ_ó"&s.“ïDò§ª×©!éçN€ÝO+ Nëw¬PÍß‚÷¶)ÛFu7}áóCJ¸Á©ØKÓW}ö%“7\I>X7Õ�ݼ1²i˱ 2ÛÙ5‰Ò[À^=Š­{ÖÞ2Q¾K‡ê÷=Y¡êÔy-”ÌJ…û8Ã….sµLæ®PæöÝiUF®�eR´‚L¼¯È­Ù‰(ŠʶâZ•ø TåuÇ5Åp¶i€Y‘¢ˆ:þ0Š U ›u.S~ý)7%/^u÷kcc®àÜû~sY�ƒ„Îp>Ä&¥õ-à=Z ÄŒdRGCøkáøJî½Më¿Ý}š‡¶QÄzò3g0‹ÊSKË· g�×ÉB…*Eg‰øv2ÿž×%#ËUô1p[ž¯•Û£"7,ÇcMó†ó/HVa”¤S·9½�x³ì‰(º¶ž±}‡ r;�PĽ0â÷�§l¿Ú™Èù\�¦¬‚¬:!b· FÎu¨d9Œ\³5U”퀲g—¯(v×üŽ64¨çJdÚ.\ÿ�]Š‹&œ+¤Ê`µ»„•@jRZ²R¬ðžfLöÔÛ†Žù~â>ÎWnté®MU—æìV¨ƲEÙÞ­©qR퉾bÂ~jʼŸá›2’e*Ëü’ ›ýòXh>ðÌ•© �L£ü®O»ÛJR¾Wÿ>•xÛ*¾³Iýt%]Ê6~âYÊK¦Ï[‰œS2¯½å†îvIÜTqþE…t²}x\žÅe{AâÊ鼒ЋXYàø¬Uó!ƒ² 3$­uÏÓ„q䱓J;•T¨ßPA«•zîÙÛTó6lb$Ž&ÎU­P×(îî�Éå¢7>”ÍW^¤üŽ4Zðjo‰z™PI/TÎ =~Jf,ܼë©1¤+g,‡•5õz£l¤ß=X5Ïî›á[�"ÏÉ1×ÇÅØóÄv%¾Iý“”r 4뛲1c¥#ϹàäE£4ì”ÿ笱åf1YòºÞèä.±ÅvIYi~“ë¥î1�³Ô?lõeÞ•¾¾‘gt÷�MІóïZP·t«âý}Æ4üþÄ*e ù[¶‚¨;ý&¾© µoVf䚆/`T\¡vcò•Æ+7D^!ÓÖ¸�IVMŠO¬‰¥U×Ö� ³ 1¿w×MÊ¡3u™qî$´HY�h(CE½^âç× Ê6ZÙªh$µ¢²K.NS�…+;»þ”×]Ž|¿m©xž�ôüÕ|É»©·Ç0sV~kO2µ´°iâðŒ„ÍRÖ«Ù¼ÊÖë¶ËìÂÏlÈ3¹r=O3|Ý}jKïí u’™waOJAiR‰ËŒ²t6DºÖôCS)kXGŒ‹n˜~¾î´W”Oi¡¡øø©òØø_aA¥£\¬Û²¨Becq·¬A@n:åÇüf)Û!i¬êõ€—÷j0%ÝÚÃîR¶wn• Iün»üj>v­)[g©Bãw�r9Fù‰�|èò•m±�Qè®4nR6Š]èeã§Ì ¦bíJ9;ÈKë&:.íuÅwZÔî B�d¨÷O–²ÞÅmÊïò¢ÿëgÇ'¢G>&y±b{.ò¢}?/Ðý{ÏÓŸÜv’zîô»”_ÓÈ×ÛµÉ o0hÀY©cLÛPcPÏýºÓ•ʨ“0’ÍȽ]ªèí¨Q. Ê&iiþ�dè&<çK’ Y(k°_*¸žß­{!s¼Jc}Idh— §ÌƒeÇeÓäË*S6˜ç¤5ø¼1šßËqÒ3¥DÉÍÆä6.ºkšDÑÿ�ÉŒÔ}”ÃÅ·­j$¹m8YÌ[±ïSšgFÙ Ϻn¾ëì÷¢Ù·C-˪êõø×¤ñ5+¶TçRä:#‰†—V¨cõN€r/UŒÃ`z"ÖêßdAvekõ~AÕ&öð'•ççÈMh2FÒþ×ñ|ÂHNü³�rÂa[©ÕC/˪Êÿ…Þ"eÿ‡º¹6¶Ö¸A{øÇÀ=õǸ–7et¤×¡Fò@õ´ Æ“ñzÝq"î'E?º”giïO€C€ëy0üoO÷íýÉÀ#½´×ð2ñ?GÙ$/žÉþòÞŸº¿©;€Ã‰Œ‘þ(÷KлáøúÈ‘#_Ö70¹Po)Øß‚ž|Uw\`"¡1ï„€}áÿØè@~Rê3LIEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7«IDATxÚí�„�ÙÇgþˆ´Ã–ih­»–†šª�±±um-ÑQFÿXÓ¥\­›¢DG«\¥»Ö¦K„²z•¥6ÖVˆM—êí±ÕiˆD‰ÜÝš†•X¦V(Oßgîóä<ïyŸó¾ï½sçþ˜|?¼’9ïyÏ{Þç<ç9¿žsîÜ€Jˆèlvu³kuÒy³�è tb2ø­FrÒyÓO¦'M£3g'�˜Ä0î2é¼€Ù€ “Î `z˜ŸtAF Ùõæüüüͦûhtš¥û¤¿L72Ýû[ý:`f¦A͌إ쟯e×7ùÊŒØõ’¸›Ù?�ìú²ý7»>ήfÏ}žx欤û‚ ;¼+¤ùù‡“þþý û>–ÑÓÙõIvýç ~ç~áéÌADôd=»¾"Agßüþ¤ó5-dòYœëÛ%¶N:?Ó@&“'3YÜŸt>À\¿›Yµ% ûzv�«rÉî_£rÎ3‹yigWkÒrP†Ç½o�â´E&�ì:!a Ùµ2‹ßu[$ÄX©Â^ð —4¸GM£ë�HÔ °R�ÌŸú£AæzÝ|És½È05iˆ6{æŠäë’VH©ä ÏÐ�YþÊvîΠŒ)O‡DÖ+ñ^“¼äܳ³›x~12˜pâ,È¥£N—gŸŽLB6&_KVFFÇ»rOGãOPXBºUb³šÆ.êØˆóÎr<7&96²ù}”‡¶ùÞBciâZû²bãI:KR¿šôz¢'ZßWDÆSß1;™PÞ¡�IÜWá_¥b�f•Â(õ”Ó8Ø‘ÕiJ×ͳM}Єñ»þEý^ßß×u³,ý§©Œd¦Æ³k­"®V€v¤Ô sï²(÷’¹Ï#™M¹ƒjLïIE!§AõxW*•7Ý~M¾m¤SŠæ›íUKO¢t :#áÏʽ…q4Tœ¢ä2ìU<Ó#ÖqîYƒÙ®H‡ãÝUƒiÂÉ¥£å¶è‡ïk§Cê“7ºÖrãúÍ6ywâ-YE­*ƒºq÷øMšÿ³ ‡n\©¢ã—Ý;AýŽ×CçžêǃìúS…\6LCÙvÒÑÆ´CÑh?ã%)'¶÷hPcŒ’®%î±pw³Ïw¥‰ü)ZnT/ŠñçFõÕœ~Œ+ƒQ¢0%ÜÆø&Þdž]T‘:<ß•g*G�”w@ò”º—z¿äSG¸íÞÕ2a§Ä°vÍ¥†˜;,›QÜ¡º1 9›oXs*¬RÐ¥šé (…K3*ã‘tÈ¢4o‰¾2÷õ×Ïs¹,qØ(/””a»LßÅ`jܸƒ«£MGëÉ߉|FÝYZÙ¨ xZúy§|Hl Ëïx"­¦è-Q�ƒÔx�N›Ú47DÏy åt”6‰1¼âÒ5­ƒ”˜Ù ’˜·™Ð+›FCÊ %yÒ2^�ÒYÆPR¿�Ñ%Ÿ«ÆË_HØJô†Ô;:£qwjê~í:9à÷-H²¿–¿µ>®;q¦³ùÿ·My¿L¡ÁRg«Nâ�­XvÑ;Ú&ݤŽDrܶv€úõ�T>¨?{ÀåwÖNÉ¿\/x$Í�¨Ã£”ùL@ýµ'’ÛüÝklG‡ïl™²È�*œ¸üÍ…ÊäyÕI;e$_vdóÒ\ Fä×y‹óµH%�¼Ä;:ª×”îp®‘Yw­¯E¥ïU¸1¼K¡�íI¶u�õì˜#‹¸£~¿¤üGËè|É÷6(?Ó¶R·Ub’ò2i7�°k”ïÄ”êÉ�…ò7ð´Ìq çé™^Í4Tˆ\HµFÔßþÁŠÆ½oz!®xªµ<|MüØ€h^åïKæûÏQ}T®O:ï|J*Å] £pöøÕi�e¹VL%¹Gå®ðɵF~´‚ÙQTAަw+¢ó|]C7Ñ^il ¬AsâvT÷(x0êTîVÉ;šñ{ÊòÝÓ2¿.åËuO=7B%Û¡"�È•SÏm RåhâÆF’§öNÝUûÒ©J¬áFïê¦ÂëÔo‘›z£öJâYÇÈQmkR9M'®'‹n¬'ò]ŸQ½>'á:»p©"o*ûIã'Îb—óa§ú+uÛÑ•ž¦Mam>çPùXAÁ]÷1½#×r«§roߎ¤ã9[ðZÄòì¹èžç\ò`®&äôM^ívœ5©x;bè:œ7öÚ£þôŒžpd÷¤ªòÆ{RyÄ\¶'Õ;ø¢PyiÇÇÊ‘òŽ=ŠŒ%…éðc5ÞÅ… ƒÊk””4ïàvº¨ít椙ÚÂBeeC}_�"ÔŽîWÉ8Ò‰¹R�j!�CIOïø�?­È§ê”'sÍë¼×É{@õ;o #èËRo9�5'wRÛ å\Q~—ÌßÚh?éÄum�•»~\Êú!…N×ç½ng†Ÿ{‹ÂžTÝztDþ厣väXngã|‘ÓR41ñã 7êä÷ÀBýé¯cT<Öì�šÏsãñ‡+ð®(¼»>EÎQaZÁ¢x¥k¦NºîÔ™õÓ(ÜæydÇ—Q˜‚ëP‘Wå_oêP+‡gØjå�|/b�ÔŸ=Ó2/]GaöâÍŠ÷<“([;Q]9âäƒy¼OZ¢ü±^?¤�tDQ”ß;yÈRj3vNá�¿]ÏÔŠ|âS´~Z‘‰rnUÅ­‘–ngЋ�“ŽþN&žÉÅ2y%žo8•Eåx-Š«•®…kC™Ë'™[(x-&7”�ò§ÐR­t²â=jD¾êÜãåƒûTs¿. îtL–9Ïx,›8ö€‡ØO 5ê°FÒÕ-£�UgRwäŠk–Ä€óTäë±(8rÇ|àCBL}RX6<ãt#õM5ÓmPX×5N;šW§¶­è¹d£Ia™étÉ{W¤l´žUz¢SÂÛ—òSßWåýŸ‘ïuÝ’0oĽCÎú©�óPk¹ðÀCû°§H*KáôQ5æ—Mü\A ì´ ñãõÓœ¡‘°Ü†sÉSW*ä[£–…yG‹Ê½5µb Û ž§ÄèŠBGŠåc�¾ãÓiìÖ–‰md;Uß1.�Iuª*Gïá‘ÔîÑ‘”wtbyóéâ0 BÍw[ƒÙ–ºÃºzÂÜWc}“ÂÚð–„yÛ:1’UÇ”.PXÿ»aÞß�÷ å›0€|ôŸ«¦Óß®S_JÒ\£Ð~´ÿ›Âš¶6J©ua椄Ù}Âû²ÆHÅN—=á¨còäU)¶à¦“®Ê1Öo¶¯³ŸåüØCaDÃNO·©+ÿ–‰GUÎZ›ýÉ÷ ;jÞ§ ú�¼Ü¦÷%î“ÌNçž«ÈUÏSÉ:4…Ά·'ù¨Ügõ#` ÅåUKg¨fçŒúž¹×v¶âm–3¢ßûêˆAùµEyÏN[ŸÎ›gZ~>JëJ,£XŽ&¼é’Ž(ã=½~)ÊÏyÑ›‘ë ù#Cýužó´GO_ SÑ)Ï©žqÃù+*ú+Œ£ÓÕ¡0J] â/Ð~}Fî}…YÏöœ®$ÞŸ³;`Ÿe?m ö´Tö·¢x9ƒ'ÊY¹çÒÄWC¡Šë”ß"¡ëEj4OS3ç/йí¡:Æ�ÈõðM®CS�+ú™ª´§‘ØhR ‡*ÿ©@•Ÿçi®‡4hÌ� ò§ôìÏumÅõIâ\�þnI:ÞþSJÄu�¯Läse\²¡0SbO¨R5ð{š‚¤àWqZþþ�JFìF>\ç6­�¡ñtºz^ÃfÜÔÉ]/šÆv�ò£êm¯›hP vŒ êO·V6Ô?µig€tÕá#÷SN4#Ͼ�¸e'ÜUd'^›jœ�=ÃÓ[s*Ú£¼vËØü�\'Ü£°'XO òöàn˜°f• Çð�m2SzÑ=þïQ��Q#É£éÄ2óäÉÂvÎô”«‰ÿºõGÄüMËTqƒ!ß¡ë§ ¢/ŸVÄoîõ�{Èë*•ÌpU<»Dy‡¹ŽØÑ»„õÓÙƒ|ß_ˆlR4 —HC{amš‚_Ô±|\Evâuˆê��tˆzÏ¥ë¤Yø÷©x&sÆÄÙmh£ç&.CŠÖQk>c�$Igãß©ÕyžßwuRß\ãû¸1Õ5AýÉD+'fOÎ…Òh“ÿó,@Õ–¡ßF$“¦ÚîÇ&t[ ÛÝ�XWJÔœÝFÍ(ì‘á;��ÂòY­ˆÓ9Ù=y:½=“#Њï]�*»:Á­&â«£Æ+¦ÓUit¦¡A•| ;úàˆõ§ºŽ‘ä°SÎs3Ñ!�: ;� uæwœ¶ü«këeÇîÉñmD²Ð©Ú Jlë)yöˆ¸P˜>Ž÷Ÿz'‚5‡iÀÁ˜¡àÁ÷¨ ��7ºzœc2ò©H3ê™ôoˆ6iˆ�¹hgB�¤Ý:Ó:Caݶ-o›¹2‚t¹�¬} …ó�'¾Ž(:[[O(ÚFýåžî��ª²c3CÝ0‹÷sÛ  °¦z`׺d$þüÞSz|�Ñl[aýá>Õíî¤ó‡ú¦=™ÆÓ³ŠÕYïäÞß ¯”32e=ê{Ò{ |N58øPðîͺžÌO:€ƒ…¬ýý »ž– O²ëoóóóŸ›&8ØÈÚï—²ëÏИzþw}èm–IEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7IDATxÚí�ˆ]GÇwʼnȖÂ)„WK%²UVJÃÒx)ÁX\ªÄ*ˆ[E¨ •­,ø‡O¡-Å(„ˆ lþ(–Ö’Rˆ–€ð©iÅm &%/�bº¥ið�㜽çì=wî̽óÞþ¸o7ß »ïÞ3÷Μ™9ó{îÈÄ ¢YçºÎM8·¥éðëW�~F*S’еÝt˜f´éÄp•åf÷çCùùÉÑÑÑËõCîÏÝÎ}ܹ/»ë4Và£M ‚Èß?9w�s‹ª«@Ÿâ¿®b�txØ÷²V¶Ë…{¿òއ›Žü2â0æþÙt<6’(çü‚‘�“ €Äßæ€ÌR…JÙ0§^L*P­L-üÜÉý±À3²óÂÌfwÓú«Ð†ÏIü®’ée™OH�W�÷D†¢…MY£êZ·^z7ºÒ[wM†Á Ϙ¤%—‹×ä_”ÆÉBà^‡ò^ªÌç½ò´‡óMÓñ@Om‰Û‘SR>f´¬s¹òäÕ–� ÕÊŽ‹.(ï ̆lÌzÄÅã9‰Óßš UííKÒøÐ†HKì’Þß: $RÈè™qɰš÷Gäº&ã¶÷Þ‘ûç$“wM¦„¤º k “ŽTòµj%n:$º'"³Åök~¥*áxBâÜ’k}ïUÉ#¦†Œã<z†T¦�ç+N/Û$]fBú™×4?8÷LDfJî/VŽ�ûš7•“Ržvxå)øü†uÃyK{Ÿ›ršoÉߥ 2 ;NEJ²"Ó‘JUÚ‘ /ë€; S5²=ÊF1Γ7egòÇuq¾ �ò̽ÛòÞMaº‘²¡Å˘l¼g2[©ØŒ Qv%‰Ê½ÔTìÁr‚n1þ”Žñ3¨˜÷¼®’.Z¦Ò·+™šg-UT ²/‰h¬GBTÞ›ãB×GXzV¯F�·D÷]Ê{=Ìó�g\qî©UÔû­Ð ­ÑÐe=áåCûJOŒØ˜W´ñ¤ ­ !“ô%Šìëö ¦Êiïc³soÈõ3n�ç*éh&PžN‹1?`ã'efVË‚sG+žÛ¦â~·Bö<å•ôŠÍ­Kܺ”Oå¼*;ž�f[ËC±�°^‘ ÆììbƒLeÏè˜<Äá;yÎN‘áwþ>p¼+ÙÞÜöåß”ÄÜáÝoÉýk’Ñc­™ƒTÝK=&÷¾DÞ0ƒ¼ƒ3Ç^smñ9æ÷d]áIŒoK 丹ƙð¢Íœ"7ðÐr?˜²^Æ­Àu5ž:¼g‡Ìw$ýQjœRÞÀíQ¤—0˜±ÞsÆ»·EÞ1C«¸ NÒô´äY]áp½ qÓJ£Ô€–ëשº’œìC–+XÉ uq퀗nSžŒÆi‚Š£Aš~ žj‰ìM¹_ê!JYy]tØ „«eòa�¼Æ´‘ãg¿"Ï9¸÷MÉÇ=ɳšŽ8¼§ Êæó¸ kËvN®�Éßå=˜iªXLÕ½TÊâŠw_ÄÛü¿TÄì“KGåÙ•s‹’™Œ„‘ô]”÷0V´gc OmÀÉÙY/S0Äm‘£ìxÍsÕ¿5\Z°z²3~xi…1 ñoSÞ;QƒÓ¡ŠE[U:7¿­AÓÞAÕ8ON$¼‡ÃZ¦—{ß2q¹¸ß2eí}Éj¬4 x däÝÖ`¾‘—2{Ã\Û$zÐdÖH>H5si6œ ú9-ïõ�y1¤ö°üÆ›Í]Šô®"²“²s~ÞX.¢Çò?7¬NEÂÈ\y΃›äž6zöüÙF×9/ ¹B½ó+rçh’çõDÎv&¸Üß²z rgs°!(ï‰2§(oyë" nɼXóŒh/•²JûlȘGžµT¨8,Ìðpσ5þƒÃÃT®Pÿ,¿÷™¥áºT#yÏ|J!¦b£¥í]gΛ‚È…R“µž»X¨Ì5.!C¯½ßÝÿ«]¡n¢|)´‡‡Èjçr|£i š^®¨¦þÚÆxœ®yǘÿž”°îï7†ñ9¹Æéú¾\û6EVÀSq* f,K¦ç!úµó‰Ê ¢sÛøm›òñˆÉ›ÁÞN@—Kqª‘éRqÑa?½T+{±FVåV¤B¥¬aòŽü?ãÜ»dFWBïuÜ Ó<•GóZ¢­t[šÿ%ŸV–J쥊lÈF°Ÿ×åži?F¹í,äù«#É»}M“mH(3ªºüœí)Ê{–¥!¬È3ºdz©�ûgÍó;DÑ}«¤…D2ðAñ3kïEüŽÅdüLaâ§aš3¿Õ¢>{°”õv~.þ9Î{*d5#ú[¯ù 8Ìs#5ÿvûE¡¡AY­rÏDü÷U¡:ù�8w?%ôöéÖ¦r�µG ßhÊstªB+/«_ÎWlŒß�ß�ÔéÕÂn�œr°FN{Œlµçz•*z„)ÆRòq(/Y#yZâï“+ÇI îï?É¢Œ¼WËW»BFux�Êóû6íSFw´v®F®�7ú…ŠÓ§E¿=*cÄí*]XO‘û3rû²¼ošÌ–™„ðÎùy¾"-ÔÖªŸ9#³[Òž��†Ó´Ö‘ ÿ¾½{¯”·‚X:‘Ã()ŨÙ^j¨BåÊàœyælä9þÐsŠrƒ;Q†B&ñîuýwš0ñówR>?aÂÙwa¤¬rX Uì¤x…ZíÙ­3 ÆÏü.44¨Øóÿ;yÆ’òÆçn®�?¡l á 1tõl)\±¦¬÷ š[6€ÜpàVô¹Ç†‰+Û¾æ)¡B¥b…±¿æyº‚G!ΘðV­¼¯4–&�mï—([½�Ê=$’ëw™kZf+÷¹RZ…ÊqÖyÔÂ*T*ö<“ò�ÉÛêdRžWó…Ãx§ü[ÉÏ2óÏT]]¯�yˆ²¹îM>SõNã¯E5S&ŒÖ&èIèп|)löIÚMK^9;¨®7”U&sTäXþ— EZ)bÜ8£Ü¢p+54tW;TìÉOî]O1ÎF^[^µ=€_ÊìÕé�âC/!ƒ™d¨ÜKQ½0ó’N &½î÷ü·Œ¼®Îœ6÷·Q^ñqãá—”U Z9ôÕC „ßV¬µCH^žÑù_Î_[²<¤õ½ÂT0@ª[ Šm ";aŒ$ëM°U#ÖX†ò‡o$õßHˆ\gü6�”2`òÒ£‘ûz8Qà`?ü”°Ûتi ïgÅs$MŸ•ÿµ1Û*mTRqaU]£«EÅÆ}ía<â¯S§GßVÆl J&<¾ýž£„é�Û Êz$Ö õ³÷Q•ý�<{F2¨ßr.$*Eæý*ÞÏòó�ëS åó$oœÂpþy(Ê{:'1Sa0+ º Á¿)<äoR¸GÏslþI+¬�ÃT悊5?OÓ*1®}å§€_… ¸m`Ø•¾=ê㓆Tœ;ÖÕ£SòKƲ"ŒZ¡ÆŒ¤b�d7"ÛK1’^üy驸ÒW{@¼ÍäY /\ÒùôI ÌyGÞ©=ÞØB¨eÏŸR^©iåsN—UŶ6èªæÑOSÂ0º—®m ì-­I ŠåwÉÃóæw,¯¨-óeÉ4XCv,ª™Gõd�‹Ü!s­`(¨Ïý§iR¾Ý^5÷’3†M&é‘ÙŒ¾:Ñù:6$Qx�â‚)¬Z‚jÂûÔ¿�?mS¸¡±›ê÷¿-—Š^º”ŸËÜ¡>W㮾!¨ˆkt~=òÜ-Þo­¬tnÖÎÏ2s’�*{¦�÷h�E æÑyk,ý­3ƃ!Ý95’ÁÕõæš– ¿'Ú+Îõ˜TW©Èœ–©Ä a,E–²áüv(ŒrÿlJ™IÏ_(_(§úW›Z±¬ø õ$§Þ ö%Q*Îóú{Vu•¯�(äs�óŠ?UŠwÉî€eB‘yTÉDÛ<Ù ?a<ÃØ¯\Úêa š†…@˜4¬ó¦‚XñÌ@Y�_Wq.µJ)«¤4\vU¦fÌÊáðŠ÷-&ïZÕ”Í^ݳ‡‘@ºÆUjèê¶]ñ! ¥†çG»b[T³%§Ï¸LÖZ“?> r…Ê÷ìØLhus¨Œ– *…§#Zj¥ÏeC­‹þ&VR7úh™r4GùçáX8Ÿ;¨˜”é šO´ÑeÏîQÅ©Z+¨““vºÕN×pþ~%�®×)ÜË/M•EÒ¾dwÀ @ÙhüÂÊ[p¾�- Õù¿û¨PíéO/Káæê^Š,˜ 5\�Fñ/Ðð*J»D ‚o,“Œ•çOk÷”£ñ(­³ Ü^ž©Œ«ÜKÝÃ[¹W²Áøjø®’9äCòÿ•€‘Üx†N3†ñ*Þœ�oJ�pN˜<Ìiô’‰ã ‹ µ1ß–Êi¿¹w²Æï’Ý’ÿ¹Ý¢5>uȦå§­qx~EyïZ�„Ô)±=Þ3æ¶¼Ò) Ùüé0µ_~ãÜœ;o®}Ö¹o8…_=)•ÅÏ߉弔¿ÅJÙœ,“O^ºêÜWäÛ€ÿpÿ³!}Ûós£ÿ7 ¾·\8v¹wÉ¥KÎ}ß¹¯¹{ÿòÄcß/Ü5R�èõqç~j.ß‘<~îî�ëj…^ b»£BV·ñªÄ_¸¸Þ’Ê•�È´ûýU¹d$Ë‹þ·}ß.9÷–s¼˜ì-IwÎûŸrî�ßÝ¢�ÿyzà8ßçÜÓæ›«u „#æ.×»šV€ÅŃÃôiÊFz~íÜ›#YzÝ7àóX/£”�ª�Ð爞Îôñ(þf°.$jì{¤. _Ýp\þ3’}7õMÑÛÈHž·ßS?ÒØz<ð¸þ…ˆÝ«åŸg²çå–VÐú-ÓÒ;'¿“½P¶§pÍÇ  ž¿ }½§GËŸ?õ{³´BsÂÄß¡ŠQ * Zú9‘Éö>ùòO“R–¾"bä¸gt#à_ýUΟ�%}7y ìä'3÷.{…/�œ õâ~4� ¼}l…tQ°îÇY:¡a¾~§ç?”WºU頻T†:OiµnÈiAä9s´:s=AñE Ý>+T»¿oÍE_k…]�Yì`p6¶úåvÛÒßVضÒÈ—8È[I)q IʧøúÊO…ºÐÛ²*ž¦±•>å{ê[)e¦æ¹ ”¯˜n‰7ÕøiS¾rûÅ´7­ŠNü Uónð3Œ’Ox[ÓÔ¯'ϰǢÆ^ïuñÝê å‹8üUgíHB�’J¦M°‡ÑO›Êc{ÿ·Í)%¾NhÛbÖ}Éq*‡˜OaÒ•É›�ìºÎ3”�Àø=.® –µHf�J‚ò9Ó¦W¿—×5òv;\�òOmÚ­5¥ÕÝ`!s†((ý”!Ùnâ]k¥�õ 0´fi¼Ú|»ø_w"¯ˆŸ®¢Õ£[FO}ʲQ�FFrü)ß‚dË¿ÒÔh ¬*´Œ½~”ŸËštêØzÂë‰Ï™ÿ7lã*A'³”p8F…>Fôä þ`¨‘DR3V¹Þ,S'ÌÑ¿ë Ê÷¼êœßÂí>¢£# rÓd¾C,S|Öó»MÇV�T#)²ûM�­C gã®g(?Ì`ÏzŸ^KL>±¼Ñt¸–ËhÓltîËìƒ ˆôNw9w·suyæxÓa�Äÿ¬µ¿Z²$×§IEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7IDATxÚí�ahÇÇ¥‚kê!(„£� P1‚`U$®)-¦" (&¡ôú!`j*åHŽ@¨±?Ô_B z)LLH0uBCƒ± Q jLhð5_jDŠ�‚‰1^çéÞÓ¾�›Ù�;Ýé¤ÓÿÃÝÍÎîμ™y3óÞìÞÄŒ9Dôš -æ\82êü8Ø|cÔHEÏ«.LKÔ÷\ø� ?va~Ôùp°ÙÓªÑÑ=h[[rªò§“Ç•Qçå@ã*✬2¸pÇ…ZÂ9³.4å¼s�ãÙ”T…É·3¡PÅÀò¹4Rd½×`_¹Ôý¦ WxªeÓò™ïX(òˆüª²²O’�µŒ:ÿ»» DNë.Ük× åi«G�ß!Ë¢tc§ºß¤/ž1ñ‡åËéð¨Ër e¿àÂ_´R$~&’^û¦¤¯„*K:ÁšTætY> òÇ×9?b=¬Ểç’A”'?saCä¶.ƒ*‡£,_ ÏS‘ø9©÷k.<¹“zrþGÞf$19¾+r¼ï ßeâ¥ÝÞ¯ sÞÛ—JRúÓœù=HÃzcUʽ)¡&ò¨šAµ8—åõð¨Ë¹ùè¤ë.cBz]ÝtáÞ1Ö/'åz=é.°¤•롆jÒÖ$˜�EÔó¢Øh'3kž¡Ž@Üá_–ÆØ4ry‘gÊ}Êö„(UŒv¦]“UÈL¯×@Y«&/\_Ÿ»ðV ]E”Ú-ž€ív>{,Ó®·¹/+®¤ÝÜ”º�“tÚK{Âô£fÉu­Â¼lâ¹Nfi�[�dÒ^ÙØþÄýà*|í”YrLgä¸Nì(o!Y‘zøŽºì ²ár Ä¿kdµäÂbÁ5¸�±Iü�Èé„9V‘öÈœ2õ�ÁuØHž2Š~1’nMÒœq᥂ëm¨‘Yèœt´Jäü­e~s㘙4’'Ur[Œ¹oß«H ërÍ¿ÉçEêlÜQn³‘{JG•šÓZRß\÷lö¯Î×ÎÍ“Š¥Þî¾{ømfˆ÷ÑUÄœü¾nêrÙ(±+òÉõÿpà:m9>M‘]Ñ’æ�:°˜xn—‹dVÒ§öŒ+Áå嘑Ëu“ï ‘“–�<¸i§<‘{‚N�äû‚È}Cê£.¿¹OOKš�M>¨3)zÍüæ>ýh$­ÖOÅä‹õçm‘ÁÔ=éš“òsš÷E^~š%é“·ENïî­m‘?ÏÊçe²=ÛŸ÷5N°É'Wð}©„WI6,ɱŠÄß‘P£¸yXT6ÝT½cåØëR©u¿±«²7¿m^vPN.Ãz O|¿[Ò!:¸Q¶úc.Kƒž… Šc«Ñ{çñ¬¼o?‘©¯†‰ãûÝ ¤�•´-/ÞZ/†>X @έ�_©ô>Zg|?^e]ó2L4Ýz(_”M´X¹>8¾$×h‹Â<á·Êò¢ L{BIRÇ-ÀÜ#ÏêA2y Îêû‚¤{�Œ©Þ“ÝZÂýžuácÊÜ*–Ý Ú]ëŠù>ÒMÔYi¯S·˜ÛÌsRW×$®î÷uÑyW�îðÛØ,e‹T›�³uH«õÞ%?i_ººå~7㟕>Æ|¸¶Ö÷¥5sŸO<÷JvIÈàÅðÄ|­ �5íÞ¥üº*ƒQ‰_œ:SÖ!oÝT“c<Ø}¢ò�,xÅlÍï7ŒŽ´}­jò³Iëå'8nýè^™94©³‚eæüº§¼ëŠÛ O8tURâ8¹€ÔÙ!ö˜é¤Ú8/ð¾*_ û‚~T÷¶÷ÛWüöœ©^:uÓ`úHžx…`}›”÷÷lJ§ä8~½âj2UXA WÏY§ ³!ç5$,&Þ“Ó¶Íï¨]ܵSzñ5“�== R;Ë)3i}—>?Ky«B©’.J+}�¹å+LùÔ|5#Ê’áÕÇ;§ýs�º­>[þüT9Jz®ÿ¶\s…^Øáɳl¢Ì÷Ò Hv@ÕÝÍLn° \cJBN_™cŸ¤ÖUBÙt²P—߬ï‘YlxeS’&]iT?U(ó%sý¨ë¨-×ñûo…ò ¤9ïØ\x#p?­{ö}—Zì$=›Ÿë)é$”™x€yž:&®85 óÊj¶à|m(vðúÐ…u/Wì)ó»ª�Kði“�*•l™7 9i@•x.Û’Ü£% dIÃ%êø9¢Š1A– çíh$ wÌ'ÌoíRž�DÅÆy÷ͽ¾âªH�rXŠ\Gg²›d|‡‰åþ‡^åÕž2›¢¼Ÿ“Ëɛ͖Ío5j}s{/]ÝQdˆ¤�/KKù �õwsžtõÁƒÅ+ÞyÚ>ÖL\E®uÕK;'ñ§¬Ò£ì·C&-Q¶«»júßJ0'K™ƒý,�öÊ6^ù.�œœKúü¶k"p¬Ùo¿5ר‰¼µžš”M4‚å4ý­—IW«àþ o@z\âfL<›…Û‘s¡kK¹MžÁÔ½r’²Gp¦½óW%ý“þíѧFŽ–ÄÊž˜"´¶x¡àÜ®NEŸÁi/7Œó{»R~kY.¸¯6ä•z_Í$w�À¹Tv®ÈØ>Áákê(~n´‡åû-*P)î?eîšìÜ‘Ï5Š›ïõZºŠe¥r,¡ÌIà|'›«)›UsHÚd£õ(+Ç«ÿŠýQ ÒÞTaô¼‰‡ò¦Ê²õeJ¸^�Î*LìÔ¯}_‚¿ú`ù„üáË”dy°ùÀÔµ²"òY5iµ°|¾câuC]ÒêÃëg§"it·n[ÒæþTÓsЪïÀ±3ýö[s ¾Ÿšã)Ë¥-¸_¾C‘Ç~¨cjf]|޲]»¦|õÈy _ùoËÞ´®û¦|oÉùÛõ�ÉYÊLÞ¯JÜÈýø{ê,ã/‰�Ø©­föY´KÎÍU‚û<*¿Ï—œ·Ý1¤“ù+Ô©’óµ�ÎŽižº6K\¯âÂS”9ô›)çEîK‘ãµ@ƒmKã¶³ÂK)÷§°ÿTårËvrS.®[Þ½ÚQÉõ½à)•ó¦MÔ)³Ô¥“±Û`Vλ؃¬4ŸmÊÖ™’sr²¥¼ÿÊ_Ù5}ÙôQŸU#–¥?IäÁ`ÞÔÃ&ìæ•s^‘´çL›PeÙ¤o–(I˜yÌÉË´¹¶N–CJ²â]·ÑK»7åþBò3Mùè‹ÔJ°5I#oâEž/5¡>ZDZ‚evL¾óÀv+!Oe“.û5©‹è¾sÿ‹L+r•áñ’ºh«˜ßïË÷)ÑêÎÉP~7Sïé¸úî^FW*õ’óZ�JÐîFL¡YÅ(¿µa&íxÕN9Æ>¦û~¾L9y0˜3�ÕgÍW=È‘ü²™c¹†H‘çE~ ÷jøJ˜";|å˜*aÝÊ"’ޝÁ+›�E´) À¢lÖJíd"ßUs­6•¬ŒíLëîL$íŽüÁ^ÛP…óŠÔ[‹ò´¥M±û¢Ì²°IÙÞžˆ¼êk°@Äê^Úuhè͈’ ™/Û½(IÊLɺ:× 1שžÐñêëÉÀu¬/5e@½9>ÿ)—É®æïQ6é»ÉÓº)çi‰·>ІÔë$6­Î•äÁšÈ?3íàÿªwN£ ­TmÛ�2ùz±fÊ`-hDá‰Wð^@ ìu‚>eJ®¨õS¶©Àïˆ[ŸH„â�)°ùñc“ÿУ3Êç”­Ž8Ô)ïÐïùßTÌõ—Çr ‘+L‰§å@áÕKt§4uÌ ( ÛŸp/}¦Xe¥×PÖ$¤n*Óljª” ¬e¾¸P›¹HÇñ=½üAÚ%pëßãïúb�ãä=)eJy•Ü ‘?F¢&þž—­gʬ ¡¾¦ÊÞ®C>ÈZè^~=EÊÃeçGh.Ëuž4²ÑÉëÊ%‹½9Iå\d:eñ­”>“P7V'Ø6^�¤·“�týœºá‰ [r’üŽ"+}ÜH'¬[¯¤ð‹v´­TǶu%ôµ'Ûuó;(SŠè-À éqÓ¸g¨àh"ôõ@|Õ\çï”c¡ÉÄK;o®mM¥ln¾BÙˆº7cL‹òÊ;B){d(ôv˜\£§À 3&Ÿ@šŠ¯)q§´Ô©*‹ÒsàüšQ:5ÉK‹ï'\K}hU*°„:7Å'UÉ�_QöÌr×.SêÁ¿'é“&`”=›yÛÔcQ{ñ¹\=µŠøO{¸W•Œ%!¥|‘|)9ž$g¿Ï{ñýì{¨Pd•zZN¸†æ}[Ö”½àá…~Ê8j-䉨s‘´�ˆÎ= QÒVrÏîÇÒÆôˆ`Üï¨dc ^  ñºƒV}³¶Sç*ÊTzÒûY©3kÖYÛEÊÞjc;ÃH^ÞOÙŒk�c[�ê˜ßM ¯>J•CHR‚Ṳ̀å?�Iu|0}=Gk®Qf&�µ™V,-å­&Üfjö5%ýràZUJ|V²‡²òjí7rMmÇE�ó^|ÕÆ—(Éœy¯DIæÚ“Äóà}\uÂNË^ “ãTâG¥Î^�\½JütÑy%÷UYÖä³nŽ•º]¼k$/ zÈŸNfôÏMb»ôYç„6¯U­ÌT?ÒÍûÇ úWPo�DÀ³T`ò UB �βŠÔ*õg¢kË¥ø“g‚<#Üõ÷åòvI”¯5³ÍYYQ±¬T9ìt@ÝoDÚL—r•cMʶøŸ¢üdë²—V7ÎÄ^tŸü¬d�å±î¶¨„ž[nD”äª�7×ñ_[w5¢$“ÔÝBní±µ"õêúK¡2õxµ,1²{6ñÜ*%lªê3_Uʬ›‘4:`B»Äí$¾«�H¼ZôÊ\Q½( (á94’‡¤½8_9rc±¼§nÛOzˆ}2{›ÌÎ]êø¸ØAÅ>�ÿ©žowøæÞ?;NDÔØ¦´°XI´)àïñÞ<±\¤ÿ%ŸQ–ÏDŽ7}%)OÓ›Hi?ÿc™(½GÙ3¢Å& =^÷WFoõü·y”ùbÙ,½«“|3 òçOM~ø Ik¦\:ɲÏ/ÏRöWƒ¥– 0DÈóSR~-J¾û~+€]‘J]¥1YaQö'ãm0•ãß—ÿô beBþÓqÁ(ɳ¦?ýÈWˆ’¶)ªÒ–¾yw?L*z”ËSNÞ¬Ó·ÿt\0m¥éMäy0}ÃK«;ÊߦìÞ¹…‘¤«Ò}æ åwÐò¬¨îUlîí<äí:gdFX£îÇ!t¦{fl(;�÷+äí$§Ì48Vƒ…Wæ­É˜|¯Åú“×w/RfÞkјîĔɢ|Ÿ9èP2Ö.ÊÞ[Í«÷×ÉÛì'}‡'_릭è¤äûO€uþ´AŸícsÓ  Ì_5¶ 9ˆ 2~RcíHzÕuþ¼ºË*2ŽP籯±œ8ô ƒ.kÿaùŒù®î© 0nP�;)]º—(ó“éÎÞ×RÎÝOÈ$ãiù~ØÈ©ç×„Ž EÖ®HúŠ„)Ê^My`Íå€1‡zx eÿ¤>´.ø@ÙuWż­ìû=;�‰¶•”¿_{žºÁ` _¨óO2ÉfLмßuÜÿàçf0àÕÕÊί¼‘ÉEéÛœLúeÊÞ µç9ì—ÉQg°7ùÕäää•Qçe/!«°“.|Ë…�88}9ê|»ÄÿuŠ»?båá IEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7‰IDATxÚí]}ˆ\WŸˆËBDR k$&öWKDƒEœ*Hd­DÅuZŠ«°*ÑÐ"Œ5Áõ�Š“Jm±†`¡¦ıX[£bbbiÉ& ¦ñcÛjlŒ_Ç{òÎÙwÞ�{ßÇìì¾Ù�ß.oÞýz÷ž{î¹çܯi4X3lª»e@D[¯vÓ¦Ku—¨Ž6»Ç.y}ι:÷jçn“÷#ŽGþ\w9Ö N0öœëVLÓtn?¥ØbÂîqngÝõV®}?Hýø‹sÏ8wIÞ©»œŒ/^¾–sï°{üÖ¹[œû“{?î,Š÷•Lþ?ç¶Ëï§œ{Á¥•X$l¹|‰…«{ÿôZÖ X=¸öœr晽ÎýÛ¹8÷;çþîÜy×Ö÷™xs�„¯6>dÊŽŸ§�;'VÅ·œ;T2ý¢sÏúÖˆû½ÀïbùÎÔ]Ï!Ñj‹µÂÇ ÜŽÖòäAÓ¹‰išu—}” tkñ Î8ÓFeO$lbÌû×ÿ&á“ !?ëB-k¨²&úNçŽïÎâ¸�“†úëOÜ»\ü&¼×H,”[œÿãuÔk@ZpGgëêâõ_ç¶9÷qyÿ‚«ÏÝu—sˆõ]p�?8w_l½SÿÏ�{•s¿uñ^_!Nûg—楺뺚�>ô‘FÂ7ºžünçXé¸ÓÕÿŒ‰{Ü=®sî“�IJßè´aY1+´±ýé¯Îý̹Û}Þ“Yî‹; ÿœ©òÍõá™ÿåô;Þ£ò’ MÝe*B´¢i^K+¤Sÿ X $–ênç®7á÷¬7 Õ•u6°.ÈuØ*kÆmçöÕ]ÎÖ±gêvÑü>-B,”¦kâÍÚvGH_Y¶ÜsO€oº†n—|ÚŠßî;u×g•éôH ?q_ºUx.¸Ö.3B-CK¥õÖºë啳ò®d\–#$Í!#;÷‹,�°³>†¸Ïí.‘?ö­Œ\£ì²ƒd$ÎyjƒŸôÔ|U™B:j3 àî5ˆ›ª4Â霵�â¿ËLåóé/S”§Œ@lÕ]—@ÝÖø{JK¦ãB¿¶ {O(ê€Â‚t»ñ?húÏ|Ý4\m1uç>õ€? o-ÒôÂ[†÷zòÎ}÷£<ÕT¯•RGÆ 94Ø)òDyFë2%«Öɹ#Ì'&½,¬ø¶ ʵU¾ÑÂÀ:b�F¾Â.'ΔÄ’·KØ0ÑDñ3éz«X¯½Î=ï ¢!öÔ õ–t¦U´µ£¾ù|$¾ öS>ýE tbᣫ ¬Ñ÷T8^•çœsÛ â2íŽya3”ÎÌ–øæÈ)39åµ³W¬¨m‰Ä;,òƒ|EVxÏ*‚ûdp~ŠRKoÍhB‰Uyɼ³åx0·i”�sªtEⱊƒ¶[dq7ö-ó½ý’OËóÇ['(Ѷ¿! üKçvEâ,¡rØ g!7_e@ÕÎãùí.Òμø¼˜ÿÍHØ‚8½0Õ¦Ÿ–xç¤ó­ÓŠÀÐi.î wÐ*LaIÞÏo>6:ÿν(åÚïPªI¯éàU²¾4`º¦Ôívçn¬�®%ôT´ â÷b´3O7'}Sâ|ؼ¯ê¦J,žŽô§Êüo”° [qï5ƒ¯Ï·: ^÷eÑ1quJuµUþ¾Ý|Éuû˜ÔÕ/·Us%ò§/ ]TñŠ-ËÜ`xÒ–ñ)ÇäjÑ(a’®þ¯ôÂïtî¬Pýéškj ß¼Ý}=+ô)�R)e çÆ§¬%jÏÎ^Ó6�û�s›�OÁ�éWƒ2%e§·Ô²?;l&7Â× š()QŒ´\1m»m$+Q#s¡‰’4@º¶iÛ³Tá6e×÷JYí–‘aLÿG�›Ž¤=,IÉûnùþÐxGùíï%è[.‘_�R«‹Ëº3'nË =ÊZ©vPºJÆÒ“>µË‹wDx•Û·ÒìXA}Tñ¹Gòg+ù?íjË^¨„æä³�EAy56¨*Ÿ´�—µ+Ïy²ÆòÜ„‘ „&ëfeMA¢Ñ†èza̸Ç%ìd ýóÔoÎR:Uóù"â†,�¹C,f“—v¾üàf¯³ùÚíô Â’Ò�ϨAºHPü*OhJ6þÈn6£dÀcáa7�èš1ûŸ?ÈÒ£þõ½…‚ø=C;¿_°ÕuEÂ&i™ÿΚwmã¶’¥Î{K�7Kù§Œ¿‚/ì˜a¹�RÅr¶Lþ&¿�¦Î§(§¯QŽ‚âÑ93XHø´8ÆUo¯I?ðŒŠ0üÍU…4¡Á•®�âÛ¤DæöÑÀÄY$OÙ¿3Î=dò�Îä´�å®×›)5  Bÿªä=v ä\ªBã·Š;aò_pPºéÇbÞK×¢TKfAñt M‘¦dH‡;yß:j :Z’´¥vöyßeÜH"4)Ýä¯y0vS:%Ù)+Ð(@µàN®k;Z¶“·�“¿¶ùHÜ‚DÞÚ¢¡Ÿ�’ý¥ [¢‚ì%¿ëOéN;ÇâR²ö¥ —¿Yîzç.x~”VH2ŽD¾;%üËgÆOS`ÙÃäÑ |«Ï¿$�8mWÒ.ˆÛ¢À:¢GçK‘´Ê“º9¬'¿gÊ´KN™z†.KÊóîyW¨­ŠêDåd˦Ø^åí�0�U°†O[ü.K»ïòúú„—Çe-N–ñ{%¾�ÍSZÏQ:&èfÖ÷­ ðӔMCX_d:,¥Wr£6Å�­ãÌrg’†\�4-ò¦h#ålÙ<"q(�âlQí•Í^§~¥UéXþLÛ9ó®¬Ký¸¿Äw4½�öYH±Ž-49+ñøwl‡÷.SVx¶5j„Ä«ë6�P2­š+ü6ËÜsq{~yMøGývÿƒÔ¿á¯k„ä´ôÅ'�»7�/óëUéO[)]‹ IƤ—^ù©CgM¼þÂ<5EñÍIºÖ××óúµäyÆÒŽÒ´K!ZÐÎ*ë¶Ë;Óý´ ¿f`Òõ)]”XýÛq‘6$Ê™ñ�8§#<ÁØcü:æ»zì¨)ß:㥷·(î¿yOë34Åkì@©¦Ã»Ün?%`7_GË{ßiâ0³íóÒYÁXiýTÒ,w<Š ””NQÿZ*3æÊ9:C©‚Qº³Jôë=?í¨vç Óõ%é4¶°5Y8€S|‡o.-µ#RºÃ[…èî@Ü}¦ýYA)Üp±Z°|&ïjø“òSå#6«3;U©ÕÝÞØuˆòå+¨u*óî@<¢T9�A{ÒË“–«”]“Ô¾ìÏeèhü—ûyHXRzóS+§Ïét)+÷Âõž®fúoIºùVÿtȈ<˜§ì^‚EJ-ðè”.e­T]ö×�5Py&á@$?x/{mÀ¸hùÔ”ÕWPމÿf“^ÓŽðõÃ+¡™ ÏŒ~ j„¸j½(#,Ê{hpO´£0<]ð�–e~*1ÄòP§þ)�YÃÐ:u›YK¥ì4�/”›Ê0U:«æ ã�ó”y_$Ïê§dÚ¯Ô÷(¼Ãw™–T`‘›Žt…Ò�µˆ×¢ìÆ-V2¶7V¡o¥õi�gtSÚ¥@<.ïóónRv³ÌÎH¾Îqï¢ì™Ô)Ógt`9ÉKÃy€œ�ßW"BrÁøM˜þhã²ßþÀw2BÒ§£çß1y‡T­?åäÁ4x ¥ÖÕ”*–Nª¨v½úEe� Ûñx“ ¥èeC‹2ë¢ÖJU¹´/�WWž:èNEÚN÷�¡a[Þ}Eºcãü7{ï¾ÕÛóikgÑõeu „M›6=ÖH/:ÿ,%ëy;œ;ïÜß"É>åÒ½(¿âÜk)0øü8–O…¢þùŸ:÷)ïw(Õ ™ ø2w¾îíËÎ}Ϲ›�ûµsvúãR7ö?ê ‹£â÷�F.ÙaoÉ û…sß—¼Úòíö:2÷{©€FZî8·åDáëî¾–—·µ\rÏåx�”ý�x�Ë„oj$|ÀÏcÒÙc–Jt‡·€Ûm¥ëƹ·ÃÚuò| ’÷yÊßxÚe{�yæÑŽÃßãÜ3Î}Ö¹ç„g˜WNˆ y—sÿrîOν#�Ç÷L^’ßÏ®Ý;oÿtÂýæ?*8*¯û¤.,ùúÐïzuì³Ð †ó�앤!påþrK¨í…—Ùzf>fyñAi‹§ø/ …o¹\|µ%_Ùx›s/4Êã×.Ÿßx~/È·+_{J‰"µ(õþ‚sßæëÿœã«a¿¢mä^ßV”—)Óç âÇí¼£‘ðÉA¡ñmR÷?:ÇùòÑ¿òceQeÃ'ä©ï[E8¸Böñ×+/u¦0ÆßUÚ( J§T“|"/³Ã—Ë�µ¼Å _ã“AðTcP²‘J5ʳ”.²7©Ä¹OJ§òn¥t—¨j�dèÀkr�–È/¦Õ5­V—§å…ÒâÓSdÃǰ@ÉôšÒûY ¬yS2ÕþPN:%kQ4 fèJù—W\ŒÐf‹”Ÿ§dCWàqé1•Í�ð m)ÝMÌéŽxõakS×w?È‹g.ÈïÏKš^œ.å[Šº!ØÖ”Î*Z&|™¯¨�µòEö”,§XÚ<%åºL鬈µ´µn!«þT¤ž½2}&R>�YkÊÂå|œ*ìf¥¬•êO¿îºòZy›àA*ØE/<Ä}n«”)dÍ.FxÅßIM94<åùÅâZ©| b,Aýe¶â„vø²@:gÒ] 6 dE·òcî4½—×vJ­&gÄž_¤têÄ_CU&lzïÁ)Û߉¥×=_ÔQWH§¶iãyù&+$ª€=–“Ö®ÅiY‹Ž5ù|–wyE“²ëDMÓ.ÏJ LéyßT…Œ»‹"䣀¥LÉq*Ÿ¤tàÛêå××¾–tä·¿YP×Ú|ºQÉw[4ÄKH(Tx2å²<ñâ¶( \S:Å:–[§’e¼Ÿ²û”~<øU=F¤íÓ+ˆ·‡Jœù¥d½ù ¥Ò|EGûbHdÊ!ï¡ãZç¨ÚÒQd ì×ý‘8*¤BwÂê™Nl¿7a™ÃvaŒ�Àí$n·¹+Súk·¥„å[¸C;äžóý¡�þ¥Ÿ+¥6Q:CPô÷p•6±QÿMU‚6M+Õ„3¯Ü!OžŸ‰äÃBò"¥çW½ð Ø2Èoí?³�ôjöíð]) ›*ªŸ£TQ}ˆú�¼M?Œ'<¿©aÔIä”?k¦³U¯T]PËöƒ‚üÞ.ùñ g „+¯ÄÖ–{Þ»Ï+z\¦å§�”‡é2-q&@9s?™Þ£‚©Z,Vö1?e71öÖ]wS¶íTÁ¢¡äྯuó‘‚ËÃr'X ¤Ï(L¼& ù�hÃeP«¬#mÍÓZ÷ ù;=�‡ZèT´–[µ,Q+Õ‹—k}¸°ÏH¬¨ý%ÎôŒMi/*/Qj�èŸV´D�xŒ7˜QÍ÷ž í¤Î½ýŒ’eß [¾éi…åZ_¢ôL9[·!?5n–(`-›u&â¯ËLz�òM�)ñÔÜ :€PÄdeˆ. õ¨÷î¨z`˜oäX·6L‰å¤ÌùEñ{¥;ÿV“. ¸~:ª È½!ä›áJ­ÄZnp"oGð€yØ ��ìF„$[½þ¹BÿêÌÅŸ-ÔucAPúoG¡u»IQ¼ýÝ©'WÒ«\Ÿ¶¸-G‰¢1OñË.Xæ°Âþ~�–*‹ìQªËB3VDî>ìI¼é2åV€˜€4Ú¤jÊ]«yJà)�3Ô?å´a5J¯»#ü?”ßöB‡¦Õ�ÿ–�L›Aá ÌQPºŒ2ð4rLHþX¾õFü¿ê÷'ñŸ oÆ‚’?º8T7�*ÒC§ÿcÓälu?*qfe`8½ž•Е҅Ò æ û‡ z,눗�* '�?¼ë®çXCÖ&uc„j9¡õŸÚ…áЦEÙ³ls^¸Þ3R <ª XSÏ)ÛžAÚ±@HvTᢊ÷²ŠeÂWŽ×M›aƒ’;¶-xpàõ¿++Ï}}ÂðJ7 ï�¤±›ßôò›‘YfKˆt“y×�›Ÿ“F^ÑÁÿõ JÿW´M5®qëÔ¿öõqŠßä5'ñ÷AZËr¯mô¿—YþLê`Rw¹j¤GGëOÙ˜‰]':)O½Ë—/—Øpzp Dåo¢¡ô¯´Ú’nlf=ŒU6¶J+U8ºBé1GÅ¡qâÆDákäÆ2x¶Å²š¤ôHÞØî9 ÈùÓœøM£xm8ÞzyÝ`dºFnœÁ'ŽÊïó�ä*>{åé8bG•ÈÂOC=Ò0Ò d÷ï†ß¼W$—¬Ô]ŽQ�¬%wÆÙJCü#âAÍ-`IEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7IDATxÚí�ˆ\WÇd ¬”…ÀºPAˆ„†1,e°‚¡°øG‚: ÅÐEX„Å@) ⱡ ¥"ˆKA,– C—‚8%eQ kc±t,‚u±d k@9Þ“9gßywÎ}?fg÷Íî~?pÙy÷Ý÷Þýqî9÷÷9Õ8Út¨ �fåòŸÁýçèÑ£4/Øs‚B<ÜU°Út|Àddd&¸gXV‚Û¤a.w¬éx@#p/C”!+ÈG›Ž˜L‚l´‚{ÝRþ{#¸ç‚ë×7†µ/~SMÇö” ø6‚»Üý¦ãöîy–ÜŸ î5cLÙ�ΪÁ ;b@»r�Ã}(׳Õbã¡Ñ9Ô ôÎ…?gƒûap îGG�}©éLi(/x^ð|pŸ¯ÿ÷RÈ�¿7·1¦±þ|6¸?w"¸ÿ÷åà éü•þrøó™à¾ÜŸC˜GJÞÿ\øó�à>Ü—Bø7šNónÀ½vþËóÆòû‘�Üð|òß‚ÿëMÇqˆê×§¿zrvyÁõ­Ü'‚û[p¯…¼øWÓñcFzWLd®Â3-3Ì7Ü2+ëছNO"¾ÓÁµS½¦àÿŠ3/Èó…+Á}‡Ÿm: 5ÓÛvÒs+¸7eXö’éu¾'†6~ÇŒôF•óÁ¯ðíYùËÒ•¦óbŒyªùÁù¸%ò~!Êã¾Ô§S‡µ‡.ú`VÊ?ήOßC»ï=ñB…ô·eJmZü.:õ±/aZÑó³òüDêRP‚©Ê� ϬHØ�äùž(š‰«(bü{_ÔùDZ6TÀMxËbÓi)H#Ç·c®Õ nʽyç™UqTÆæJ� 1<Ýà®÷ZÓy3N$?–MÞnŠ�s1¬0?=(æ–�1å:õË„Ay¾éx–¤aNê…No¨Ž¼ZðÌŒ‘ƒ[òüÓÁý@ë§¼§+aî÷µØxÒ ñÚžD} *BƒUœJ«$l_Â�‘k­@�ôähÐ�.¸ß3B΂:ãÜ{`l�#ØÛ¦ÛDúJÒΕ³ùi¥î<ÇéZ—pkä¬ö6ÿVÑ»ïïÔ}fÒ‰&³ÜÃ$£:”õb5ßúV)šû3£Çb²¡l±#§ýzp�‹¬iñï÷%Ì�IÍ ‰ÛïÍõy‰óIcd;Îsm©/ÌiX.9áÎРCrGf³Ñ}ö{µé|;€²Ë%¶AH…á…'—�ß TsÈWŒWÏ\·Duj¼ƒ•Ór™Â§¬M¾mîicàÝྒx¾%¢rÜö )³nä·mP¹B;ÏÌ¥¯L;ẦñÄïš?RƒÝha�b¤Ef»âVäš§9jIG•?ì„™2ù»I¦gjŒòDô>¤uLÞ\”ßh„µI÷"›C ©ObPfãïP6ìÙXÑ ÷¸b®y›á¿ƒû™­Îsó‘ŒPÁ7®ý³Bù†þ«ò�—›ÊƒDœ[×ÖÎßvÀ ™tV —+Â¥D˜slPÿZ,ß¹e®U竾3j Ôy1[ªÍ=ny^îq,&Þ1'JfbæS)›ËNÔ­8_h0�ª|(•v»Õ…UEK4B/uÒÛ³2S�'mù=-Š;†7¬.Wû²›··*Ä…•呱YãÏF¶_þµ]ËÇy‰ÿiÊï+~Wi¼GxwÏÔÉ«zäq s“‹Ù¼<^”zwq�óçy£»â>6ùÔ.y¾’^ŠÒºpþöš§k£gk5rGÈ mNQ¶ÖF¹°›ßÞ÷Ð`1Å=%-÷§D _�ükO Óð\&ÚûòW²Lmk°,ì52ûѽ…HPX ·冀؄vqHË;Ï ê\ß–I—Îë°½/~wƒ{'¸õÄ7¾G¦—º[i©˜^ožW~°!}QâÙ î˜1]-OÚáZ6f�ŠS�9OÙ¾Am±»ŠÀ¼—ßÉ‹utÁÒfÁ¡lÏ�2âa<6¨KQXê»QÞœ¯ªŠ”åçQ{T>~ÎT¬+ä+Q­¬CÒw›:2³yròÖÍ;Ê7ä¨ä]qÜUIÆs—¬$m®Y>îI}RƒÂuñ#2#E&®§œoo‘?ŒGTlèô�}*n°1·œûVá^.øN®¬Ìõ¦ösr�2ÍQ¶EiÓ¾£f9ëŠwuñ"6Ž�6t¯ñ^[ÿ”¥’ðC²Dƒ¡íÛ‘ëˆû‘—Ó%þIþëݺ Çi\s¾?$e²M%�.`� »(¿µEo˜·…@s”Ñ»ã‚Say1 ”=ý�)¿"O¹ ®{àªôR§DxWM¥].yFóà¸ñkÅJÂø­FÏ·‹„8 ›Tzäl2¯ð>6ªÚò÷æÐ!÷X¡?1jY�B¬0MÚ¼mAz��ÉäD�o¼F#ìÁ¥á^êÐö™(îÖ ~S¼ìa,gšë–yw¼—õ'¶|D‰zë®yõÑVŠ‚8wÉ éQqƒ-W6&ŒöÙ �L|'W÷)Ñ’º¨ÌE÷V«Ö™‚ï[x1™�óÞ½qµÆ{µþi£Kô—au #6¨Ü@߈ürûÆ)ßÜ6þzj™•³{ YéÙr°rR"#D˜G-‡U‡\4ã¢0¹B ¬E´TòîXhÞãË=á9Ê6Ïw(›¿mQvšM�ò &¬µ¤U9”=6¤ÀÄÃÞ3yÚrž¯cP·•žsßVèEª8TK‰U½4º¾$÷JOÔ*xí…$ ™Ù*¿&NW3w)} ÖçyË I=V³è$)þFj¿†‰çP)RŠWk8»ñsæÞrô¾Ô°Ü=Omä_Kʺ-ùËéå�¾·'sZnñJÞÔ*ò¯RÉö¹Èß«[ËUëL48ñ=ùŽ®g]¢+|+¿iÞ—[íKƒÆÝö¼p"}q�c�zÏ\kçÆ“OF¹._³åžˆëE:¸‚Œäôaoªes/ÏÈ5Š7LЋ*ƒ®½GÙ‚Š·œgbå8nÒ¤'µp%×­oP¾§ÌÂÁ‡T\tžWåPÚûNø«ÐÛFŠ×3*â(l‹ŠçQG:™GÞ»fãkî�2:t©Î{Í;(?ÌVXÉéµÇFÅyFX§ÉÌK›ïñêÆ*>aKWÙΓ?—ªJPO»yÖ óur ðû­ù½(uÌö.Ür¥¬'£C¤=¹Ž‡�Ÿ4åä¡ÊrV�?³”Í5·ßá郛’¾8Žl”? 3Ê@‰Æ54ºÇ‘W6`vˆt�ò+hkïf�èÙóŸuE:§_O]Ú>à!z^§Z”õÒã“ÑRº$· .•O”M™¹#ik™¼9E0¨ihÐêfE¤Fò¬æ–Sß•ð<÷øªü~ÁÜÏ’ºÎkvMåd%³k›–ÉÙî#qY!¶-@5Æese½TŠŽ¤ôÜT-å ¾=na6ùóUçž=þÅÄó©“¶fÈ'iô™É•ŠéÙñB*>sµMÙ ÚU©jln:JòŒQ’sòûÇQ˜®Ö ç›VIöäú)æ²Ä¥ï)Jóž±ía”2Ií9µgûê¼/7V®Çi¤ô{ÊÊæÇÒñÔ8ë ¯e°ë=Ú”?ª’Ëì‘èùŽÜ»d~Çò”Ò%*+:‚ȼí„[õäÄÄó@!Ú”ÍÞ%>mÞ+¹§­®¡ÕwžÁm—qߢD¯`Ò¯´ìQžž(>ŽßZÉ»zqÚÌ=Úeù­#eP'B€µ È_6Ïÿ<”÷:?]ðN»:T]Ù‰X± µ'!ŸhxHÏ64)›{ÿzôœU’Ú{¾…Q%Û*ò—wéÐîSRwúRn*ËÉ£D÷(Ÿâÿ>ÃyƇæëZ]À8´Å(òßpÞ]y_Ci·k´Ä� {bI™ñAù3Î;Þ–¿*+Ç̽"]²b§X6Öå÷q‰wztÝDlPõ<€‰9nß"Ê@Wö®8÷{^!8árabå…=M»|”V…t«Â_¡¬åF’«%Ïr‹û²ãÿ`ÛŒ¹V¥èÍ¥LœrHԇĠeÿt€óŒ.½2æïǵñ³^M\beÙ¢lŽ•äëä ¿HX6†o:÷»’¯vŒ.2‰ûßhGVúb´&â$©ßš7Ú°«á5/½…fJ†³Bº[V©&h„ù\ÊF-6÷»Úc ìÈÁÔ1�ßs²,wóû©þ”µáž^z‰IEND®B`‚ FRAM ¦Ô IHDRÔ<ßGK7 IDATxÚí]}ˆ]Å)MSRìÚ”uE�— mêZ»"¦ñ«¼(ÒHl±BcPŠëDý'Z¶ÚJÊS Mµ‚þ‘F„â‹`«Æ’j©¸ Át+eØEÉZª¡’(hÓ´ÐÓ9¹ç¼{î¼¹_oßî}»ûûÁðî�™;wæÜ3çœ9óñj5È-¯º}'7U]`îà¾÷8Eàߟ¹0æÂã.\æÂ`�ç]h¹p¡Ü»0Pu»úN Þ϶êz½‡Q ŒI¹Ÿvá˜Äqá¸\�å”ÅÊó° WŠRåëQ X� ÇãM¶U] �áäR¨/¸pWÕõ™Åv‹àωÍg°b“v’(=V¢#�|'ñ÷åHYF•K«zØ”­Ø,ï¬WÝþ9 - ‡EùÞKÍ5ÉdEÎsƒÒ†å~‡ªÛÌDÑL³´ª®O—mPæÍÈ££µFHÁHžy3/(Ö2£HS…Êm~°`9ãta¥»Ñ(Ñäúm¹¶àðhÕô™-ˆ°¬[�¹Ð°Ø…¿ŽujCâÖ ß•g”{ýïqá™!á›1ésCU·s¡áÓUW€±dÉ’7ÜǽÄ]~âÂúªêáê°ÎÕåÅ’Ï0c³P{EÂ%{È…Ûe -¸¼³ÝõSUÑ¢‡8§@ýæÿºÜàÑ…i¼Ã…ë]8¥ñÉŸ\xÓ…¯J¶×åwµ 纰´ê†û�ÑóõRÿç]ø%_»ïüí’Eý§óÜWî/„o¸üÿ¹ë¿UÝÖ.hÃÂý¡ ×ÿIwµkËÓ3(sÀ=ÿ‘\×Ýõ¡ªÛY[„w» ü±¶õ‘Ÿ¸ðY×&ͻ҅®}¯Êa…¼Ï…k]ø‡ ;%þanqe¼ë~ŸQ:Í'°Ñåê}¼êzô-�¦ÄªÍÈsÂ27÷�–µÒÅj÷Êœîf—GTEGc’>¥ºëÓåw§ŒÖÙŠ¼³ŸGZv„ê×SÚ·9kä)t×2÷º°ÊË7 ¶bJF¬[}KÝÝŸMѼìî·5Á3%Ÿ].#‹+%ŽÛQzšC諼ÄuÛ #øëø·—mŸ �q]›‘—¶å6¹fÄnΠgÆ{…OØ vÃ<¡M�b�Ý6é#Þ£NŒ§”aûß¡¥N™Œ›ç�æÅ4‚Ô‹ Û¥þ$~^ÔNa„ÎîŒ<ïHžAQfD)nÂŒ2ÈSʪGŒ‚,¬¤ÍóG)Ûm9b˜yÒÖAÒÇ(9/øŽéLc~þªa”á4yn_JΣêb$Û‘-Þ“2ö  ÞÕ0ù9ß³)uZ*õz¾ÇmMðŒ—¶Œb7šå+âߣÈuÇiu­cêc 9æ™»)24öšº0-+�¯—zúFÏfó-¯¡H�úy¦„nŒ¦ ¾ÿŠ”2O LÚŒ•éãsH/þf¿‘:''%]¾ësÊÑþ7iÊ`:ï"1~)2ê¼@p�÷ü`Y¹: ´hh’ú0´°Fמ�¶4)^P”6�ªm="mÜî ºÂ÷-¡áú”w]éÑb}F½FË|¿‚mñîÕ ôêÕ”tæ««¤Ý])ƒujÚª`ñ1M2/Os0Ú³²b#ãås/ϸO¯@9uá¯é¬|9uá2T™~@aœ=›ô(QWÛVý¦»½<¼é�¤ç~GJ]$t¿Í¤+OK6il˜^0‡ígãóHñö‹•¡úÖ`–BM¥RRÙNy ®ÆÄ¬0�ŸRV%]¶!…j;tª¡!Ê€çYá¦äišÎCµ”ù~ÊJðŒÄMJ}O¸Ñ$ŽÔ*¹^EѪu¢Y%zÂòx .MéOªP•O{>2£ä‚±‡boK#�_QÏ(s4‹¿ ÔI;ª2�¢ØKÀ¼Ë†ëÅ�v̹ûГu©ï˜§@ZÒî+Sì½Êš"S9ê7ÂJãÃÊ«î÷B–·³ÔÖ!н,×6PØc§x$ÔFOþ´ºá‘!á<,„{¢`9Ü·¤}$smž,ø¾Ä\jN^«8mGâù¿çCBH˜§gó?®¬5F¸Œæå{s‰rÚ/…é”chȳ;%ÏDH y�gN÷,xfÄÐk4å™�Ù®+\z^:×#dºÍ5]¼‹Ëº#%�ûæqù>KE`ZžÊR¨�œ÷¶ù«Ë:ß*ÏO{tà:²B} @SÝjr�¼ ç=üLÏÜ£¢h›)VšMê\o È2àdÖ2däSó÷åžÔ±¬r»lÛ…¶Þ”Ü—¦Pyc(¥¼&/õñš“9EV=ãá@àCæ¾N�=�ҹߡ$š&ÝŽ�^ ¾¢eQr”ê3‘2öm�犜$´\Ê×È [÷ò¨°ãôû =uQXá­K”1�jÚZ”.Ú1&rÞUµBÕöÎx¦ÝfÊÙ64ƒz%\z�ô­’ô57™RÖuŽ’Ô¤ dˆ ½l¼ï6qj(�Û¾@ÉQ"Ó…Þ‰yL‘7¹“‚u•úNQ‡(vùòzPr-Cžâel”ûur¿>�—Üv¹"³ÿ[èÑú0¯,5Ï©2mR¼ mÈòIY¡¤#(ƒ5(š Ÿöâ¸Ó!£( �(夤[gÔi± ÕÎ§í¡ŒÅ'”²'•’+KíÉ¥htðT #2£�fò)¾ä=¿¢¬p o•.X[&³ì ägž‘(æ‹ÛEP³PR£oPâ¤ÀöšÙæûå÷ ùýƒüúßù§òû´¼S÷Ëò~GÞ ú–ô)ÞÌ}ðëæYÞw½6e#”¼¯´ã›»üíýÚ ¤µˆ·NŒHf@ûþÿj{¥ŽwÖ"9ô]t$Æû›yçÞ%xîõI¾0Ãz0Ïè~¿$ýu¥©Ë““äýR¯³jÑænpN-}_8Ç[9ÄÊpŸû½ÏËw£üÞäÅÿÐ…ß mx1óÉ™Ï×?'¿©ý?@›W fÝQ‹e`!ÖÆ&s®X»¼|jÕŒÈ=Ï=¾U |òî­õÚ+Yêä"±ö5–.[m¾[z•X¤º¯Ì·ÌVPìö)´²—¼Ð&¾.em6q- »gs­ÁŒo¦v“:]ÌLc»g2k”ª®¦c~�•»«6‡Èâ™Ïr�¯^8HáÕÛŒ”²'•âé�å�´Ìºøü%<çó#»Ãæ^GzùFüwÉ}htÊ#@»ê8k5˜nÚ®õ™NáÛÔ‘?¥Œ~ íìtR¾Óº@^5è–Ê€¢ÑÝÛäM)Pì½*»x�ë™9�ê÷}ŠÖX%½{käØÓ›ôÛøûÐñY²ÅȺ,É›.Ò÷uÈŸ¹D?ŽP/wá|¾ èø±¿Ô¢“an äe j¿\³5óù8LÔ—9ë£ðGø5%[gfx®VrrǽµÈºýW-5·•*EóRŸwáç5cze©E#ܹpŽtòdÏHû�Î+k�£žwÝswd½ÃÐæ¢Àkùdž?ÖŒW) øÖÖ"žX›E?¹ÜQ‹G©> >ãâΣ°¡öŠüî ¤=dÊ⟖&}÷.þÌ]íh»G`yÇ2b%�`ü«Ðêu*7}°Ï´mÁg”­òþ¢ oºöúÛY?æÅ½R‹FÞ‰xêœKgÚžeîÓŒí¼zï�<,N­Ôv9whbÜßr²ÞX:/P¼rìï&O‡•$V÷.Éļ’f¸Å€"wÞ¸)s\êy‡ /åXïK%oKê–·2h’Y€$÷M{_¤Œ‚m]–“> B¯M)ñŽš8õV”=ªÏ–»‰’óU¥éJÞAÝÖEʪS4WvÅsyg{yšRçIŸv¦.!·u§x„¿Îĵ$®îÅe�Ϭ¼¤s²&ç­RÆ‹”œGíjä@‘×§.ïßH�†òˆá-ßÛë|Ÿ”™m›¶…¼T›)cŽz&ß?£Í#9éíCó –§ícø«ÁÛ«ÖMœòÊj÷^€ÿ¦Sx%A3áƒý�|MS¯ÐÖ™† EöÞHÝõYÐm�q­P´×ê…@e€ "’ßᓤ* ŠG�ÃyÌ\²=:GÖ2ŒÃ'ýèQ`E„:Ód[Nž`Û¨Óõ¢÷=U¨]Ð¥ž“>*Õ™6q«%®«c…oô_i4ä2ÞAŠ]h ®t ¹uG)eA–”¡Uá>ÿŸØ*bîuÝ&Niˆ?öJÚr§F,ó¨*Øv™/3ÞÓH‘bmPöÂ7ÿ[q½&2ò�˜ûÉ@>Ýþ4åů˜ËþÒÚi{ûô0i¾‹Ÿ÷ïÉõê\,ª}4mjÆî®`#K÷š¯—w¶¤>{)žÂá ½„~J–çÑVòŒÛÎ�RŽ  U&®’@ñFæÄ”äÍïˆ@Úè=[/Rï�°•ø)JZŠJ—Ñ”2úF@ˆ0>fîu[£Ô�ò|é#ÿR„4{ætó†BÕy¿û¼ø!IÑ>ç¶Ð�øNÁ÷< ñçzñƒ"$Y(®± �"HOÂêPø3 Å²@\šB}�¼=¨ϧùó§¡þ£Æû/~"ô¾~…¡×û5‰Ó­+®m¥ø4­�²Ôé·3L[ÓxÅþçªÒûW)õHð1Eÿ‰|£<Ó3£ Päx#ô‘MþÔþ CK”Rßÿ¯¨XgÌ\ê¦7íhóñ‚¦rÊ9]ãB‰ãÑAÈÕÅ£¼yñ?ˆR×´9C=èœï7JÇÜÙí»æ# -ÆÅÐh!É#ÃÁÀ3¬x§…f»éMéCº§°%q+Rêpš ?–<ºÒœä£4Ç.< Ÿ¹ÌB^,à>õ±Ä©aÛ¦c ¼eOÍèQ’p—÷3¤�GåÚ~'"sä ÷Ì!Š=d!^™Z²ÂÝdÊÛ^ >Ò¿õð‘Y7º%<åš+IÌŸR|ÈÍsÿ;ÅV¶=éé ÊŸç;ñçæÂÜŠ½U·§G4ñ­âÕ”\TÄÆÕ}iq!ÃS¨Ö;&üÚήÎ�$ߊ@:ÓVGc¹F©gî{{)žëí«ãß<Ú� ˜ôFÿ-Ÿ¥¤;›È{U·­ Ú‹„(ö”µÈ;ßäW©nZÿ0�Q‰o O5)cÕü;®Y¦�*‚J‰7˜&¤Œ ¼&#mAß&2Cÿ_X�ƳGâ) iè4/ø�çJSà¹Í‡éMI+$k(>Ÿx«(ë/£ú¯¾„Õ¤‚"7ws1[¸”±25ç™�”4’ J¡zm y½Ø¹ %ÿ*«xd$Îîô U·¥D›3Gà9Ïq­ 1 if game.players and (substr_count(game.players, ",") > -1) then --DEBUG(game.players) -- check every player in the game for username in string.split(game.players,",") do api.client_readmemory(username, ah_mh_request_id, ah_mh_offset, 2) -- HINT: place here more readmemory requests end end end end end -- handle response from the client function ah_handle_client(account, request_id, data) local is_cheater = false -- maphack if (request_id == ah_mh_request_id) then -- read value from the memory local value = bytes_to_int(data, 0, 2) --TRACE(account.name .. " memory value: " .. value) if not (value == ah_mh_value) then is_cheater = true end -- process another hack check --elseif (request_id == ...) then end if (is_cheater) then -- lock cheater account account_set_auth_lock(account.name, true) account_set_auth_lockreason(account.name, "we do not like cheaters") -- notify all players in the game about cheater local game = api.game_get_by_id(account.game_id) if game then for username in string.split(game.players,",") do api.message_send_text(username, message_type_error, nil, account.name .. " was banned by the antihack system.") end end -- kick cheater api.client_kill(account.name) INFO(account.name .. " was banned by the antihack system.") end end pvpgn-1.99.7.2.1+dfsg/lua/command/000077500000000000000000000000001462640461400163775ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/lua/command/ping.lua000066400000000000000000000005571462640461400200460ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- function command_ping(account, text) -- allow warcraft 3 client only if (account.clienttag == CLIENTTAG_WAR3XP) and (config.ghost) then return gh_command_ping(account, text) end return 1 end pvpgn-1.99.7.2.1+dfsg/lua/command/redirect.lua000066400000000000000000000015411462640461400207040ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Send text to user from server. Works like /announce, -- but directly to user and message text is not red. -- /redirect function command_redirect(account, text) local args = split_command(text, 2) if not args[1] or not args[2] then api.describe_command(account.name, args[0]) return -1 end -- get destination account local dest = api.account_get_by_name(args[1]) if next(dest) == nil or dest.online == "false" then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "User \"{}\" is offline", args[1])) return -1 end api.message_send_text(dest.name, message_type_info, dest.name, args[2]) return 0 end pvpgn-1.99.7.2.1+dfsg/lua/command/stats.lua000066400000000000000000000005531462640461400202430ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- function command_stats(account, text) if config.ghost_dota_server and config.ghost and (account.clienttag == CLIENTTAG_WAR3XP) then return gh_command_stats(account, text) end return 1 end pvpgn-1.99.7.2.1+dfsg/lua/command/w3motd.lua000066400000000000000000000012251462640461400203170ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- -- Read file w3motd.txt line by line and send text to user -- local username = nil function command_w3motd(account, text) -- allow warcraft 3 client only if not (account.clienttag == CLIENTTAG_WAR3XP or account.clienttag == CLIENTTAG_WARCRAFT3) then return 1 end username = account.name local data = file_load(config.motdw3file, w3motd_sendline_callback) return 0 end function w3motd_sendline_callback(line) api.message_send_text(username, message_type_info, nil, line) end pvpgn-1.99.7.2.1+dfsg/lua/config.lua000066400000000000000000000032561462640461400167370ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Config table can be extended here with your own variables -- values are preloaded from bnetd.conf config = { -- Path to "var" directory (with slash at the end) -- Usage: config.vardir() vardir = function() return string.replace(config.statusdir, "status", "") end, flood_immunity_users = { "admin", "" }, -- ignore flood protection for these users -- Quiz settings quiz = true, quiz_filelist = "misc, dota, warcraft", -- display available files in "/quiz start" quiz_competitive_mode = true, -- top players loses half of points which last player received; at the end top of records loses half of points which players received in current game quiz_max_questions = 100, -- from start to end quiz_question_delay = 5, -- delay before send next question quiz_hint_delay = 20, -- delay between prompts quiz_users_in_top = 15, -- how many users display in TOP list quiz_channel = nil, -- (do not modify!) channel when quiz has started (it assigned with start) -- AntiHack (Starcraft) ah = true, ah_interval = 60, -- interval for send memory request to all players in games -- GHost++ (https://github.com/OHSystem/ohsystem) ghost = false, -- enable GHost commands ghost_bots = { "hostbot1", "hostbot2" }, -- list of authorized bots ghost_dota_server = true, -- replace normal Warcraft 3 stats with DotA ghost_ping_expire = 90, -- interval when outdated botpings should be removed (bot ping updates for each user when he join a game hosted by ghost); game list shows to user depending on the best ping to host bot } pvpgn-1.99.7.2.1+dfsg/lua/extend/000077500000000000000000000000001462640461400162505ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/lua/extend/account.lua000066400000000000000000000010401462640461400204020ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Get count of all online users function users_count() local count = 0 for i,account in pairs(api.server_get_users()) do count = count + 1 end return count end -- Get count of all server account that were used by the server function accounts_count() local count = 0 for i,account in pairs(api.server_get_users(true)) do count = count + 1 end return count end pvpgn-1.99.7.2.1+dfsg/lua/extend/account_wrap.lua000066400000000000000000000552531462640461400214520ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- All attribute get/set actions must have a wrapper to avoid write a wrong type into database -- -- Profile -- function account_get_acct_email(username) return api.account_get_attr(username, "BNET\\acct\\email", attr_type_str) end function account_set_acct_email(username, value) return api.account_set_attr(username, "BNET\\acct\\email", attr_type_str, value) end function account_get_auth_admin(username, channelname) if channelname then return api.account_get_attr(username, "BNET\\auth\\admin\\" .. channelname, attr_type_bool) else return api.account_get_attr(username, "BNET\\auth\\admin", attr_type_bool) end end function account_set_auth_admin(username, channelname, value) if channelname then return api.account_set_attr(username, "BNET\\auth\\admin\\" .. channelname, attr_type_bool, value) else return api.account_set_attr(username, "BNET\\auth\\admin", attr_type_bool, value) end end function account_get_auth_operator(username, channelname) if channelname then return api.account_get_attr(username, "BNET\\auth\\operator\\" .. channelname, attr_type_bool) else return api.account_get_attr(username, "BNET\\auth\\operator", attr_type_bool) end end function account_set_auth_operator(username, channelname, value) if channelname then return api.account_set_attr(username, "BNET\\auth\\operator\\" .. channelname, attr_type_bool, value) else return api.account_set_attr(username, "BNET\\auth\\operator", attr_type_bool, value) end end function account_get_auth_voice(username, channelname) if channelname then return api.account_get_attr(username, "BNET\\auth\\voice\\" .. channelname, attr_type_bool) else return api.account_get_attr(username, "BNET\\auth\\voice", attr_type_bool) end end function account_set_auth_voice(username, channelname, value) if channelname then return api.account_set_attr(username, "BNET\\auth\\voice\\" .. channelname, attr_type_bool, value) else return api.account_set_attr(username, "BNET\\auth\\voice", attr_type_bool, value) end end function account_is_operator_or_admin(username, channelname) return account_get_auth_operator(username, channelname) or account_get_auth_admin(username, channelname) or account_get_auth_operator(username, nil) or account_get_auth_admin(username, nil) end function account_get_auth_announce(username) return api.account_get_attr(username, "BNET\\auth\\announce", attr_type_bool) end function account_set_auth_announce(username, value) return api.account_set_attr(username, "BNET\\auth\\announce", attr_type_bool, value) end function account_get_auth_botlogin(username) return api.account_get_attr(username, "BNET\\auth\\botlogin", attr_type_bool) end function account_set_auth_botlogin(username, value) return api.account_set_attr(username, "BNET\\auth\\botlogin", attr_type_bool, value) end function account_get_auth_lock(username) return api.account_get_attr(username, "BNET\\auth\\lock", attr_type_bool) end function account_set_auth_lock(username, value) return api.account_set_attr(username, "BNET\\auth\\lock", attr_type_bool, value) end function account_get_auth_locktime(username) return api.account_get_attr(username, "BNET\\auth\\locktime", attr_type_num) end function account_set_auth_locktime(username, value) return api.account_set_attr(username, "BNET\\auth\\locktime", attr_type_num, value) end function account_get_auth_lockreason(username) return api.account_get_attr(username, "BNET\\auth\\lockreason", attr_type_str) end function account_set_auth_lockreason(username, value) return api.account_set_attr(username, "BNET\\auth\\lockreason", attr_type_str, value) end function account_get_auth_lockby(username) return api.account_get_attr(username, "BNET\\auth\\lockby", attr_type_str) end function account_set_auth_lockby(username, value) return api.account_set_attr(username, "BNET\\auth\\lockby", attr_type_str, value) end function account_get_auth_mute(username) return api.account_get_attr(username, "BNET\\auth\\mute", attr_type_bool) end function account_set_auth_mute(username, value) return api.account_set_attr(username, "BNET\\auth\\mute", attr_type_bool, value) end function account_get_auth_mutetime(username) return api.account_get_attr(username, "BNET\\auth\\mutetime", attr_type_num) end function account_set_auth_mutetime(username, value) return api.account_set_attr(username, "BNET\\auth\\mutetime", attr_type_num, value) end function account_get_auth_mutereason(username) return api.account_get_attr(username, "BNET\\auth\\mutereason", attr_type_str) end function account_set_auth_mutereason(username, value) return api.account_set_attr(username, "BNET\\auth\\mutereason", attr_type_str, value) end function account_get_auth_muteby(username) return api.account_get_attr(username, "BNET\\auth\\muteby", attr_type_str) end function account_set_auth_muteby(username, value) return api.account_set_attr(username, "BNET\\auth\\muteby", attr_type_str, value) end function account_get_auth_command_groups(username) return api.account_get_attr(username, "BNET\\auth\\command_groups", attr_type_num) end function account_set_auth_command_groups(username, value) return api.account_set_attr(username, "BNET\\auth\\command_groups", attr_type_num, value) end function account_get_acct_lastlogin_time(username) return api.account_get_attr(username, "BNET\\acct\\lastlogin_time", attr_type_num) end function account_set_acct_lastlogin_time(username, value) return api.account_set_attr(username, "BNET\\acct\\lastlogin_time", attr_type_num, value) end function account_get_acct_lastlogin_owner(username) return api.account_get_attr(username, "BNET\\acct\\lastlogin_owner", attr_type_str) end function account_set_acct_lastlogin_owner(username, value) return api.account_set_attr(username, "BNET\\acct\\lastlogin_owner", attr_type_str, value) end function account_get_acct_createtime(username) return api.account_get_attr(username, "BNET\\acct\\ctime", attr_type_num) end function account_set_acct_createtime(username, value) return api.account_set_attr(username, "BNET\\acct\\ctime", attr_type_num, value) end function account_get_acct_lastlogin_clienttag(username) return api.account_get_attr(username, "BNET\\acct\\lastlogin_clienttag", attr_type_str) end function account_set_acct_lastlogin_clienttag(username, value) return api.account_set_attr(username, "BNET\\acct\\lastlogin_clienttag", attr_type_str, value) end function account_get_acct_lastlogin_ip(username) return api.account_get_attr(username, "BNET\\acct\\lastlogin_ip", attr_type_str) end function account_set_acct_lastlogin_ip(username, value) return api.account_set_attr(username, "BNET\\acct\\lastlogin_ip", attr_type_str, value) end function account_get_acct_passhash(username) return api.account_get_attr(username, "BNET\\acct\\passhash1", attr_type_str) end function account_set_acct_passhash(username, value) return api.account_set_attr(username, "BNET\\acct\\passhash1", attr_type_str, value) end function account_get_acct_verifier(username) return api.account_get_attr(username, "BNET\\acct\\verifier", attr_type_raw) end function account_set_acct_verifier(username, value) return api.account_set_attr(username, "BNET\\acct\\verifier", attr_type_raw, value) end function account_get_acct_salt(username) return api.account_get_attr(username, "BNET\\acct\\salt", attr_type_raw) end function account_set_acct_salt(username, value) return api.account_set_attr(username, "BNET\\acct\\salt", attr_type_raw, value) end function account_get_userlang(username) return api.account_get_attr(username, "BNET\\acct\\userlang", attr_type_str) end function account_set_userlang(username, value) return api.account_set_attr(username, "BNET\\acct\\userlang", attr_type_str, value) end -- -- Profile -- function account_get_auth_adminnormallogin(username) return api.account_get_attr(username, "BNET\\auth\\adminnormallogin", attr_type_bool) end function account_set_auth_adminnormallogin(username, value) return api.account_set_attr(username, "BNET\\auth\\adminnormallogin", attr_type_bool, value) end function account_get_auth_changepass(username) return api.account_get_attr(username, "BNET\\auth\\changepass", attr_type_bool) end function account_set_auth_changepass(username, value) return api.account_set_attr(username, "BNET\\auth\\changepass", attr_type_bool, value) end function account_get_auth_changeprofile(username) return api.account_get_attr(username, "BNET\\auth\\changeprofile", attr_type_bool) end function account_set_auth_changeprofile(username, value) return api.account_set_attr(username, "BNET\\auth\\changeprofile", attr_type_bool, value) end function account_get_auth_createnormalgame(username) return api.account_get_attr(username, "BNET\\auth\\createnormalgame", attr_type_bool) end function account_set_auth_createnormalgame(username, value) return api.account_set_attr(username, "BNET\\auth\\createnormalgame", attr_type_bool, value) end function account_get_auth_joinnormalgame(username) return api.account_get_attr(username, "BNET\\auth\\joinnormalgame", attr_type_bool) end function account_set_auth_joinnormalgame(username, value) return api.account_set_attr(username, "BNET\\auth\\joinnormalgame", attr_type_bool, value) end function account_get_auth_createladdergame(username) return api.account_get_attr(username, "BNET\\auth\\createladdergame", attr_type_bool) end function account_set_auth_createladdergame(username, value) return api.account_set_attr(username, "BNET\\auth\\createladdergame", attr_type_bool, value) end function account_get_auth_joinladdergame(username) return api.account_get_attr(username, "BNET\\auth\\joinladdergame", attr_type_bool) end function account_set_auth_joinladdergame(username, value) return api.account_set_attr(username, "BNET\\auth\\joinladdergame", attr_type_bool, value) end -- -- Profile -- function account_get_sex(username) return api.account_get_attr(username, "profile\\sex", attr_type_str) end function account_set_sex(username, value) return api.account_set_attr(username, "profile\\sex", attr_type_str, value) end function account_get_age(username) return api.account_get_attr(username, "profile\\age", attr_type_str) end function account_set_age(username, value) return api.account_set_attr(username, "profile\\age", attr_type_str, value) end function account_get_location(username) return api.account_get_attr(username, "profile\\location", attr_type_str) end function account_set_location(username, value) return api.account_set_attr(username, "profile\\location", attr_type_str, value) end function account_get_description(username) return api.account_get_attr(username, "profile\\description", attr_type_str) end function account_set_description(username, value) return api.account_set_attr(username, "profile\\description", attr_type_str, value) end -- -- Warcraft 3 -- function account_get_soloxp(username) return api.account_get_attr(username, "Record\\W3XP\\solo_xp", attr_type_num) end function account_set_soloxp(username, value) return api.account_set_attr(username, "Record\\W3XP\\solo_xp", attr_type_num, value) end function account_get_sololevel(username) return api.account_get_attr(username, "Record\\W3XP\\solo_level", attr_type_num) end function account_set_sololevel(username, value) return api.account_set_attr(username, "Record\\W3XP\\solo_level", attr_type_num, value) end function account_get_solowins(username) return api.account_get_attr(username, "Record\\W3XP\\solo_wins", attr_type_num) end function account_set_solowins(username, value) return api.account_set_attr(username, "Record\\W3XP\\solo_wins", attr_type_num, value) end function account_get_sololosses(username) return api.account_get_attr(username, "Record\\W3XP\\solo_losses", attr_type_num) end function account_set_sololosses(username, value) return api.account_set_attr(username, "Record\\W3XP\\solo_losses", attr_type_num, value) end function account_get_solorank(username) return api.account_get_attr(username, "Record\\W3XP\\solo_rank", attr_type_num) end function account_set_solorank(username, value) return api.account_set_attr(username, "Record\\W3XP\\solo_rank", attr_type_num, value) end -- -- Starcraft -- function account_get_normal_wins(username) return api.account_get_attr(username, "Record\\SEXP\\0\\wins", attr_type_num) end function account_set_normal_wins(username, value) return api.account_set_attr(username, "Record\\SEXP\\0\\wins", attr_type_num, value) end function account_get_normal_losses(username) return api.account_get_attr(username, "Record\\SEXP\\0\\losses", attr_type_num) end function account_set_normal_losses(username, value) return api.account_set_attr(username, "Record\\SEXP\\0\\losses", attr_type_num, value) end function account_get_normal_draws(username) return api.account_get_attr(username, "Record\\SEXP\\0\\draws", attr_type_num) end function account_set_normal_draws(username, value) return api.account_set_attr(username, "Record\\SEXP\\0\\draws", attr_type_num, value) end function account_get_normal_disconnects(username) return api.account_get_attr(username, "Record\\SEXP\\0\\disconnects", attr_type_num) end function account_set_normal_disconnects(username, value) return api.account_set_attr(username, "Record\\SEXP\\0\\disconnects", attr_type_num, value) end function account_get_normal_last_time(username) return api.account_get_attr(username, "Record\\SEXP\\0\\last game", attr_type_num) end function account_set_normal_last_time(username, value) return api.account_set_attr(username, "Record\\SEXP\\0\\last game", attr_type_num, value) end function account_get_normal_last_result(username) return api.account_get_attr(username, "Record\\SEXP\\0\\last game result", attr_type_num) end function account_set_normal_last_result(username, value) return api.account_set_attr(username, "Record\\SEXP\\0\\last game result", attr_type_num, value) end function account_get_ladder_wins(username) return api.account_get_attr(username, "Record\\SEXP\\1\\wins", attr_type_num) end function account_set_ladder_wins(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\wins", attr_type_num, value) end function account_get_ladder_losses(username) return api.account_get_attr(username, "Record\\SEXP\\1\\losses", attr_type_num) end function account_set_ladder_losses(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\losses", attr_type_num, value) end function account_get_ladder_draws(username) return api.account_get_attr(username, "Record\\SEXP\\1\\draws", attr_type_num) end function account_set_ladder_draws(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\draws", attr_type_num, value) end function account_get_ladder_disconnects(username) return api.account_get_attr(username, "Record\\SEXP\\1\\disconnects", attr_type_num) end function account_set_ladder_disconnects(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\disconnects", attr_type_num, value) end function account_get_ladder_last_time(username) return api.account_get_attr(username, "Record\\SEXP\\1\\last game", attr_type_num) end function account_set_ladder_last_time(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\last game", attr_type_num, value) end function account_get_ladder_last_result(username) return api.account_get_attr(username, "Record\\SEXP\\1\\last game result", attr_type_num) end function account_set_ladder_last_result(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\last game result", attr_type_num, value) end function account_get_ladder_rating(username) return api.account_get_attr(username, "Record\\SEXP\\1\\rating", attr_type_num) end function account_set_ladder_rating(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\rating", attr_type_num, value) end function account_get_ladder_rank(username) return api.account_get_attr(username, "Record\\SEXP\\1\\rank", attr_type_num) end function account_set_ladder_rank(username, value) return api.account_set_attr(username, "Record\\SEXP\\1\\rank", attr_type_num, value) end -- TODO: wrappers for 2x2 -- -- Warcraft 2 -- --[[ Warcraft 2 has the same functions as Starcraft, but "W2BN" instead of "SEXP" in path. Replace it if you have a single 2 server. ]]-- -- -- Diablo -- function account_get_normal_level(username) return api.account_get_attr(username, "Record\\DRTL\\0\\level", attr_type_num) end function account_set_normal_level(username, value) return api.account_set_attr(username, "Record\\DRTL\\0\\level", attr_type_num, value) end function account_get_normal_class(username) return api.account_get_attr(username, "Record\\DRTL\\0\\class", attr_type_num) end function account_set_normal_class(username, value) return api.account_set_attr(username, "Record\\DRTL\\0\\class", attr_type_num, value) end function account_get_normal_diablo_kills(username) return api.account_get_attr(username, "Record\\DRTL\\0\\diablo kills", attr_type_num) end function account_set_normal_diablo_kills(username, value) return api.account_set_attr(username, "Record\\DRTL\\0\\diablo kills", attr_type_num, value) end function account_get_normal_strength(username) return api.account_get_attr(username, "Record\\DRTL\\0\\strength", attr_type_num) end function account_set_normal_strength(username, value) return api.account_set_attr(username, "Record\\DRTL\\0\\strength", attr_type_num, value) end function account_get_normal_dexterity(username) return api.account_get_attr(username, "Record\\DRTL\\0\\dexterity", attr_type_num) end function account_set_normal_dexterity(username, value) return api.account_set_attr(username, "Record\\DRTL\\0\\dexterity", attr_type_num, value) end function account_get_normal_vitality(username) return api.account_get_attr(username, "Record\\DRTL\\0\\vitality", attr_type_num) end function account_set_normal_vitality(username, value) return api.account_set_attr(username, "Record\\DRTL\\0\\vitality", attr_type_num, value) end function account_get_normal_gold(username) return api.account_get_attr(username, "Record\\DRTL\\0\\gold", attr_type_num) end function account_set_normal_gold(username, value) return api.account_set_attr(username, "Record\\DRTL\\0\\gold", attr_type_num, value) end -- -- Westwood Online -- function account_get_wol_apgar(username) return api.account_get_attr(username, "Record\\WOL\\auth\\apgar", attr_type_str) end function account_set_wol_apgar(username, value) return api.account_set_attr(username, "Record\\WOL\\auth\\apgar", attr_type_str, value) end function account_get_locale(username) return api.account_get_attr(username, "Record\\WOL\\auth\\locale", attr_type_str) end function account_set_locale(username, value) return api.account_set_attr(username, "Record\\WOL\\auth\\locale", attr_type_str, value) end -- -- Warcraft 3 (DotA) -- function account_get_dotarating_3x3(username) value = api.account_get_attr(username, "Record\\W3XP\\dota_3_rating", attr_type_num) if (value == 0) then value = 1000 end return value end function account_set_dotarating_3x3(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_3_rating", attr_type_num, value) end function account_get_dotawins_3x3(username) return api.account_get_attr(username, "Record\\W3XP\\dota_3_wins", attr_type_num) end function account_set_dotawins_3x3(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_3_wins", attr_type_num, value) end function account_get_dotalosses_3x3(username) return api.account_get_attr(username, "Record\\W3XP\\dota_3_losses", attr_type_num) end function account_set_dotalosses_3x3(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_3_losses", attr_type_num, value) end function account_get_dotastreaks_3x3(username) return api.account_get_attr(username, "Record\\W3XP\\dota_3_streaks", attr_type_num) end function account_set_dotastreaks_3x3(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_3_streaks", attr_type_num, value) end function account_get_dotaleaves_3x3(username) return api.account_get_attr(username, "Record\\W3XP\\dota_3_leaves", attr_type_num) end function account_set_dotaleaves_3x3(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_3_leaves", attr_type_num, value) end function account_get_dotarating_5x5(username) value = api.account_get_attr(username, "Record\\W3XP\\dota_5_rating", attr_type_num) if (value == 0) then value = 1000 end return value end function account_set_dotarating_5x5(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_5_rating", attr_type_num, value) end function account_get_dotawins_5x5(username) return api.account_get_attr(username, "Record\\W3XP\\dota_5_wins", attr_type_num) end function account_set_dotawins_5x5(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_5_wins", attr_type_num, value) end function account_get_dotalosses_5x5(username) return api.account_get_attr(username, "Record\\W3XP\\dota_5_losses", attr_type_num) end function account_set_dotalosses_5x5(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_5_losses", attr_type_num, value) end function account_get_dotastreaks_5x5(username) return api.account_get_attr(username, "Record\\W3XP\\dota_5_streaks", attr_type_num) end function account_set_dotastreaks_5x5(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_5_streaks", attr_type_num, value) end function account_get_dotaleaves_5x5(username) return api.account_get_attr(username, "Record\\W3XP\\dota_5_leaves", attr_type_num) end function account_set_dotaleaves_5x5(username, value) return api.account_set_attr(username, "Record\\W3XP\\dota_5_leaves", attr_type_num, value) end function account_get_botping(username) value = api.account_get_attr(username, "BNET\\acct\\botping", attr_type_str) local pings = {} -- if pings were not set yet then return empty table if string.empty(value) then return pings end -- deserialize and return table -- data format: "unixtime,botname,ping;..." for chunk in string.split(value,";") do local item = {} i = 1 for v in string.split(chunk,",") do if (i == 1) then item.date = v elseif (i == 2) then item.bot = v elseif (i == 3) then item.ping = v end i = i + 1 end table.insert(pings, item) end -- sort by ping ascending table.sort(pings, function(a,b) return tonumber(a.ping) < tonumber(b.ping) end) return pings end -- pings is a table that received from account_get_botping() function account_set_botping(username, pings) local value = "" -- serialize table for k,v in pairs(pings) do -- ignore expired pings if (os.time() - tonumber(v.date)) < 60*60*24*config.ghost_ping_expire then value = value .. string.format("%s,%s,%s;", v.date, v.bot, v.ping); end end return api.account_set_attr(username, "BNET\\acct\\botping", attr_type_str, value) end pvpgn-1.99.7.2.1+dfsg/lua/extend/channel.lua000066400000000000000000000017711462640461400203710ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Return channel id by name (if channel not found then return -1) function channel_get_id_by_name(channel_name) for i,channel in pairs(api.server_get_channels()) do if channel.name == channel_name then return channel.id end end return -1 end -- Send message in channel -- message_type: message_type_info | message_type_error function channel_send_message(channel_name, text, message_type) channel_id = channel_get_id_by_name(channel_name) if (channel_id == -1) then return nil end channel = api.channel_get_by_id(channel_id) for username in string.split(channel.memberlist,",") do api.message_send_text(username, message_type, nil, text) end end -- Get count of all channels function channels_count() local count = 0 for i,channel in pairs(api.server_get_channels()) do count = count + 1 end return count endpvpgn-1.99.7.2.1+dfsg/lua/extend/enum/000077500000000000000000000000001462640461400172145ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/lua/extend/enum/attr.lua000066400000000000000000000003561462640461400206750ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- attr_type_str, attr_type_num, attr_type_bool, attr_type_raw = 0,1,2,3pvpgn-1.99.7.2.1+dfsg/lua/extend/enum/eventlog.lua000066400000000000000000000005651462640461400215500ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- eventlog_level_none, eventlog_level_trace, eventlog_level_debug, eventlog_level_info, eventlog_level_warn, eventlog_level_error, eventlog_level_fatal, eventlog_level_gui -- Win32 GUI = 0,1,2,4,8,16,32,64 pvpgn-1.99.7.2.1+dfsg/lua/extend/enum/game.lua000066400000000000000000000055561462640461400206430ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Strings used here - not numbers, because these values will be -- compare with string values returned from API. -- For example "0" != 0 in Lua. game_type_none, game_type_all, game_type_topvbot, game_type_melee, game_type_ffa, game_type_oneonone, game_type_ctf, game_type_greed, game_type_slaughter, game_type_sdeath, game_type_ladder, game_type_ironman, game_type_mapset, game_type_teammelee, game_type_teamffa, game_type_teamctf, game_type_pgl, game_type_diablo, game_type_diablo2open, game_type_diablo2closed, game_type_anongame = "0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20" game_status_started, game_status_full, game_status_open, game_status_loaded, game_status_done = "0","1","2","3","4" game_result_none, game_result_win, game_result_loss, game_result_draw, game_result_disconnect, game_result_observer, game_result_playing = "0","1","2","3","4","5","6" game_option_none, game_option_melee_normal, game_option_ffa_normal, game_option_oneonone_normal, game_option_ctf_normal, game_option_greed_10000, game_option_greed_7500, game_option_greed_5000, game_option_greed_2500, game_option_slaughter_60, game_option_slaughter_45, game_option_slaughter_30, game_option_slaughter_15, game_option_sdeath_normal, game_option_ladder_countasloss, game_option_ladder_nopenalty, game_option_mapset_normal, game_option_teammelee_4, game_option_teammelee_3, game_option_teammelee_2, game_option_teamffa_4, game_option_teamffa_3, game_option_teamffa_2, game_option_teamctf_4, game_option_teamctf_3, game_option_teamctf_2, game_option_topvbot_7, game_option_topvbot_6, game_option_topvbot_5, game_option_topvbot_4, game_option_topvbot_3, game_option_topvbot_2, game_option_topvbot_1 = "0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32" game_maptype_none, game_maptype_selfmade, game_maptype_blizzard, game_maptype_ladder, game_maptype_pgl, game_maptype_kbk, game_maptype_compusa = "0","1","2","3","4","5","6" game_tileset_none, game_tileset_badlands, game_tileset_space, game_tileset_installation, game_tileset_ashworld, game_tileset_jungle, game_tileset_desert, game_tileset_ice, game_tileset_twilight = "0","1","2","3","4","5","6","7","8","9" game_speed_none, game_speed_slowest, game_speed_slower, game_speed_slow, game_speed_normal, game_speed_fast, game_speed_faster, game_speed_fastest = "0","1","2","3","4","5","6","7" game_difficulty_none, game_difficulty_normal, game_difficulty_nightmare, game_difficulty_hell, game_difficulty_hardcore_normal, game_difficulty_hardcore_nightmare, game_difficulty_hardcore_hell = "0","1","2","3","4","5","6" game_flag_none, game_flag_private = "0","1" pvpgn-1.99.7.2.1+dfsg/lua/extend/enum/message.lua000066400000000000000000000022251462640461400213440ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- message_type_adduser, message_type_join, message_type_part, message_type_whisper, message_type_talk, message_type_broadcast, message_type_channel, message_type_userflags, message_type_whisperack, message_type_friendwhisperack, message_type_channelfull, message_type_channeldoesnotexist, message_type_channelrestricted, message_type_info, message_type_error, message_type_emote, message_type_uniqueid, -- bad message type? message_type_mode, -- bad message type? message_type_kick, message_type_quit, -- IRC specific messages message_type_nick, message_type_notice, message_type_namreply, message_type_topic, -- Westwood Online Extensions message_type_host, message_type_invmsg, message_type_page, message_type_wol_joingame, message_type_gameopt_talk, message_type_gameopt_whisper, message_type_wol_start_game, message_type_wol_advertr, message_type_wol_chanchk, message_type_wol_userip, message_type_null = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34 pvpgn-1.99.7.2.1+dfsg/lua/extend/enum/messagebox.lua000066400000000000000000000026331462640461400220600ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- https://github.com/pvpgn/pvpgn-server/issues/15 -- http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx -- -- use math_or(mb_type1, mb_type2) to combine two messagebox types --[[ Example code to display Windows MessageBox: local mb_type = math_or(MB_DEFBUTTON2, math_or(MB_ABORTRETRYIGNORE, MB_ICONEXCLAMATION) ) api.messagebox_show(account.name, "aaaaaaaaaaaaaaaaaaaaaaaaawwadwwadawdawdawdwadawdaaw", "MessageBox from " .. config.servername, mb_type) ]]-- MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE, MB_HELP, MB_OK, MB_OKCANCEL, MB_RETRYCANCEL, MB_YESNO, MB_YESNOCANCEL, MB_ICONEXCLAMATION, MB_ICONWARNING, MB_ICONINFORMATION, MB_ICONASTERISK, MB_ICONQUESTION, MB_ICONSTOP, MB_ICONERROR, MB_ICONHAND, MB_DEFBUTTON1, MB_DEFBUTTON2, MB_DEFBUTTON3, MB_DEFBUTTON4, MB_APPLMODAL, MB_SYSTEMMODAL, MB_TASKMODAL, MB_DEFAULT_DESKTOP_ONLY, MB_RIGHT, MB_RTLREADING, MB_SETFOREGROUND, MB_TOPMOST, MB_SERVICE_NOTIFICATION = 0x00000002,0x00000006,0x00004000,0x00000000,0x00000001,0x00000005,0x00000004,0x00000003,0x00000030,0x00000030,0x00000040,0x00000040,0x00000020,0x00000010,0x00000010,0x00000010,0x00000000,0x00000100,0x00000200,0x00000300,0x00000000,0x00001000,0x00002000,0x00020000,0x00080000,0x00100000,0x00010000,0x00040000,0x00200000 pvpgn-1.99.7.2.1+dfsg/lua/extend/enum/tag.lua000066400000000000000000000047261462640461400205030ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- -- Software tags -- CLIENTTAG_BNCHATBOT, -- CHAT bot CLIENTTAG_STARCRAFT, -- Starcraft (original) CLIENTTAG_BROODWARS, -- Starcraft EXpansion Pack CLIENTTAG_SHAREWARE, -- Starcraft Shareware CLIENTTAG_DIABLORTL, -- Diablo ReTaiL CLIENTTAG_DIABLOSHR, -- Diablo SHaReware CLIENTTAG_WARCIIBNE, -- WarCraft II Battle.net Edition CLIENTTAG_DIABLO2DV, -- Diablo II Diablo's Victory CLIENTTAG_STARJAPAN, -- Starcraft (Japan) CLIENTTAG_DIABLO2ST, -- Diablo II Stress Test CLIENTTAG_DIABLO2XP, -- Diablo II Extension Pack CLIENTTAG_WARCRAFT3, -- WarCraft III CLIENTTAG_WAR3XP, -- WarCraft III Expansion CLIENTTAG_IIRC, -- IRC -- Westwood Online tags CLIENTTAG_WCHAT, -- Westwood Chat CLIENTTAG_TIBERNSUN, -- Tiberian Sun CLIENTTAG_TIBSUNXP, -- Tiberian Sun Extension Pack CLIENTTAG_REDALERT, -- Red Alert 1 CLIENTTAG_REDALERT2, -- Red Alert 2 CLIENTTAG_DUNE2000, -- Dune 2000 CLIENTTAG_NOX, -- NOX CLIENTTAG_NOXQUEST, -- NOX Quest CLIENTTAG_RENEGADE, -- Renegade CLIENTTAG_RENGDFDS, -- Renegade Free Dedicated Server CLIENTTAG_YURISREV, -- Yuri's Revenge CLIENTTAG_EMPERORBD, -- Emperor: Battle for Dune CLIENTTAG_LOFLORE3, -- Lands of Lore 3 CLIENTTAG_WWOL, -- Other Westwood Online games CLIENTTAG_UNKNOWN = "CHAT","STAR","SEXP","SSHR","DRTL","DSHR","W2BN","D2DV","JSTR","D2ST","D2XP","WAR3","W3XP","IIRC","WCHT","TSUN","TSXP","RALT","RAL2","DN2K","NOXX","NOXQ","RNGD","RFDS","YURI","EBFD","LOR3","WWOL","UNKN" -- -- Architecture tags -- ARCHTAG_WINX86, -- MS Windows on Intel x86 ARCHTAG_MACPPC, -- MacOS on PowerPC ARCHTAG_OSXPPC -- MacOS X on PowerPC = "IX86","PMAC","XMAC" -- -- Game languages -- GAMELANG_ENGLISH_UINT, -- enUS GAMELANG_GERMAN_UINT, -- deDE GAMELANG_CZECH_UINT, -- csCZ GAMELANG_SPANISH_UINT, -- esES GAMELANG_FRENCH_UINT, -- frFR GAMELANG_ITALIAN_UINT, -- itIT GAMELANG_JAPANESE_UINT, -- jaJA GAMELANG_KOREAN_UINT, -- koKR GAMELANG_POLISH_UINT, -- plPL GAMELANG_RUSSIAN_UINT, -- ruRU GAMELANG_CHINESE_S_UINT, -- zhCN GAMELANG_CHINESE_T_UINT -- zhTW = 0x656E5553,0x64654445,0x6373435A,0x65734553,0x66724652,0x69744954,0x6A614A41,0x6B6F4B52,0x706C504C,0x72755255,0x7A68434E,0x7A685457 pvpgn-1.99.7.2.1+dfsg/lua/extend/eventlog.lua000066400000000000000000000014671462640461400206060ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Log event wrappers function FATAL(text) api.eventlog(eventlog_level_fatal, __function__, text) end function ERROR(text) api.eventlog(eventlog_level_error, __function__, text) end function WARN(text) api.eventlog(eventlog_level_warn, __function__, text) end function INFO(text) api.eventlog(eventlog_level_info, __function__, text) end function DEBUG(text) if (type(text) == "table") then text = table.dump(text) end api.eventlog(eventlog_level_debug, __function__, text) end function TRACE(text) api.eventlog(eventlog_level_trace, __function__, text) end function GUI(text) api.eventlog(eventlog_level_gui, __function__, text) end pvpgn-1.99.7.2.1+dfsg/lua/extend/game.lua000066400000000000000000000011311462640461400176600ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Get count of all games function games_count() local count = 0 for i,game in pairs(api.server_get_games()) do count = count + 1 end return count end function server_get_games(clienttag) local gamelist = {} for i,game in pairs(api.server_get_games()) do if clienttag then if (game.tag == clienttag) then table.insert(gamelist, game) end else table.insert(gamelist, game) end end return gamelist endpvpgn-1.99.7.2.1+dfsg/lua/extend/message.lua000066400000000000000000000007611462640461400204030ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Send announce to all connected users function message_send_all(text) for i,account in pairs(api.server_get_users()) do api.message_send_text(account.name, message_type_broadcast, nil, text) end end function localize(username, arg1, arg2, arg3, arg4, arg5) return api.localize(username, arg1, arg2, arg3, arg4, arg5) endpvpgn-1.99.7.2.1+dfsg/lua/ghost/000077500000000000000000000000001462640461400161055ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/lua/ghost/command.lua000066400000000000000000000255271462640461400202410ustar00rootroot00000000000000--[[ Connection interface between PvPGN and GHost https://github.com/OHSystem/ohsystem/issues/279 Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- ------------------------------ --- USER -> PVPGN -> GHOST --- ------------------------------ -- /host [mode] [type] [gamename] function command_host(account, text) if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end local args = split_command(text, 3) if not args[1] or not args[2] or not args[3] then api.describe_command(account.name, args[0]) return -1 end -- if user already host a game if gh_get_userbot_name(account.name) then local gamename = gh_get_userbot_game(account.name) local game = api.game_get_by_name(gamename, account.clienttag, game_type_all) if next(game) then api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You already host a game \"{}\". Use /unhost to destroy it.", gamename)) return -1 else -- if game doesn't exist then remove mapped bot for user gh_del_userbot(account.name) end end -- get available bot by ping local botname = gh_select_bot(account.name) if not botname then api.message_send_text(account.name, message_type_error, nil, localize(account.name, "Enable to create game. HostBots are temporary offline.")) return -1 end -- redirect message to bot gh_message_send(botname, string.format("/pvpgn host %s %s %s %s", account.name, args[1], args[2], args[3]) ) return 0 end -- /chost [code] [gamename] function command_chost(account, text) if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end local args = split_command(text, 2) if not args[1] or not args[2] then api.describe_command(account.name, args[0]) -- send user each map on a new line for i,map in pairs(gh_maplist) do api.message_send_text(account.name, message_type_info, nil, string.format("%s = %s", map.code, map.name) ) end return -1 end -- find map by code local mapfile = nil for i,map in pairs(gh_maplist) do if string.lower(args[1]) == string.lower(map.code) then mapfile = map.filename end end if not mapfile then api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Invalid map code.") ) return -1 end -- if user already host a game if gh_get_userbot_name(account.name) then local gamename = gh_get_userbot_game(account.name) local game = api.game_get_by_name(gamename, account.clienttag, game_type_all) if next(game) then api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You already host a game \"{}\". Use /unhost to destroy it.", gamename)) return -1 else -- if game doesn't exist then remove mapped bot for user gh_del_userbot(account.name) end end -- get available bot by ping local botname = gh_select_bot(account.name) if not botname then api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Enable to create game. HostBots are temporary offline.")) return -1 end -- redirect message to bot gh_message_send(botname, string.format("/pvpgn chost %s %s %s", account.name, mapfile, args[2]) ) return 0 end -- /unhost function command_unhost(account, text) if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end local botname = gh_get_userbot_name(account.name) -- check if user hasn't a mapped bot if not botname then api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You don't host a game.")) return -1 end -- do not allow unhost if the game is started (and owner is a mapped user bot - -- it is necessary because we can get duplicate name of another's game, -- due to save/restore table state when Lua rehash) local game = api.game_get_by_id(account.game_id) if next(game) and (game.status == game_status_started) and (game.owner == botname) then api.message_send_text(account.name, message_type_info, nil, localize(account.name, "You can't unhost a started game.")) return -1 end -- redirect message to bot local botname = gh_get_userbot_name(account.name) gh_message_send(botname, string.format("/pvpgn unhost %s", account.name) ) -- remove mapped bot anyway to make sure that it was removed -- (even if bot casual shutdown before send callback) gh_del_userbot(account.name) api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Your game was destroyed.")) return 0 end -- /swap [slot1] [slot2] function command_swap(account, text) if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end if not gh_is_owner(account) then return 1 end local args = split_command(text, 2) if not args[1] or not args[2] then api.describe_command(account.name, args[0]) return -1 end -- redirect message to bot local botname = gh_get_userbot_name(account.name) gh_message_send(botname, string.format("/pvpgn swap %s %s %s", account.name, args[1], args[2]) ) return 0 end -- /open|close [slot] function command_open_close(account, text) if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end if not gh_is_owner(account) then return 1 end local args = split_command(text, 1) if not args[1] then api.describe_command(account.name, args[0]) return -1 end -- redirect message to bot local botname = gh_get_userbot_name(account.name) gh_message_send(botname, string.format("/pvpgn %s %s %s", args[0], account.name, args[1]) ) return 0 end -- /start|abort|pub|priv function command_start_abort_pub_priv(account, text) if not config.ghost or not account.clienttag == CLIENTTAG_WAR3XP then return 1 end if not gh_is_owner(account) then return 1 end local args = split_command(text, 0) -- redirect message to bot local botname = gh_get_userbot_name(account.name) gh_message_send(botname, string.format("/pvpgn %s %s", args[0], account.name) ) return 0 end -- /p function gh_command_ping(account, text) -- if user not in game then do not override command if not account.game_id then return false end local game = api.game_get_by_id(account.game_id) -- check if game owner is ghost bot if not gh_is_bot(game.owner) then return 1 end -- redirect message to bot gh_message_send(game.owner, string.format("/pvpgn ping %s", account.name)) return 0 end -- /stats function gh_command_stats(account, text) local useracc = account local args = split_command(text, 1) if args[1] then useracc = api.account_get_by_name(args[1]) -- if user not found if not next(useracc) then api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Invalid user.")) return -1 end end local stats = gh_get_dotastats(useracc) -- localized strings local win, loss = localize(account.name, "win"), localize(account.name, "loss") local pts = localize(account.name, "pts") local game = api.game_get_by_id(account.game_id) -- user who is owner of the hostbot in the current game local owner = gh_find_userbot_by_game(game.name) local gametype = "5x5"--gh_get_userbot_gametype(owner) -- user given in args or (user in game and game is ladder) if not args[1] and next(game) and not string:empty(gametype) then -- iterate all users in the game for u in string.split("harpywar,admin",",") do -- get new stats for the player stats = gh_get_dotastats(api.account_get_by_name(u)) local rank = stats.rank5x5 local rating = stats.rating5x5 local leaves = stats.leaves5x5 local leaves_percent = stats.leaves5x5_percent -- switch gametype if game name has substr "3x3" if gametype == "3x3" then rank = stats.rank3x3 rating = stats.rating3x3 leaves = stats.leaves3x3 leaves_percent = stats.leaves3x3_percent end -- bnproxy stats output format (DO NOT MODIFY!!!) api.message_send_text(account.name, message_type_info, nil, string.format("[%s] %s DotA (%s): [%s] %d pts. Leave count: %d (%f%%)", stats.country, u, gametype, rank, rating, leaves, leaves_percent)) end else -- in chat api.message_send_text(account.name, message_type_info, nil, string.format("[%s] ", stats.country) .. localize(account.name, "{}'s record:", useracc.name)) api.message_send_text(account.name, message_type_info, nil, localize(account.name, "DotA games") .. string.format(" (%s): %d-%d [%s] %d %s", "5x5", stats.wins5x5, stats.losses5x5, stats.rank5x5, stats.rating5x5, pts)) api.message_send_text(account.name, message_type_info, nil, localize(account.name, "DotA games") .. string.format(" (%s): %d-%d [%s] %d %s", "3x3", stats.wins3x3, stats.losses3x3, stats.rank3x3, stats.rating3x3, pts)) -- display streaks in self user stats if not args[1] then local streaks5x5_result, streaks3x3_result = win, win if (stats.streaks5x5 < 0) then streaks5x5_result = loss end if (stats.streaks3x3 < 0) then streaks3x3_result = loss end api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Current {} streak", "5x5") .. string.format(" (%s): %s", streaks5x5_result, stats.streaks5x5)) api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Current {} streak", "3x3") .. string.format(" (%s): %s", streaks3x3_result, stats.streaks3x3)) end api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Current leave count:") .. string.format(" %d (%s%%)", stats.leaves, stats.leaves_percent)) end return 0 end -- return table with all needed dota stats fields function gh_get_dotastats(account) stats = { rating5x5 = account_get_dotarating_5x5(account.name), rating3x3 = account_get_dotarating_3x3(account.name), wins5x5 = account_get_dotawins_5x5(account.name), wins3x3 = account_get_dotawins_3x3(account.name), losses5x5 = account_get_dotalosses_5x5(account.name), losses3x3 = account_get_dotalosses_3x3(account.name), streaks5x5 = account_get_dotastreaks_5x5(account.name), streaks3x3 = account_get_dotastreaks_3x3(account.name), leaves5x5 = account_get_dotaleaves_5x5(account.name), leaves3x3 = account_get_dotaleaves_3x3(account.name), country = account.country } if not stats.country then stats.country = "-" end stats.country = stats.country:sub(1,2) -- two first symbols stats.leaves5x5_percent = math.round(stats.leaves5x5 / ((stats.wins5x5+stats.losses5x5)/100), 1) stats.leaves3x3_percent = math.round(stats.leaves3x3 / ((stats.wins3x3+stats.losses3x3)/100), 1) if math.isnan(stats.leaves5x5_percent) then stats.leaves5x5_percent = 0 end if math.isnan(stats.leaves3x3_percent) then stats.leaves3x3_percent = 0 end stats.leaves = stats.leaves5x5 + stats.leaves3x3 stats.leaves_percent = math.round(stats.leaves5x5_percent + stats.leaves3x3_percent, 1) stats.rank5x5 = api.icon_get_rank(stats.rating5x5, CLIENTTAG_WAR3XP) stats.rank3x3 = api.icon_get_rank(stats.rating3x3, CLIENTTAG_WAR3XP) return stats end pvpgn-1.99.7.2.1+dfsg/lua/ghost/command_callback.lua000066400000000000000000000174661462640461400220600ustar00rootroot00000000000000--[[ Connection interface between PvPGN and GHost https://github.com/OHSystem/ohsystem/issues/279 Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- --------------------------------- --- GHOST -> PVPGN (CALLBACK) --- --------------------------------- -- /ghost [cmd] [code] {args} function command_ghost(account, text) if not config.ghost then return 1 end -- restrict commands for authorized bots only if not gh_is_bot(account.name) then return 1 end local args = split_command(text, 5) local cmd = args[1] local code = args[2] if code ~= "ok" then if code == "err" then local err_message = args[3] -- HINT: you can handle error here end return -1 end -- /ghost init if (cmd == "init") then INFO("GHost bot is connected (" .. account.name .. ")") -- /ghost gameresult [players] [ratings] [results] elseif (cmd == "gameresult") then gh_callback_gameresult(args[3], args[4], args[5]) -- /ghost host [code] [user] [gamename] elseif (cmd == "host") then args = split_command(text, 4) -- support game name with spaces gh_callback_host(args[3], account.name, args[4]) -- /ghost chost [code] [user] [gamename] elseif (cmd == "chost") then args = split_command(text, 4) -- support game name with spaces gh_callback_chost(args[3], account.name, args[4]) -- /ghost unhost [code] [user] [gamename] elseif (cmd == "unhost") then gh_callback_unhost(args[3], args[4]) -- /ghost ping [code] [user] [players] [pings] elseif (cmd == "ping") then gh_callback_ping(args[3], args[4], args[5]) -- /ghost swap [code] [user] [username1] [username2] elseif (cmd == "swap") then -- HINT: you can notify user about success usage -- /ghost open [code] [user] [slot] elseif (cmd == "open") or (cmd == "close") then -- HINT: you can notify user about success usage -- /ghost [cmd] [code] [user] [message] elseif (cmd == "start") or (cmd == "abort") or (cmd == "pub") or (cmd == "priv") then -- HINT: you can notify user about success usage end -- FIXME: may be do not log commands from bot? (return -1) return 0 end function gh_callback_host(username, botname, gamename) local gametype = "5x5" if string.find(game.name, "3x3") then gametype = "3x3" end gh_set_userbot(username, botname, gamename, gametype) api.message_send_text(username, message_type_info, nil, localize(username, "Game \"{}\" is created by {}. You are an owner.", gamename, botname)) end function gh_callback_chost(username, botname, gamename) gh_set_userbot(username, botname, gamename, "") api.message_send_text(username, message_type_info, nil, localize(username, "Game \"{}\" is created by {}. You are an owner.", gamename, botname)) end function gh_callback_unhost(username, gamename) gh_del_userbot(username) api.message_send_text(username, message_type_info, nil, localize(username, "The game \"{}\" was destroyed.", gamename)) end function gh_callback_ping(username, players, pings) -- make sure that user is still in a game local account = api.account_get_by_name(username) local game = api.game_get_by_id(account.game_id) if not next(game) then return end -- make sure game owner is a bot if not gh_is_bot(game.owner) then return end -- get silent flag (do not send result to user) -- it's needed to do not show ping when user join a game, but we want to save the ping into database local silent = gh_get_silentflag(username) local users = {} i = 1 -- fill usernames for v in string.split(players, ",") do -- init each user table users[i] = {} users[i]["name"] = v i = i + 1 end i = 1 -- fill pings for v in string.split(pings, ",") do users[i]["ping"] = v i = i + 1 end local latency_all = "" local ms = localize(username, "ms") -- iterate each user for i,u in pairs(users) do -- load user pings local pings = account_get_botping(u["name"]) local is_found = false for k,v in pairs(pings) do -- update user ping for current bot if (v.bot == game.owner) then pings[k].ping = u["ping"] is_found = true end end -- if bot not found in database pings then add a new row if not is_found then local item = { date = os.time(), bot = game.owner, ping = u["ping"] } table.insert(pings, item) end -- save updated pings account_set_botping(u["name"], pings) latency_all = latency_all .. string.format("%s: [%s %s]; ", u["name"], u["ping"], ms) -- if game started send ping of each players on a new line if (game.status == game_status_started) and not (silent) then api.message_send_text(username, message_type_info, nil, localize(username, "{}'s latency to {}: {} {}", u["name"], game.owner, u["ping"], ms)) end end -- if game not started send pings of all players in a single line if not (game.status == game_status_started) and not (silent) then api.message_send_text(username, message_type_info, nil, localize(username, "Latency: {}", latency_all)) end end function gh_callback_gameresult(players, ratings, results) local users = {} -- fill table users i = 1 for v in string.split(players, ",") do -- init each user table users[i] = {} users[i]["name"] = v i = i + 1 end i = 1 for v in string.split(ratings, ",") do users[i]["rating"] = tonumber(v) i = i + 1 end i = 1 for v in string.split(results, ",") do users[i]["result"] = tonumber(v) i = i + 1 end -- iterate each user for i,u in pairs(users) do local rating, wins, losses, leaves, streaks = 0,0,0,0,0 local gametype = "" -- get stats if #users == 6 then gametype = "3x3" rating = account_get_dotarating_3x3(u["name"]) wins = account_get_dotawins_3x3(u["name"]) losses = account_get_dotalosses_3x3(u["name"]) leaves = account_get_dotaleaves_3x3(u["name"]) streaks = account_get_dotastreaks_3x3(u["name"]) elseif #users == 10 then gametype = "5x5" rating = account_get_dotarating_5x5(u["name"]) wins = account_get_dotawins_5x5(u["name"]) losses = account_get_dotalosses_5x5(u["name"]) leaves = account_get_dotaleaves_5x5(u["name"]) streaks = account_get_dotastreaks_5x5(u["name"]) else -- bad result ERROR(string.format("Bad game result (%s %s %s)", players, ratings, results)) return end -- calc new stats if u["result"] == 2 then -- leave & win leaves = leaves + 1 wins = wins + 1 elseif u["result"] == 1 then -- win wins = wins + 1 elseif u["result"] == 0 then -- loss losses = losses + 1 elseif u["result"] == -1 then -- leave & loss leaves = leaves + 1 losses = losses + 1 end local result_str = localize(u["name"], "won") if streaks >= 0 then -- win streak if u["result"] > 0 then streaks = streaks + 1 else streaks = -1 end else -- loss streak if u["result"] <= 0 then streaks = streaks - 1 else streaks = 1 end result_str = localize(u["name"], "lose") end -- update stats if #users == 6 then account_set_dotarating_3x3(u["name"], u["rating"]) account_set_dotawins_3x3(u["name"], wins) account_set_dotalosses_3x3(u["name"], losses) account_set_dotaleaves_3x3(u["name"], leaves) account_set_dotastreaks_3x3(u["name"], streaks) elseif #users == 10 then account_set_dotarating_5x5(u["name"], u["rating"]) account_set_dotawins_5x5(u["name"], wins) account_set_dotalosses_5x5(u["name"], losses) account_set_dotaleaves_5x5(u["name"], leaves) account_set_dotastreaks_5x5(u["name"], streaks) end -- diff between old and new rating local points = u["rating"]-rating -- inform user about new stats api.message_send_text(u["name"], message_type_info, nil, localize(u["name"], "You {} {} points.", result_str, points)) api.message_send_text(u["name"], message_type_info, nil, localize(u["name"], "New DotA ({}) rating: {} pts. Current {} streak: {}", gametype, rating, result_str, streaks)) end end pvpgn-1.99.7.2.1+dfsg/lua/ghost/ghost.lua000066400000000000000000000024561462640461400177430ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- custom map list gh_maplist = {} function gh_load() -- load ghost state local filename = config.vardir() .. "ghost_users.dmp" if file_exists(filename) then gh_load_userbots(filename) DEBUG("Loaded GHost state from " .. filename) end -- preload custom map list local mapfile = gh_directory() .. "/maplist.txt" if file_exists(mapfile) then gh_load_maps(mapfile) DEBUG("Loaded " .. #gh_maplist .. " GHost custom maps") else WARN("File with GHost custom map list is not found: " .. mapfile) end end function gh_unload() -- save ghost state local filename = config.vardir() .. "ghost_users.dmp" gh_save_userbots(filename) DEBUG("Saved GHost state to " .. filename) end function gh_load_maps(filename) -- load maps from the file file_load(filename, file_load_dictionary_callback, function(a,b) -- split second part of the line to get mapname and filename local idx = 0 for v in string.split(b, "|") do if idx == 0 then mapname = string:trim(v) else mapfile = string:trim(v) end idx = idx + 1 end table.insert(gh_maplist, { code = a, name = mapname, filename = mapfile }) end) endpvpgn-1.99.7.2.1+dfsg/lua/ghost/handle.lua000066400000000000000000000026221462640461400200450ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- function gh_handle_game_userjoin(game, account) -- check that game owner is ghost bot if not gh_is_bot(game.owner) then return end -- send ping silently gh_set_silentflag(account.name) local botaccount = api.account_get_by_name(game.owner) -- send ping to bot (to save result) command_ping(botaccount, "/ping") if (config.ghost_dota_server) then -- user who is owner of the hostbot in the current game local owner = gh_find_userbot_by_game(game.name) -- if game is not ladder if string:empty(gh_get_userbot_gametype(owner)) then return end for u in string.split(game.players, ",") do -- show stats of the player who joined the game for each other player local useracc = api.account_get_by_name(u) command_stats(useracc, "/stats " .. account.name) -- show stats of each player in the game to a player who joined the game command_stats(account, "/stats " .. u) end api.message_send_text(account.name, message_type_info, nil, localize(account.name, "Joined ladder game. Game owner: {}", owner)) end end function gh_handle_user_login(account) if gh_is_bot(account.name) then -- activate pvpgn mode on ghost side gh_message_send(account.name, "/pvpgn init"); DEBUG("init bot " .. account.name) end endpvpgn-1.99.7.2.1+dfsg/lua/ghost/helper.lua000066400000000000000000000076761462640461400201070ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- table key=value (user=bot) -- it fills first when user uses a command gh_user2bot = {} -- array with users who will not receive a response from /ping silentpings = {} -- return a user mapped bot name -- (nil if bot not mapped) function gh_get_userbot_name(username) if not next(gh_user2bot) or not gh_user2bot[username] then return nil end return gh_user2bot[username].bot end -- return a user mapped game name that created by bot function gh_get_userbot_game(username) if not next(gh_user2bot) or not gh_user2bot[username] then return nil end return gh_user2bot[username].game end -- return a user mapped game type that created by bot function gh_get_userbot_gametype(username) if not next(gh_user2bot) or not gh_user2bot[username] then return nil end return gh_user2bot[username].gametype end -- find gamename in games and return owner of the game (username) -- return false if not found function gh_find_userbot_by_game(gamename) for u,bot in pairs(gh_user2bot) do if (bot.game == gamename) then return u end end return false end function gh_set_userbot(username, botname, gamename, gametype) gh_user2bot[username] = { bot = botname, game = gamename, type = gametype, time = os.time() } end function gh_del_userbot(username) gh_user2bot[username] = nil end -- save table to file function gh_save_userbots(filename) table.save(gh_user2bot, filename) return true end -- load table from file function gh_load_userbots(filename) gh_user2bot = table.load(filename) -- debug info if next(gh_user2bot) then TRACE("Users assigned to bots:") for u,bot in pairs(gh_user2bot) do TRACE(u..": "..bot.bot.." ("..bot.game..", "..bot.time..")") end end end -- add user into a silenttable function gh_set_silentflag(username) table.insert(silentpings, account.name) end -- return true if user in a silenttable, and remove it function gh_get_silentflag(username) for k,v in pairs(silentpings) do if v == username then table.remove(silentpings, k) return true end end return false end -- Get path to GHost directory function gh_directory() return config.scriptdir .. "/ghost" end -- is user owner of bot in current game function gh_is_owner(account) -- 1) user in game if not account.game_id then return false end local game = game_get_by_id(account.game_id) -- 2) user owner of the bot in current game if not game.owner == gh_get_userbot_name(account.name) then return false end return true end -- is bot in authorized config list? function gh_is_bot(username) for i,bot in pairs(config.ghost_bots) do if string.lower(bot) == string.lower(username) then return true end end return false end -- redirect command from user to bot through PvPGN function gh_message_send(botname, text) -- send message to ghost from the server api.message_send_text(botname, message_type_whisper, nil, text) end -- return bot name (the best for user by ping) -- return nil if no available bots function gh_select_bot(username) local pings = account_get_botping(username) local botname = nil -- iterate all bots in config for i,bot in pairs(config.ghost_bots) do local is_found = false for k,v in pairs(pings) do if bot == v.bot then is_found = true end end -- if user has has no ping for the bot yet - use it if not is_found then botname = bot end end -- if user has all bots pings from config if not botname then -- select the best bot by ping (the table pings already sorted by ping) botname = pings[1].bot end local botacc = api.account_get_by_name(botname) -- if bot is offline then use first available -- FIXME: use next available by the best ping? if botacc.online == "false" then botname = nil for i,bot in pairs(config.ghost_bots) do botacc = api.account_get_by_name(bot) if botacc.online == "true" then return bot end end end return botname end pvpgn-1.99.7.2.1+dfsg/lua/ghost/maplist.txt000066400000000000000000000003331462640461400203160ustar00rootroot00000000000000************************************************** Custom maps that can be hosted using /chost ************************************************** DOTA = DotA|DotA v6.80c.w3x MW = Mirana Wars Final|Mirana Wars Final.w3xpvpgn-1.99.7.2.1+dfsg/lua/handle_channel.lua000066400000000000000000000011111462640461400204010ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- function handle_channel_message(channel, account, text, message_type) if config.quiz and channel.name == config.quiz_channel then quiz_handle_message(account.name, text) end --DEBUG(text) --return 1 end function handle_channel_userjoin(channel, account) --DEBUG(account.name.." joined "..channel.name) end function handle_channel_userleft(channel, account) --DEBUG(account.name.." left "..channel.name) end pvpgn-1.99.7.2.1+dfsg/lua/handle_client.lua000066400000000000000000000012031462640461400202510ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Receive SID_READMEMORY function handle_client_readmemory(account, request_id, data) --DEBUG("Read memory request Id: " .. request_id) -- display memory bytes --TRACE(data) if (config.ah) then ah_handle_client(account, request_id, data) end end -- Receive SID_EXTRAWORK function handle_client_extrawork(account, gametype, length, data) --DEBUG(string.format("Received EXTRAWORK packet with GameType: %d and Length: %d (%s)", gametype, length, data)) end pvpgn-1.99.7.2.1+dfsg/lua/handle_command.lua000066400000000000000000000057521462640461400204260ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- List of available lua commands -- (To create a new command - create a new file in directory "commands") local lua_command_table = { [1] = { ["/w3motd"] = command_w3motd, -- Quiz ["/quiz"] = command_quiz, -- GHost ["/ghost"] = command_ghost, ["/host"] = command_host, ["/chost"] = command_chost, ["/unhost"] = command_unhost, ["/ping"] = command_ping, ["/p"] = command_ping, ["/swap"] = command_swap, ["/open"] = command_open_close, ["/close"] = command_open_close, ["/start"] = command_start_abort_pub_priv, ["/abort"] = command_start_abort_pub_priv, ["/pub"] = command_start_abort_pub_priv, ["/priv"] = command_start_abort_pub_priv, ["/stats"] = command_stats, }, [8] = { ["/redirect"] = command_redirect, }, } -- Global function to handle commands -- ("return 1" from a command will allow next C++ code execution) function handle_command(account, text) -- find command in table for cg,cmdlist in pairs(lua_command_table) do for cmd,func in pairs(cmdlist) do if string.starts(text, cmd) then -- check if command group is in account commandgroups if math_and(account_get_auth_command_groups(account.name), cg) == 0 then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "This command is reserved for admins.")) return -1 end -- FIXME: we can use _G[func] if func is a text but not a function, -- like ["/dotastats"] = "command_dotastats" -- and function command_dotastats can be defined below, not only before return func(account, text) end end end return 1 end -- Executes before executing any command -- "return 0" stay with flood protection -- "return 1" allow ignore flood protection -- "return -1" will prevent next command execution silently function handle_command_before(account, text) -- special users for k,username in pairs(config.flood_immunity_users) do if (username == account.name) then return 1 end end if (config.ghost) then -- ghost bots for k,username in pairs(config.ghost_bots) do if (username == account.name) then return 1 end end end return 0 end -- Split command to arguments, -- index 0 is always a command name without a slash -- return table with arguments function split_command(text, args_count) local count = args_count local result = {} local tmp = "" -- remove slash from the command if not string:empty(text) then text = string.sub(text, 2) end i = 0 -- split by space for token in string.split(text) do if not string:empty(token) then if (i < count) then result[i] = token i = i + 1 else if not string:empty(tmp) then tmp = tmp .. " " end tmp = tmp .. token end end end -- push remaining text at the end if not string:empty(tmp) then result[count] = tmp end return result end pvpgn-1.99.7.2.1+dfsg/lua/handle_game.lua000066400000000000000000000054751462640461400177230ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Global function to handle game create function handle_game_create(game) --for i,j in pairs(game) do -- api.message_send_text(game.owner, message_type_info, game.owner, i.." = "..j) --end end -- Global function to handle user join to game function handle_game_userjoin(game, account) if config.ghost then gh_handle_game_userjoin(game, account) end end -- Global function to handle user left from game function handle_game_userleft(game, account) --for username in string.split(str,",") do -- if (account.name ~= username) then -- api.message_send_text(username, message_type_whisper, nil, "Bye ".. account.name) -- end --end end -- Global function to handle game end function handle_game_end(game) --api.message_send_text(game.owner, message_type_whisper, nil, "End game") end -- Global function to handle game report function handle_game_report(game) --for i,j in pairs(game) do -- api.message_send_text("harpywar", message_type_info, game.owner, i.." = "..j) -- api.message_send_text(game.owner, message_type_info, game.owner, i.." = "..j) --end --DEBUG(game.last_access) end -- Global function to handle game destroy function handle_game_destroy(game) --api.message_send_text(game.owner, message_type_whisper, nil, "Destroy game") end -- Global function to handle game status function handle_game_changestatus(game) --api.message_send_text(game.owner, message_type_info, nil, "Change status of the game to ".. game.status) end function handle_game_list(account) local gamelist = {} if (config.ghost and config.ghost_dota_server and account.clienttag == CLIENTTAG_WAR3XP) then -- get gamelist local glist = server_get_games() -- and add ping field for each game for i,game in pairs(glist) do glist[i].ping = 1000 -- initial ping value for all games -- if game owner is ghost bot if gh_is_bot(game.owner) then local pings = account_get_botping(account.name) local is_found = false for k,v in pairs(pings) do -- fetch user ping for current bot if (v.bot == game.owner) then glist[i].ping = pings[k].ping is_found = true end end -- if ping not found for the bot then use ping "0" to move the game on top -- (so, after join the game user will receive a ping from the bot) if not is_found then glist[i].ping = 0 end end end -- sort gamelist by ping ascending table.sort(glist, function(a,b) return tonumber(a.ping) < tonumber(b.ping) end) -- iterate sorted gamelist and fill a final list with a new order for i,game in pairs(glist) do table.insert(gamelist, game.id) table.insert(gamelist, game.name) end return {"id", "name"}, gamelist end end pvpgn-1.99.7.2.1+dfsg/lua/handle_server.lua000066400000000000000000000006551462640461400203130ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Loop each second function handle_server_mainloop() -- Tick all timers for t in pairs(__timers) do __timers[t]:tick() end -- DEBUG(os.time()) end -- When restart Lua VM function handle_server_rehash() if (config.ghost) then gh_unload() end endpvpgn-1.99.7.2.1+dfsg/lua/handle_user.lua000066400000000000000000000014341462640461400177570ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- function handle_user_whisper(account_src, account_dst, text) --DEBUG(account_src.name.."->"..account_dst.name.. ": ".. text) --return 1; end function handle_user_login(account) if (config.ghost) then gh_handle_user_login(account) end -- send SID_REQUIREDWORK --if account.archtag == ARCHTAG_WINX86 then -- api.client_requiredwork(account.name, "IX86ExtraWork.mpq") --end --DEBUG(account.name.." logged in") --return 1; end function handle_user_disconnect(account) --DEBUG(account.name.." disconnected") end function handle_user_icon(account, iconinfo) --TRACE("iconinfo"..iconinfo) --return iconinfo end pvpgn-1.99.7.2.1+dfsg/lua/include/000077500000000000000000000000001462640461400164045ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/lua/include/bitwise.lua000066400000000000000000000013101462640461400205500ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- -- Bitwise functions: -- math_not, math_and, math_or, math_xor -- local function nand(x,y,z) z=z or 2^16 if z<2 then return 1-x*y else return nand((x-x%z)/z,(y-y%z)/z,math.sqrt(z))*z+nand(x%z,y%z,math.sqrt(z)) end end function math_not(y,z) return nand(nand(0,0,z),y,z) end function math_and(x,y,z) return nand(math_not(0,z),nand(x,y,z),z) end function math_or(x,y,z) return nand(math_not(x,z),math_not(y,z),z) end function math_xor(x,y,z) return math_and(nand(x,y,z),math_or(x,y,z),z) endpvpgn-1.99.7.2.1+dfsg/lua/include/common.lua000066400000000000000000000012201462640461400203720ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Return the script path and line of the CURRENT function where it is executed __FUNCTION__ = nil -- Return the script path and line of the PARENT function where it is executed __function__ = nil setmetatable(_G, {__index = function(t, k) if k == '__FUNCTION__' then local w = debug.getinfo(2, "S") return w.short_src..":"..w.linedefined elseif k == '__function__' then local w = debug.getinfo(3, "S") return w.short_src..":"..w.linedefined end end }) pvpgn-1.99.7.2.1+dfsg/lua/include/convert.lua000066400000000000000000000013331462640461400205670ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Convert bytes table to integer function bytes_to_int(bytes, offset, length) local t = table.slice(bytes, offset, length) local n = 0 for k = 0, #t do n = n + byte_to_utf8(t[k]) * 2^((k)*8) end return n end -- Convert bytes table to string function bytes_to_string(bytes) local t = bytes; local bytearr = {} for k = 0, #t do table.insert( bytearr, string.char(byte_to_utf8(t[k])) ) end return string:trim( table.concat(bytearr) ) end -- Convert C byte to Dec function byte_to_utf8(v) return v < 0 and (0xff + v + 1) or v endpvpgn-1.99.7.2.1+dfsg/lua/include/file.lua000066400000000000000000000027701462640461400200340ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Read file line by line -- callback 2 is optional -- You can process line with function callback1(line, callback2) function file_load(filename, callback1, callback2) local file = io.open(filename, "r") if file then for line in file:lines() do if callback2 then callback1(line, callback2) else callback1(line) end end file.close(file) TRACE("File read " .. filename) else ERROR("Could not open file " .. filename) return false end return true end -- (callback) for "file_load" to load file with each line format like "key = value" function file_load_dictionary_callback(line, callback) if string:empty(line) then return 0 end local idx = 0 local a, b for v in string.split(line, "=") do if idx == 0 then a = string:trim(v) else b = string:trim(v) end idx = idx + 1 end if not string:empty(b) and not string:empty(a) then callback(a, b) end end -- Save raw text "data" into a filename function file_save(data, filename) local file = io.open(filename, "w") file:write(data) file:close() end -- Save file using callback function file_save2(filename, callback) local file = io.open(filename, "w") callback(file) file:close() end -- Check file for exist function file_exists(filename) local f=io.open(filename, "r") if f~=nil then io.close(f) return true else return false end end pvpgn-1.99.7.2.1+dfsg/lua/include/math.lua000066400000000000000000000006051462640461400200410ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- return rounded float value function math.round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end function math.isnan(x) return x ~= x end function math.isinf(x) return (x+1)==x endpvpgn-1.99.7.2.1+dfsg/lua/include/string.lua000066400000000000000000000033111462640461400204130ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Split text into table by delimeter -- Usage example: string.split("one,two",",") function string:split(str, sep) str = str or '%s+' local st, g = 1, self:gmatch("()("..str..")") local function getter(segs, seps, sep, cap1, ...) st = sep and seps + #sep return self:sub(segs, (seps or 0) - 1), cap1 or sep, ... end return function() if st then return getter(st, g()) end end end -- Check string is nil or empty -- bool -- Usage example: string:empty("") -> true, string:empty(nil) -> true function string:empty(str) return str == nil or str == '' end -- bool function string.starts(str, starts) if string:empty(str) then return false end return string.sub(str,1,string.len(starts))==starts end -- bool function string.ends(str, ends) if string:empty(str) then return false end return ends=='' or string.sub(str,-string.len(ends))==ends end -- Replace string -- Usage example: string.replace("hello world","world","Joe") -> "hello Joe" function string.replace(str, pattern, replacement) if string:empty(str) then return str end local s, n = string.gsub(str, pattern, replacement) return s end function string:trim(str) if string:empty(str) then return str end return (str:gsub("^%s*(.-)%s*$", "%1")) end -- Replace char in specified position of string function replace_char(pos, str, replacement) if string:empty(str) then return str end return str:sub(1, pos-1) .. replacement .. str:sub(pos+1) end -- return count of substr in str function substr_count(str, substr) local _, count = string.gsub(str, substr, "") return count endpvpgn-1.99.7.2.1+dfsg/lua/include/table.lua000066400000000000000000000140301462640461400201740ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Clear table function table.clear(_table) for k in pairs(_table) do _table[k] = nil end end -- Get table size function table.count(T) if not T or not next(t) then return 0 end local count = 0 for _ in pairs(T) do count = count + 1 end return count end -- Get table slice from offset to length function table.slice(values, offset, length) local res = {} local n = #values -- default values for range offset = offset or 0 if offset < 0 or offset > n then return {} end length = length or n-offset if length < 1 then length = 1 elseif length > n-offset then length = n-offset end for i = offset,offset+length-1 do res[i-offset] = values[i] end return res end -- (Debug) Print contents of `tbl`, with indentation. -- `indent` sets the initial level of indentation. function table.dump(t, ident) local output = "" if not indent then indent = 0 end -- if table has 0 index then display this key at start if not (t[0] == nil) then for k=0, #t do local formatting = "\n" .. string.rep(" ", indent) .. k .. ": " local v = t[k] if type(v) == "table" then v = "[" .. table.print(v, indent+1) .. "]" elseif type(v) == 'boolean' then v = tostring(v) end output = output .. formatting .. v end else for k,v in pairs(t) do local formatting = "\n" .. k .. ": " local v = t[k] if type(v) == "table" then v = "[" .. table.print(v, indent+1) .. "]" elseif type(v) == 'boolean' then v = tostring(v) end output = output .. formatting .. v end end return output end -- Find table key by value function get_key_for_value( t, value ) for k,v in pairs(t) do if v==value then return k end end return nil end --[[ Save Table to File Load Table from File v 1.0 Lua 5.2 compatible Only Saves Tables, Numbers and Strings Insides Table References are saved Does not save Userdata, Metatables, Functions and indices of these ---------------------------------------------------- table.save( table , filename ) on failure: returns an error msg ---------------------------------------------------- table.load( filename or stringtable ) Loads a table that has been saved via the table.save function on success: returns a previously saved table on failure: returns as second argument an error msg ---------------------------------------------------- Licensed under the same terms as Lua itself. ]]-- do -- declare local variables --// exportstring( string ) --// returns a "Lua" portable version of the string local function exportstring( s ) return string.format("%q", s) end --// The Save Function function table.save( tbl,filename ) local charS,charE = " ","\n" local file,err = io.open( filename, "wb" ) if err then return err end -- initiate variables for save procedure local tables,lookup = { tbl },{ [tbl] = 1 } file:write( "return {"..charE ) for idx,t in ipairs( tables ) do file:write( "-- Table: {"..idx.."}"..charE ) file:write( "{"..charE ) local thandled = {} for i,v in ipairs( t ) do thandled[i] = true local stype = type( v ) -- only handle value if stype == "table" then if not lookup[v] then table.insert( tables, v ) lookup[v] = #tables end file:write( charS.."{"..lookup[v].."},"..charE ) elseif stype == "string" then file:write( charS..exportstring( v )..","..charE ) elseif stype == "number" then file:write( charS..tostring( v )..","..charE ) end end for i,v in pairs( t ) do -- escape handled values if (not thandled[i]) then local str = "" local stype = type( i ) -- handle index if stype == "table" then if not lookup[i] then table.insert( tables,i ) lookup[i] = #tables end str = charS.."[{"..lookup[i].."}]=" elseif stype == "string" then str = charS.."["..exportstring( i ).."]=" elseif stype == "number" then str = charS.."["..tostring( i ).."]=" end if str ~= "" then stype = type( v ) -- handle value if stype == "table" then if not lookup[v] then table.insert( tables,v ) lookup[v] = #tables end file:write( str.."{"..lookup[v].."},"..charE ) elseif stype == "string" then file:write( str..exportstring( v )..","..charE ) elseif stype == "number" then file:write( str..tostring( v )..","..charE ) end end end end file:write( "},"..charE ) end file:write( "}" ) file:close() end --// The Load Function function table.load( sfile ) local ftables,err = loadfile( sfile ) if err or not ftables() then return _,err end local tables = ftables() for idx = 1,#tables do local tolinki = {} for i,v in pairs( tables[idx] ) do if type( v ) == "table" then tables[idx][i] = tables[v[1]] end if type( i ) == "table" and tables[i[1]] then table.insert( tolinki,{ i,tables[i[1]] } ) end end -- link indices for _,v in ipairs( tolinki ) do tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil end end return tables[1] end -- close do end -- ChillCodepvpgn-1.99.7.2.1+dfsg/lua/include/timer.lua000066400000000000000000000024331462640461400202310ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Global table with timers __timers = {} function timer_add(id, interval, callback) timer_object = timer:new(id, interval, callback) table.insert(__timers, timer_object) end function timer_del(id) -- safe remove from the timers (we can not just use __timers = nil here) local i = 0 for k,v in pairs(__timers) do i = i + 1 if (v.id == id) then table.remove(__timers, i) return true end end return false end function timer_get(id) local i = 0 for k,v in pairs(__timers) do i = i + 1 if (v.id == id) then return v end end end -- -- Timer class -- timer = {} -- Create a new timer with unique id and given interval function timer:new(id, interval, callback) options = { id = id, interval = interval, prev_time = 0, callback = callback } self.__index = self return setmetatable(options, self) end -- Event when timer executes function timer:tick() if os.time() < self.prev_time + self.interval then return 0 end self.prev_time = os.time() -- Debug: display time when the timer ticks -- TRACE(self.interval .. ": " .. os.time()) -- execute callback function return self.callback(self) end pvpgn-1.99.7.2.1+dfsg/lua/main.lua000066400000000000000000000005361462640461400164140ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Entry point -- Executes after preload all the lua files function main() if (config.ah) then -- start antihack ah_init() end if (config.ghost) then gh_load() end end pvpgn-1.99.7.2.1+dfsg/lua/quiz/000077500000000000000000000000001462640461400157515ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/lua/quiz/command.lua000066400000000000000000000075021462640461400200760ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- /quiz function command_quiz(account, text) if not config.quiz then return 1 end local args = split_command(text, 2) if (args[1] == "start") then return q_command_start(account, args[2]) elseif (args[1] == "stop") then return q_command_stop(account) elseif (args[1] == "stats") then if not args[2] then return q_command_toplist(account) else return q_command_stats(account, args[2]) end end api.describe_command(account.name, args[0]) return -1 end -- Start quiz in current channel function q_command_start(account, filename) if not account_is_operator_or_admin(account.name) then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "You must be at least a Channel Operator to use this command.")) return -1 end local channel = api.channel_get_by_id(account.channel_id) if not channel then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "This command can only be used inside a channel.")) return -1 end if config.quiz_channel then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "Quiz has already ran in channel \"{}\". Use /quiz stop to force finish.", config.quiz_channel)) return -1 end -- check if file exists if not filename or not file_exists(q_directory() .. "/questions/" .. filename .. ".txt") then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "Available Quiz dictionaries: ")) api.message_send_text(account.name, message_type_error, account.name, " " .. config.quiz_filelist) return -1 end quiz:start(channel.name, filename) return 0 end -- Stop quiz function q_command_stop(account) if not account_is_operator_or_admin(account.name) then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "You must be at least a Channel Operator to use this command.")) return -1 end if not config.quiz_channel then api.message_send_text(account.name, message_type_error, account.name, localize(account.name, "Quiz is not running.")) return -1 end quiz:stop(account.name) return 0 end -- Display Quiz Top players record function q_command_toplist(account) -- load records (if it was not loaded yet) if not q_load_records() then return -1 end local output = localize(account.name, "Top {} Quiz records:", config.quiz_users_in_top) api.message_send_text(account.name, message_type_info, account.name, output) -- display TOP of total records for i,t in pairs(q_records_total) do if (i > config.quiz_users_in_top) then break end local output = string.format(" %d. %s [%d %s]", i, t.username, t.points, localize(account.name, "points")) api.message_send_text(account.name, message_type_info, account.name, output) end return 0 end -- Display single player's record function q_command_stats(account, username) -- load records (if it was not loaded yet) if not q_load_records() then return -1 end local found = false -- find user in records for i,t in pairs(q_records_total) do if string.upper(t.username) == string.upper(username) then api.message_send_text(account.name, message_type_info, account.name, localize(account.name, "{}'s Quiz record:", t.username)) local output = string.format(" %d. %s [%d %s]", i, t.username, t.points, localize(account.name, "points")) api.message_send_text(account.name, message_type_info, account.name, output) found = true end end if not found then api.message_send_text(account.name, message_type_info, account.name, localize(account.name, "{} has never played Quiz.", username)) end return 0 end pvpgn-1.99.7.2.1+dfsg/lua/quiz/helper.lua000066400000000000000000000023061462640461400177340ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Get path to Quiz directory function q_directory() return config.scriptdir .. "/quiz" end -- Replace each symbol in string with * -- Example: input = "hello world", return = "***** *****" function q_hide_unswer(input) local output = input for i = 1, #input do local c = input:sub(i,i) if not (c == " ") then output = replace_char(i, output, "*") end end return output end -- Open one random symbol in hidden (with *) string from original string -- Example: hidden = "***** *****", original = "hello world", return = "***l* *****" function q_show_next_symbol(hidden, original) local output = hidden if hidden == original then return output end local replaced = false while not replaced do local i = math.random(#hidden) local c = hidden:sub(i,i) if c == "*" then local c2 = original:sub(i,i) output = replace_char(i, hidden, c2) replaced = true end end return output end -- Callback to sort records table descending function q_compare_desc(a,b) return tonumber(a.points) > tonumber(b.points) end pvpgn-1.99.7.2.1+dfsg/lua/quiz/questions/000077500000000000000000000000001462640461400200035ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/lua/quiz/questions/dota.txt000066400000000000000000000137351462640461400215040ustar00rootroot00000000000000************************************************** Category: DotA Author: AGOES ************************************************** One of illegal tools for playing dota? = Maphack How many kills needed for the title killing spree? = three How many kills needed for the title Dominating? = four How many kills needed for the title Megakill? = five How many kills needed for the title Unstoppable? = six How many kills needed for the title Whicked Sick? = seven How many kills needed for the title Monster Kill? = eight How many kills needed for the title GodLike? = nine How many kills needed for the title Beyond Godlike(Holy Shit!)? = ten First kill of the Game? = First Blood Two Kills simutaneously. = Double Kill Three Kills simutaneously. = Triple Kill Hero Nicknames: Vengeful Spirit. = Vs Hero Nicknames: Leshrac the Malicious Tormented Soul. = Leshrac Hero Nicknames: Kel'Thuzad the Lich King. = Lich Hero Nicknames: Krobelus the Death Prophet. = Dp Hero Nicknames: Lion the Demon Witch. = Lion Hero Nicknames: Lesale Deathbringer the Venomancer. = Veno Hero Nicknames: Magnus the Magnataur. = Magnataur Hero Nicknames: Visage The Necro'lic. = Visage Hero Nicknames: Nessaj the Chaos Knight. = Ck Hero Nicknames: Banehallow the Lycanthrope. = Lycan Hero Nicknames: Black Arachnia the Broodmother. = Brood Hero Nicknames: Mortred the Phantom Assassin. = Pa Hero Nicknames: Medusa the Gorgon. = Medusa Hero Nicknames: Balanar the NightStalker. = Ns Hero Nicknames: King Leoric the Skeleton King. = Leo Hero Nicknames: Lucifer the DoomBringer. = Lucy Hero Nicknames: Pharoh'moth the Nerubian Assassin. = Na Hero Nicknames: Slardar the Slithereen Guard. = Slardar Hero Nicknames: Akasha the Queen of Pain. = QoP Hero Nicknames: Bone Clinkz the Bone Fletcher. = Clinkz Hero Nicknames: Darkterror the Faceless Void. = Void Hero Nicknames: Viper the Netherdrake. = Viper Hero Nicknames: Razor the Lightning Revenant. = Razor Hero Nicknames: Nai'x the Lifestealer. = Naix Hero Nicknames: Pugna the Oblivion. = Pugna Hero Nicknames: Leviathan the Tidehunter. = Levi Hero Nicknames: Atropos the Bane Elemental. = Bane Hero Nicknames: Rotund'jere the Necrolyte. = Necro Hero Nicknames: Pudge the Butcher. = Pudge Hero Nicknames: Barathrum the Spiritbreaker. = Sb Hero Nicknames: Anub'seran the Nerubian Weaver. = Weaver Hero Nicknames: Crixalis the Sandking. = Sk Hero Nicknames: Mogul Kahn the Axe. = Axe Hero Nicknames: Strygwyr the BloodSeeker. = seeker Ultimate: Nether Swap. = Vengeful Spirit Ultimate: Thunder God's Wrath. = Lord of Olympia Ultimate: Untouchable. = Enchantress Ultimate: Replicate. = Morphling Ultimate: Freezing Field. = Crystal Maiden Ultimate: God's Strength. = Rogue Knight Ultimate: Song of the Siren. = Naga Siren Ultimate: Echo Slam. = Earth Shaker Ultimate: Permanent Invisibility. = Stealth Assassin Ultimate: True Form. = Lone Druid Ultimate: Laguna Blade. = Slayer Ultimate: Omnislash. = Juggernaught Ultimate: Global Silence. = Silencer Ultimate: Overgrowth. = Treant Protector Ultimate: Blackhole. = Enigma Ultimate: Ignis Fatuus. = Keeper of the Light Ultimate: Enrage. = Ursa Warrior Ultimate: Phantom Edge. = Phantom Lancer Ultimate: Mutli Cast. = Ogre Magi Ultimate: Rearm. = Tinker Ultimate: Wrath of Nature. = Prophet Ultimate: Grow. = Stone Giant Ultimate: Remote Mines. = Goblin Techies Ultimate: Hand of God. = Holy Knight Ultimate: Eclispe. = Moon rider Ultimate: Assassinate. = Dwarven Sniper Ultimate: Rampage. = Troll Warlord Ultimate: Mass Serpent Wards. = Shadow Shaman Ultimate: Warpath. = Bristleback Ultimate: Primal Split. = Pandaren Battlemaster Ultimate: Great Fortitude. = Centaur Warchief Ultimate: Track. = Bounty Hunter Ultimate: Elder Dragon Form. = Dragon Knight Ultimate: Mana Void. = Anti-Mage Ultimate: Marksmanship. = Drow Ranger Ultimate: Guardian Angel. = Omni Knight Ultimate: Sunder. = Soul Keeper Ultimate: Pulse Nova. = Tormented Soul Ultimate: Chain Frost. = Lich Ultimate: Exorcism. = Death Prophet Ultimate: Finger of Death. = Demon Witch Ultimate: Poison Nova. = Venomancer Ultimate: Reverse Polarity. = Magnataur Ultimate: Raise Revenants. = Necro'lic Ultimate: Phantasm. = Chaos Knight Ultimate: Shapeshift. = Lycanthrope Ultimate: Insatiable Hunger. = Broodmother Ultimate: Coupe De Grace. = Phantom Assassin Ultimate: Purge. = Gorgon Ultimate: Darkness. = Night Stalker Ultimate: Reincarnation. = Skeleton King Ultimate: Doom. = Doom Bringer Ultimate: Vendetta. = Nerubian Assassin Ultimate: Amplify Damage. = Slithereen Guard Ultimate: Sonic Wave. = Queen of Pain Ultimate: Death Pact. = Bone Fletcher Ultimate: Chronosphere. = Faceless Void Ultimate: Viper Strike. = Netherdrake Ultimate: Rage. = Lifestealer Ultimate: Life Drain. = Oblivion Ultimate: Ravage. = Tidehunter Ultimate: Fiend's Grip. = Bane Elemental Ultimate: Reaper's Scythe. = Necrolyte Ultimate: Disember. = Butcher Ultimate: Nether Strike. = Spiritbreaker Ultimate: Time Lapse. = Nerubian Weaver Ultimate: Requim of Souls. = Shadow Fiend Ultimate: Epicenter. = Sand King Ultimate: Culling Blade. = Axe Ultimate: Rupture. = Bloodseeker Armor Type: Spirit Tower = Fortified Armor Type: The Frozen Throne = Fortified Cost: Belt of Giant Strength = 450 Cost: Blades of Alarcrity = 1000 Cost: Blades of Attack in map 6.73c = 450 Cost: Boots of Elvenskin = 450 Cost: Boots of Speed = 500 Cost: Broadsword = 1200 Cost: Claymore = 1400 Cost: Dagger of Escape = 2150 Cost: Demon Edge = 2400 Cost: Eaglehorn = 3300 Cost: Gauntlets of Ogre Strength = 150 Cost: Gem of True Sight = 700 Cost: Helm of Iron Will = 950 Cost: Mantle of Intelligence = 150 Cost: Mask of Death = 900 Cost: Mithril Hammer = 1600 Cost: Ogre Axe = 1000 Cost: Platemail = 1400 Cost: Robe of the Magi = 450 Cost: Sacred Relic = 3800 Cost: Scroll for Black King Bar = 1300 Cost: Scroll for Dagon = 1350 Cost: Scroll for Heart of Tarrasque = 1200 Cost: Scroll for Null Talisman = 170 Cost: Scroll for Radiance = 1325 Cost: Scroll for Stygian Desolator = 1200 Cost: Slippers of Agility = 150 Cost: Staff of Wizardry = 1000 Cost: Ultimate Orb = 2100 Cost: Vitality Booster = 1200 Cost: Void Stone = 875 pvpgn-1.99.7.2.1+dfsg/lua/quiz/questions/misc.txt000066400000000000000000013617511462640461400215150ustar00rootroot00000000000000************************************************** Category: Various Author: unknown ************************************************** What is the worlds #1 most populous city = seoul What is the worlds #2 most populous city = sao paulo, brazil What is the worlds #3 most populous city = bombay What is the worlds #4 most populous city = jakarta What is the worlds #5 most populous city = moscow What is the worlds #6 most populous city = istanbul What is the worlds #7 most populous city = mexico city What is the worlds #8 most populous city = shanghai What is the worlds #9 most populous city = tokyo What is the worlds #10 most populous city = new york city What is the worlds #11 most populous city = beijing What is the worlds #12 most populous city = delhi, india What is the worlds #13 most populous city = london What is the worlds #14 most populous city = cairo What is the worlds #15 most populous city = teheran What is the worlds #16 most populous city = hong kong What is the worlds #17 most populous city = bangkok What is the worlds #18 most populous city = tianjin What is the worlds #19 most populous city = lima What is the worlds #20 most populous city = rio de janeiro What is the worlds #21 most populous city = santafe de bogota, colombia What is the worlds #22 most populous city = shenyang What is the worlds #23 most populous city = santiago, chile What is the worlds #24 most populous city = calcutta What is the worlds #25 most populous city = st. petersburg What is the worlds #26 most populous city = wuhan What is the worlds #27 most populous city = guangzhou What is the worlds #28 most populous city = madras What is the worlds #29 most populous city = baghdad What is the worlds #30 most populous city = pusan, south korea Who isolated adrenaline in 1897 = john jacob abel In what year did John Jacob Abel isolate adrenalin = 1897 Who invented the aerosol can = erik rotheim In what year did Erik Rotheim invent the aerosol can = 1926 In what country was Erik Rotheim, the inventor of the aerosol can, born = norway Who invented the air brake = george westinghouse In what year did George Westinghouse invent the air brake = 1868 Who invented the air conditioner = willis carrier In what year did Willis Carrier invent air conditioning = 1911 Who invented aluminum manufacture, (by electrolytic action) = charles hall In what year did Charles M. Hall invent aluminum manufacture, (by electrolytic action) = 1866 Who invented anesthetic: (first use of anesthetic-ether-on humans) = crawford long In what year was anesthetic (ether) first used on humans = 1842 Who invented the electric fan = schuyler wheeler In what year did Schuyler Wheeler invent the electric fan = 1882 Who invented the electric flatiron = henry seely In what year did Henry Seely invent the electric flat iron = 1882 Who invented the electric washing machine = alva fisher In what year did Alvin Fisher invent the electric washing machine = 1906 Who invented aspirin = dr felix hoffman In what year did Dr Felix Hoffman invent aspirin = 1899 Who first proposed the concept of atomic structure in 1911 = ernest rutherford The first internal combustion engine was developed in 1885 by = karl benz Who discovered bacteria in 1683 = anton van leeuwenhoek In what year did Anton van Leeuwenhoek discover bacteria = 1683 Who invented barbed wire in 1873 = joseph e. glidden In what year did Joseph E. Glidden invent barbed wire = 1873 Who invented the barometer in 1643 = evangelista torricelli In what year did Evangelista Torricelli invent the barometer = 1643 Who invented the first MODERN bicycle in 1884 = james starley Who founded the FIRST "Big Bang" theory in 1927 (the universe originated with a huge explosion) = george lemaitre1927 Who founded the knowledge of blood circulation = william harvey In what year did William Harveyfound the knowledge of blood circulation = 1628 Who invented braille in 1829 = louis braille In what year was braille invented = 1829 Who invented the conical bullet = claude minie In what year did Claude Minie invent the conical bullet = 1849 Who was the inventor of the calculating machine in 1614 = john napier Who was the inventor of the slide rule in 1632 = william oughtred Who was the inventor of the digital calculator in 1642 = blaise pascal In what year did John Napier invent the calculating machine = 1614 In what year did William Oughtred invent the slide rule = 1632 In what year did Blaise Pascal invent the digital calculator = 1642 Who invented the carpet sweeper in 1876 = melville r bissell In what year did Melville R Bissell invent the carpet sweeper = 1876 In what year was the car radio invented by William Lear and Elmer Wavering = 1929 Who invented cement in 1824 = joseph aspdin In what year did Joseph Aspdin invent cement = 1824 Who invented chewing gum in 1848 = john curtis In what year did John Curtis invent chewing gum = 1848 Who invented the pendulum clock in 1656 = christian huygens In what year did Christian Huygens invent the pendulum clock = 1656 What company invented the compact disk in 1972 = rca In what year did RCA invent the compact disk = 1972 Who invented condensed milk in 1853 = gail borden In what year did Gail Borden invent condensed milk = 1853 Who defined the contagion theory (infectious diseases caused by living agent transmitted from person to person) in 1546 = girolamo Fracastoro In what year did Girolamo Fracastoro define the contagion theory = 1546 Who defined the continental drift theory in 1858 = antonio snider-pellegrini In what year did Antonio Snider-Pellegrini define the continental drift theory = 1858 In what country were cosmetics first used = egypt In what year were cosmetics first used = 4000 b c Who first postulated the cosamic string theory = thomas kibble In what year did Thomas Kibble first postulate the cosamic theory = 1976 Where was the crossbow invented = china In what year was the crossbow invented = 300 b c The elevator was invented by = elisha otis In what year did Elisha Otis invent the elevator = 1852 Who discovered the fermentation process in 1860 = louis pasteur In what year was the fermentation process discovered by Louis Pasteur = 1860 Who invented fiber optics = narinder kapany In what year was fiber optics discovered by Narinder Kapany = 1955 Frozen food was first developed by = clarence birdseye Gunpowder was invented in = china In what year was gunpowder invented = 700 The gyrocompass was invented by = elmer sperry In what year did Elmer Sperry invent the gyrocompass = 1905 After whom was Halley's Comet named = edmund halley Who invented the holograph = dennis gabor In what year was the holograph invented = 1947 Who developed the Ice age theory in 1840 = louis agassiz Who developed the LCD (liquid crystal display) = hoffmann-la roche In what year was the LCD first developed = 1970 Who invented the first bifocal lens = benjamin franklin In what year was the first bifocal lens developed = 1760 Who developed the first cylinder lock = linus yale In what year was the first cylinder lock developed = 1851 Who invented the 1st generation machine gun in 1718 = james puckle Who invented the 2nd generation machine gun in 1861 = richard j gatling In what year did James Puckle invent the first generation machine gun = 1718 In what year did Richard J Gatling invent the second generation machine gun = 1861 Who was the inventor of the phosphorus match = francois derosne In what year did Francois Derosne invent the phosphorus match = 1816 Who invented the microphone = charles wheatstone In what year did Charles Wheatstone invent the microphone = 1827 Who invented the microwave oven = percy spencer In what year was the microwave oven invented = 1947 Who invented the moving assembly line in 1913 = henry ford IN what year did Henry Ford invent the moving assembly line = 1913 Who first discovered the existence of Neptune = johann galle In what year did Johann Galle first discover the existence of Neptune = 1846 Who discovered the neutron = james chadwick In what year was the neurton first discovered = 1932 Nitroglycerin was developed by = ascanio sobrero In what year did Ascanio Sobrero develop nitroglycerin = 1846 Who developed the first nuclear reactor = enrico fermi In what year did Enrico Fermi develop the first nuclear reactor = 1942 Who invented the first oil well = edwin drake In what year was the first oil well invented = 1859 Who discovered the ozone = christian schonbein In what year did Christian Schonbein discover the ozone = 1839 Who developed the first internal pacemaker = clarence lillehie In what year did Clarence Lillehie invent the first internal pacemaker = 1957 Paper was invented in China in = 100 a d The parachute was invented in 1783 by = louis lenormand In what year did Louis Lenormand invent the parachute = 1783 Who developed the laws of planetary motion = johannes kepler Who invented plate tectonics = alfred wegener Where was the forked plow invented = mesopotamia When was the forked plow invented = before 3000 b c Who discovered the positron in 1932 = carl anderson In what year did Carl Anderson discover the positron = 1932 Who invented the pressure cooker (early version) = denis papin In what year did Denis Papin invent the pressure cooker = 1679 Who discovered the proton in England in 1919 = ernest rutherford What year did Ernest Rutherford discover the proton = 1919 Who invented rabies immunization = louis pasteur In what year did Louis Pasteur invent rabies immunization = 1885 Who invented the first practical radar = sir robert watson-watt In what year did Sir Robert Watson-Watt invent the first practical radar = 1934 Who discovered radiocarbon dating = willard libby In what year did Willard Libby discover radiocarbon dating = 1947 In what year did Willard Libby first demonstrate radiocarbon dating = 1950 Who was the first person to receive radio noise signals from outside our galaxy = karl jansky In what year did Karl Jansky receive the first radio noise signals from outer space = 1931 Who successfully marketed the first safety razor = king gillette In what year was the first safety razor successfully marketed = 1901 Who invented the revolver = samuel colt In what year did Samuel Colt invent the revolver = 1835 Name the inventor of the Richter scale = charles richter In what year did Charles Richter invent the Richter scale = 1935 Who invented the automatic rifle = john browning In what year did John Browning invent the automatic rifle = 1918 Who invented the liquid-fuelled rocket = robert goddard In what year did Robert GToddard invent the liquid-fuelled rocket = 1926 Who invented the vulcanizationprocess of rubber = charles goodyear In what year did Charles Goodyear invent the vulcanization process of rubber = 1839 Who invented the safety pin = walter hunt In what year did Walter Hunt invent the safety pin = 1849 Who invented the first accurate seismograph = john milne In what year did John Milne invent the first accurate seismograph = 1880 Who invented the first sewing machine = elias howe In what year was the first sewing machine invented = 1846 Who invented the first continuous stitch sewing machine = isaac singer In what year was the first continuous stitch sewing machine invented = 1851 Who was the first person to make the first realistic application of solar energy using parabolic solar reflector to drive caloric engine on steam boiler = john ericsson In what year did John Ericsson make the first realistic application of solar energy using parabolic solar reflector to drive caloric engine on steam boiler = 1860 Last names of the two men who invented spectrum analysis = kirchhoff & bunsen = bunsen & kirchhoff In what year did Kirchhoff & Bunsen invent spectrum analysis = 1859 Who invented the stethoscope = rene laennec In what year did Rene Laennec invent the stethoscope = 1819 Who invented the military tank = sir ernest swinton In what year did Sir Ernest Swinton invent the military tank = 1914 Who invented the tape recorder (magnetic steel tape) = valdemar poulsen In what year was the tape recorder invented = 1899 Who invented teflon = dupont In what year was teflon invented = 1943 Who invented the electric transformer = william stanley In what year was the electric transformer invented = 1885 What is the element symbol for Actinium = ac What is the element symbol for Aluminum = al What is the element symbol for Americum = am What is the element symbol for Antimony = sb What is the element symbol for Argon = ar What is the element symbol for Arsenic (gray) = as What is the element symbol for Astatine = at What is the element symbol for Barium = ba What is the element symbol for Berkelium = bk What is the element symbol for Berylium = be What is the element symbol for Bismuth = bi What is the element symbol for Bohrium = bh What is the element symbol for Boron = b What is the element symbol for Bromine = br What is the element symbol for Cadmium = cd What is the element symbol for Calcium = ca What is the element symbol for Californium = cf What is the element symbol for Carbon = c What is the element symbol for Cerium = ce What is the element symbol for Cesium = cs What is the element symbol for Chlorine = cl What is the element symbol for Chromium = cr What is the element symbol for Cobalt = co What is the element symbol for Copper = cu What is the element symbol for Curium = cm What is the element symbol for Dubnium = db What is the element symbol for Dysprosium = dy What is the element symbol for Einsteinium = es What is the element symbol for Erbium = er What is the element symbol for Europium = eu What is the element symbol for Fermium = fm What is the element symbol for Fluorine = f What is the element symbol for Francium = fr What is the element symbol for Gadolinium = gd What is the element symbol for Gallium = ga What is the element symbol for Germanium = ge What is the element symbol for Gold = au What is the element symbol for Hafnium = hf What is the element symbol for Hassium = hs What is the element symbol for Helium = he What is the element symbol for Holmium = ho What is the element symbol for Hydrogen = h What is the element symbol for Indium = in What is the element symbol for Iodine = i What is the element symbol for Iridium = ir What is the element symbol for Iron = fe What is the element symbol for Krypton = kr What is the element symbol for Lanthanum = la What is the element symbol for Lawrencium = lr What is the element symbol for Lead = pb What is the element symbol for Lithium = li What is the element symbol for Lutetium = lu What is the element symbol for Magnesium = mg What is the element symbol for Manganese = mn What is the element symbol for Meitnerium = mt What is the element symbol for Mendelevium = md What is the element symbol for Mercury = hg What is the element symbol for Molybdenum = mo What is the element symbol for Neodymium = nd What is the element symbol for Neon = ne What is the element symbol for Neptunium = np What is the element symbol for Nickel = ni What is the element symbol for Niobium (Columbium) = nb What is the element symbol for Nitrogen = n What is the element symbol for Osmium = os What is the element symbol for Palladium = pd What is the element symbol for Phosphorous (white) = p What is the element symbol for Platinum = pt What is the element symbol for Plutonium = pu What is the element symbol for Polonium = po What is the element symbol for Potassium = k What is the element symbol for Praseodymium = pr What is the element symbol for Promethium = pm What is the element symbol for Protactinium = pa What is the element symbol for Radium = ra What is the element symbol for Radon = rn What is the element symbol for Rhenium = re What is the element symbol for Rhodium = rh What is the element symbol for Rubidium = rb What is the element symbol for Rutherfordium = rf What is the element symbol for Ruthenium = ru What is the element symbol for Samarium = sm What is the element symbol for Scandium = sc What is the element symbol for Seaborgium = sg What is the element symbol for Selenium (gray) = se What is the element symbol for Silicon = si What is the element symbol for Silver = ag What is the element symbol for Sodium = na What is the element symbol for Strontium = sr What is the element symbol for Sulfur = s What is the element symbol for Tantalum = ta What is the element symbol for Technetium = tc What is the element symbol for Tellurium = te What is the element symbol for Terbium = tb What is the element symbol for Thallium = tl What is the element symbol for Thorium = th What is the element symbol for Thulium = tm What is the element symbol for Tin (white) = sn What is the element symbol for Titanium = ti What is the element symbol for Tungsten = w What is the element symbol for Uranium = u What is the element symbol for Vanadium = v What is the element symbol for Xenon = xe What is the element symbol for Ytterbium = yb What is the element symbol for Yttrium = y What is the element symbol for Zinc = zn What is the element symbol for Zirconium = zr Category: Definitions: A harmful deviation from the normal structural or functional state of an organism = disease Category: Definitions: The training of the body to improve its function and enhance its fitness... = excercise Category: Definitions: In human beings, the extent of an individual's continuing physical, emotional, mental, and social ability to cope with his environment = health Category: Definitions: The physical substance of the human organism, composed of living cells and extracellular materials and organized into tissues, organs, and systems = human body Category: Definitions: The practice concerned with the maintenance of health and the prevention, alleviation, or cure of disease = medicine Category: Definitions: The process by which an organism absorbs and utilizes food substances = nutrition Category: Definitions: The process involved in knowing, or the act of knowing, which in its completeness includes perception and judgment = cognition Category: Definitions: Discipline that is concerned, in this context, mainly with methods of teaching and learning = education Category: Definitions: Traditionally, a collection of books used for reading or study, or the building or room in which such a collection is kept = library Category: Definitions: The study of the surface of the Earth = geography Category: Definitions: Graphic representation, drawn to scale and usually on a flat surface, of features--for example, geographical, geological, or geopolitical--of an area of the Earth or of any other celestial body = map Category: Definitions: The scientific study of the material remains of past human life and activities = archeology True or False: No matter where you are in Australia you are never more than 1000 kilometers from the ocean = true True or False: All owls lay white eggs = true True or False: It takes a week to make a jelly bean = true True or False: You can't tickle yourself = true True or False: The University of Texas system is the third-largest landowner in the United States = true Who was the LAST man to walk on the moon = gene cernan How many stars are there in the Paramount movie logo = twenty two = 22 True or False: You can't trademark surnames = true What are the names of the two stone lions in front of the New York Public Library = patience & fortitude What is Tennessee Williams' real name = thomas lanier williams What is the scent on the artificial rabbit that is used in greyhound races = anise Only one satellite has been ever been destroyed by a meteor -- the European Space Agency's ______ = olympus In what year was the European Space Agency's satellite destoyed by a meteor = 1993 What is the Western-most point in continental Europe = cabo de rocha, portugal What is the largest breed of domesticated cat in the world = ragdoll What is the oldest city in the US = st augustine, florida What is the second oldest city in the US = st. mary's, georgia what is the Latin name for moose = alces alces There are two years that will look the same when written right side up and upside down. The first is 1961. What is the other = 6009 What was the name of the plane to drop the SECOND atomic bomb on Japan in WW II = bock's car True or False: Kermit the Frog has 11 points on his collar around his neck = true True or False: Harvard uses "Yale" brand locks on their buildings = true True or False: Yale uses "Best" brand locks on their building = true True or False: There are more Samoans in Los Angeles than on American Samoa = true Whats is soccer legend Pele's real name = edson arantes do nascimento True or False: Both Fleet Commanders in the Pacific Theater of World War II Admiral Isoroku Yamamoto for the Japanese, and Admiral Chester Nimitz for theUnited States were each missing 3 fingers as a result of accidents while younger officers onboard ship = false, 2 On March 29, 1848, Niagara Falls stopped flowing for 30 hours. What caused this = ice jam blocking the niagara river Where is America's largest rosary is located = fatima shrine in holliston, massachusetts What was the first product to have a UPC bar code on its packaging = wrigley's gum What is the "you are here" arrow on maps called = ideo locator WHat does the "J.R.R." in J.R.R.Tolkien stand for = john ronald reuel What is the country with the biggest percentage of female heads of household = botswana In English, "four" is the only digit that has the same number of letters as its = value On the London Underground, one station has adifferent name on two of its platforms. They are = bank & monument A pregnant goldfish is called a = twit Duelling is legal in Paraguay as long as both parties are registered = blood donors The metal part of a lamp that surrounds the bulb and supports the shade is called a = harp The metal part at the end of a pencil is twenty percent = sulfur A pig's penis is shaped like a = corkscrew The most sensitive finger is the = forefinger Dublin comes from the Irish Dubh Linn which means = blackpool Scottish is the language called Gaelic, whereas Irish is actually called = gaeilge There is actually a word for a 64th note -- a = hemidemisemiquaver True or False: No words in the English language rhyme with orange, silver or purple = true When is it impossible to sneeze = with your eyes open Paul McCartney's mother was a = midwife A baby eel is called an = elver A baby oyster is called a = spat There are only how many letters in the Hawaiian alphabet = twelve = 12 Fossilized dinosaur droppings are called = coprolites Rabbits love this black stuff = licorice Ogdensburg, New York is the only city in the United States situated what river = st lawrence Gilligan of Gilligan's Island had a first name that was only used once, on the never-aired pilot show. His first name was = willy In England, the Speaker of the House is not allowed to = speak What color do turnips turn when sunburnt = green A flamingo can eat only when its head is = upside down An elephant can be pregnant for up to how many years = two = 2 Your left lung is smaller than your right lung to make room for your = heart Heroin is the brand name of morphine once marketed by = bayer One of the many Tarzans, Karmuela Searlel, was mauled to death on the set by a = elephant A game of pool is referred to as a = frame The Soviet Sukhoi-34 is the first strike fighter with a _____ in it = toilet During WWII, Americans tried to train bats to = drop bombs Swahili is a combination of African tribal languages = arabic & portuguese An enneahedron is solid with ho many faces = nine = 9 There are more _____ than any other kind of creature in the world = beetles Jean Harlow was the first actress to appear on the cover of ____ magazine = life Shrimps' hearts are in their = heads Charlie Brown's father was a = barber The most common name in the world is = mohammed Pickled herrings were invented in what year = 1375 Bank robber John Dillinger played professional = baseball The housefly hums in the middle octave, key of = f The only capital letter in the Roman alphabet with exactly one endpoint is = p The longest time someone has typed on a typewriter continuously is ____ hours, set by Violet Gibson Burns = 264 Hydroxydesoxycorticosterone and hydroxydeoxycorticosterones are the largest = anagrams Only one person in two billion will live to be ___ or older = 116 A cat has __ muscles in each ear = thirty two = 32 Tigers have striped ____, not just striped fur = skin Gerald Ford pardoned Robert E. Lee posthumously of all crimes of = treason The band Duran Duran got their name from an astronaut in the 1968 Jane Fonda movie = barbarella A Laforte fracture is a fracture of all _____ bones = facial There is a word in the English language with only one vowel, which occurs six times = indivisibility Cleo and Caesar were the early stage names of = sonny & cher Genghis Khan's original name was = temujin Genghis Khan started out as a = goatherd The two lines that connect your top lip to the bottom of your nose are known as the = philtrum The dirt road that General Washington and his soldiers took to fight off General Clinton during the Battle of Monmouth was called the = burlington path Lucifer is latin for = light bringer Satan means = adversary Devil means = liar The "wild" horses of western North America are actually _____, not wild = feral When two words are combined to form a single word (e.g., motor + hotel = motel, breakfast + lunch = brunch) the new word is called a = portmanteau Every photograph of an American atomic bomb detonation was taken by = harold edgerton The longest recorded flight of a chicken is = thirteen seconds = 13 seconds The muzzle of a lion is like a fingerprint -- no two lions have the same pattern of = whiskers Wilma Flinestone's maiden name was = wilma slaghoopal Betty Rubble's Maiden name was = betty jean mcbricker When ocean tides are at their highest, they are called = spring tides When ocean tides are at their lowest, they are call = neap tides Mice, whales, elephants, giraffes and man all have seven = neck vertebra Sunbeams that shine down through the clouds are called = crepuscular rays Very small clouds that look like they have been broken off of bigger clouds are called = scuds The poisonous copperhead smells like fresh cut = cucumbers In Disney's "Fantasia", the Sorcerer's name is Disney spelt backwards = yensid When angered, the ears of Tazmanian devils turn what color = pinkish red The roads on the island of Guam are made with = coral The sand on the beaches of Guam is actually = ground coral What country is the world leader in cobalt mining, producing two-thirds of the world's cobalt supply = zaire The only person ever to decline a Pulitzer Prize for Fiction was Sinclair Lewis for his book = arrowsmith A poem written to celebrate a wedding is called an = epithalamium These letters define what - AA = alcoholics anonymous These letters define what - AI = artificial intelligence These letters define what - AKA = also known as These letters define what - ASAP = as soon as possible These letters define what - ATM = automated teller machine These letters define what - AT&T = american telephone and telegraph These letters define what - AWOL = absent without leave These letters define what - BBC = british broadcasting corporation These letters define what - BLT = bacon, lettuce, tomato These letters define what - BYOB = bring your own beer These letters define what - CB = citizen's band These letters define what - CBS = columbia broadcasting system These letters define what - CPR = cardiopulmonary resuscitation These letters define what - DOA = dead on arrival These letters define what - ER = emergency room These letters define what - ETA = estimated time of arrival These letters define what - FAQ = frequently asked questions These letters define what - GPS = global positioning system These letters define what - HIV = human immunodefficiency virus These letters define what - IRA = irish republican army These letters define what - KIA = killed in action These letters define what - LCD = liquid crystal display These letters define what - LED = light emitting diode These letters define what - MASH = mobile army surgical hospital These letters define what - MIA = missing in action These letters define what - MIT = massachusetts institute of technology These letters define what - MO = modus operandi These letters define what - MP = military police These letters define what - NATO = north atlantic treaty organization These letters define what - NBA = national basketball association These letters define what - NBC = national broadcasting company These letters define what - NFL = national football league These letters define what - NHL = national hockey league These letters define what - NOW = national organization of women These letters define what - PBS = public broadcasting system These letters define what - PDQ = pretty damned quick These letters define what - POTUS = president of the united states These letters define what - POW = prisoner of war These letters define what - PSI = pounds per square inch These letters define what - PVC = polyvinyl chloride These letters define what - RCA = radio corporation of america These letters define what - RDA = recommended daily allowance These letters define what - RNA = ribonucleic acid These letters define what - ROM = read only memory These letters define what - ROTC = reserve officers training corps These letters define what - SPF = sun protection factor These letters define what - SRO = standing room only These letters define what - STD = sexually transmitted disease These letters define what - SWAK = sealed with a kiss These letters define what - SWAT = special weapons and tactics These letters define what - TGIF = thank god it's friday These letters define what - UHF = ultra high frequency These letters define what - UNESCO = united nations educational, scientific and cultural organization These letters define what - UNICEF = united nations international children's emergency fund These letters define what - URL = uniform resource locator These letters define what - VCR = video cassette recorder These letters define what - VHF = very high frequency These letters define what - VHS = video home system These letters define what - WAC = women's army corps These letters define what - WC = water closet These letters define what - WYSIWYG = what you see is what you get These letters define what - Y2K = year 2000 These letters define what - ZIP = zone improvement plan These letters define what - ASCII = american standard code for information exchange These letters define what - AMOCO = american oil company These letters define what - BB = ball bearing These letters define what - DEW = distant early warning These letters define what - ESPN = entertainment and sports programming network These letters define what - FORTRAN = formula translation These letters define what - GATT = general agreement on tariffs and trade These letters define what - GIGO = garbage in, garbage out These letters define what - HDL = high density lipoprotein These letters define what - LDL = low density lipoprotein These letters define what - HUMVEE = high mobility multi purpose wheeled vehicle These letters define what - ICBM = inter continental ballistics missile These letters define what - JPL = jet propulsion laboratory These letters define what - LASER = light amplification by stimulated emission of radiation These letters define what - MASER = microwave amplification by the stimulated emission of radiation These letters define what - MG = morris & garash These letters define what - NEC = nippon electronics corporation These letters define what - NSA = national security administration These letters define what - NORAD = north american air defense command These letters define what - OCR = optical character recognition These letters define what - OSHA = occupational safety and health administration These letters define what - Parsec = parallax second These letters define what - PPP = point to point protocol These letters define what - PT = patrol torpedo These letters define what - SAR = search and rescue These letters define what - SOP = standard operating procedure These letters define what - TIFF = tag image file format These letters define what - TTY = teletypewriter These letters define what - WAVES = women accepted for volunteer emergency service These letters define what - WNL = within normal limits These letters define what - CAT = computerized axial tomography These letters define what - COBOL = common business oriented language These letters define what - CONUS = continental united states These letters define what - GAF = general aniline and film These letters define what - HDPE = high density polyethylene These letters define what - JVC = japan victor company These letters define what - LDPE = low density polyethylene These letters define what - MCI = microwave communications of america These letters define what - M&M = mars & murray These letters define what - MRI = magnetic resonance imaging These letters define what - MIDI = musical instrument digital interface These letters define what - SCSI = small computer serial interface These letters define what - TDK = tokyo denki kogaku North American Indians ate Watercress to dissolve gravel and stones in the _______ = bladder In Russia, suppositories cut from fresh potatoes were used for quick relief of ______ = haemorrhoids A salt enema used to be given to children to rid them of _______ = threadworms Powdered Tea was once used as a snuff to stop bleeding _______ = noses A decoction of dandelion roots and leaves is an old remedy for dissolving urinary _______ = stones Comfrey (herb) baths were popular before the wedding night to attempt to repair the hymen and thereby apparently restore _______ = virginity The thyroid cartilage is more commonly known as the _____ = adam's apple Stroking the sole of the foot is used by doctor's to produce _______ = the babinski effect Insulin is produced in the _________ = pancreas Acute hasopharyngitis is more commonly known as a ______ = cold Keratitis is an inflammation of the cornea which may lead to _________ = blindness Oophorectomy is the surgical removal of the ______ = ovaries Sperm is the smallest single ____ in a mans body = cell Estragon protects against _____ disease = heart Hair, prompted by testosterone, grows faster in men in anticipation of _____ = sex An average, in America, three sex change operations are performed _______ = every day In 1977, Napoleon's _____ was sold in Paris for about US $3 800 to an American urologist = penis The most sensitive cluster of nerves is at the base of the ____ = spine In 1855, dentist Robert Arthur was the first to use ____ to fill cavities = gold The fleshy muscular organ joined to the hyoid bone is the ____ = tongue Quinine is an alkaloid extract of the bark of the _______ tree = cinchona By raising your legs slowly and laying on your back, you can't sink in ________ = quicksand An Eskimo would be ingesting toxic doses of Vitamin A if he ate a polar bears ______ = liver Smallpox is also known as _______ = variola The disease Tuberculosis, is best known as ________ = consumption Victorian women tried to enlarge their breasts by bathing in _______ = strawberries The fissure of Rolando, would be found in the ________ = human brain Iron deficiency causes the most common form of _________ = anaemia Red blood cells are produced in the ________ = bone marrow The smallest bone in the body is the ______ = stirrup The Mount of Jupiter and the Girdle of Venus are found on the _________ = palm of your hand The Auricularis muscles are used to move the____ = ears The vaccine for ________ was developed in 1798 = smallpox In the United States, 1982, the painkiller 'Tylenol' was spiked with = cyanide The normal body temperature in ___ degrees Celsius = thirty seven = 37 In 1982, Englishman William Hall committed suicide by drilling holes into his head with a power drill . . . it took ___ holes = eight = 8 The rate of Quadruplets are 1 (set) in every __________ births = 490,000 A person suffering from polythelia has 3 _______ = nipples Clinophobia is a fear of = beds The human brain is __% water = eighty = 80 The brain uses more than __% of the oxygen used by the human body = twenty five = 25 The nose continues to _____ throughout your life = grow Blonde beards grow ______ than darker beards = faster The left side of the brain is usually responsible for the control of = speech The space between two adjacent neurones is called the = synapse The crystalline quartz, Amethyst was once believed to prevent = drunkenness Sigmund Freud brought his first sample of _______ for $1.27 per gram = coccaine The septum linguae is found on the = tongue Stroking the sole of the foot produces the ______ reflex = babinski During a orchidectomy, a man has a _______ removed = testicle The medical term for a black eye is = circumorbital haematoma Medical experts say you should sleep on your right side to improve _____ = digestion The largest cell in the human body is the female reproductive cell = the ovum The smallest cell in the human body is the = male sperm There are over 100 million light sensitive cells in the = retina The opposite of 'cross-eyed' is = wall eyed From the age of thirty, humans gradually begin to ______ in size = shrink A tincture of opium, was a common sedative in Victorian times = laudanum In the early Twentieth century, rattlesnake venom was used to treat _____ = epilepsy The human body contains about sixthy thousand miles of _____ _______ = blood vessels The surface area of a human lung is equal to a = tennis court The human body transmits nerve impulses at about __ metres a second = ninety = 90 Spread out, the walls of the human intestines would cover an area of about = one hundred square feet The hydrochloric acid in the human stomach is strong enough to dissolve a = nail There are ______ phalanges (finger bones) in a human hand = fourteen = 14 In 1979 Dr. Christian Barnard was offered ________ by the American National Enquirer to perform a human head transplant = $250,000 Most people have lost fifty per cent of their taste buds by the time they reach the age of = sixty = 60 The amount of carbon in the human body is enough to fill about _____ 'lead' pencils = 9,000 Cancer claims ____ victims an hour in America = forty = 40 In the English hospitals of the seventeenth century, children were entitled to ___ gallons of beer as part of their weekly diet = two = 2 Podobromhidrosis is more commonly known as = smelly feet If a surgeon in Ancient Egypt lost a patient while performing an operation, his hands were = cut off Men have on average __% more red blood cells than women = ten = 10 New Zealand's first hospital was opened in = 1843 One square inch of human skin contains ___ sweat glands = 625 When you blush, your also ___________ reddens = stomach lining The largest muscle in the human body is the = buttock muscle The Islands of Langerhans won't be found on a map, they're a group of cells located in the = pancreas Every time you step forward, you use ________ muscles = fifty four = 54 A Rhinologist specialises in the human = nose If you could remove all the space from the atoms that make up your body, you could walk through the = eye of a needle A chromosome is large than a = gene The average human brain weighs ___ kg = 1.3 Eighty per cent of all body heat escapes through the = head The Black Death claimed roughly __________ lives in the thirteenth century = forty million = 40,000,000 The human wrist contains more bones than the = ankle Someone who grinds their teeth is a = bruxomaniac If the present birth rate continues, New York city will have a black majority by the = end of the century In 1562 a man was dug up six hours after his burial, after he had been seen breathing by someone at the funeral - he lived for another ___ years = seventy five = 75 Physcrophilia is the sexual arousal by = cold If __% of the human liver was removed, it could still function and would eventually restore itself to its original size = eighty = 80 There is more pigment in ______ eyes than blue = brown Nearly a quarter of all human bones can be found in the = feet The ' funny bone' is not a bone but a = nerve Most people blink about ______ times a day = 25,000 The human body has enough fat to produce ___ bars of soap = seven = 7 The human head is a quarter of our total length at birth, but only an eighth of our total length by the time we reach = adulthood There is no single word given to describe the back of the = knee From fertilisation to birth, a baby's weigh increase ________ times = five billion The woman of the Brazilin Apinaly Tribe bite their mates eyebrows during = intercourse Thomas Wedders, the English circus freak, had a _____ which was seven and a half inches long = nose The human body has less muscles in it than a = caterpillar Men are ten times more likely to be ________ than women = color blind An eighteenth century woman used only lard to 'wash' her face and hands and lived to the age of = one hundred & sixteen = 116 The liver is the largest internal organ weighing about ____ kilograms = 10.5 Human adults breathe about _____ time a day = 23,000 It requires __ muscles to raise your eyebrows = thirty = 30 The most common form of cancer is _____ cancer = skin The Middle ear and the Pharynx are joined with the ________ tube = eustachian The Extensor digiti minimi manus is used to extend the = little finger If you are a universal donor your blood group is = type o When recognising someone's face, you use the ___ side of your brain = right When Albert Einstein died, his final words died with him. The nurse at his side didn't understand = german St Patrick, the patron saint of Ireland, was not = irish The lance ceased to be an official battle weapon in the British Army in = 1927 St. John was the only one of the 12 _____ to die a natural death = apostles Many sailors used to wear ____ earrings so that they could afford a proper burial when they died = gold Some very Orthodox Jew refuse to speak Hebrew, believing it to be a language reserved only for the = prophets A South African monkey was once awarded a medal and promoted to the rank of _______ during World War I = corporal Because they had no proper rubbish disposal system, the streets of ancient ___________ became literally knee-deep in rubbish = mesopotamia The _______, Seventh-century native Mexicans, went into battle with wooden swords so as not to kill their enemies = toltecs China banned the pigtail in 1911 as it was seen as a symbol of = feudalism The Amayra guides of Bolivia are said to be able to keep pace with a trotting horse for a distance of = 100 kilometres Sliced bread was patented by Mrs P. Ride, a mother of 5, on 1 April = 1954 Ivan the terrible claimed to have 'deflowered thousands of virgins and butchered a similar number of resulting = offspring Before the Second World War, it was considered a sacrilege to even touch an = emperor of japan An American aircraft in Vietnam shot ______ down with one of its missiles = itself Ancient drinkers warded off the devil by _____ their cups = clinking The cost of the first pay-toilets installed in England was = tuppence Pogonophobia is the fear of = beards In 1647 the English Parliament abolished = christmas King George III was declared violently insane in ___, 9 years before he died = 1811 In Ancient Peru, when a woman found an 'ugly' _______, it was the custom for her to push it into the face of the nearest man = potato When George I became King of England in 1714, his wife did not become Queen. He placed her under house arrest for ____ years = thirty two = 32 The richest 10 per cent of the French people are approximately _____ times better off than the poorest 10 per cent = fifty Henry VII was the only British King to be crowned on the = field of battle During World War One, the future Pope John XXIII was a _______ in the Italian Army = sergeant Richard II died aged __ in 1400. A hole was left in the side of his tomb so people could touch his royal head, but 376 years later some took advantage of this and stole his ______ = 33 & jawbone The magic word "Abracadabra" was originally intended for the specific purpose of curing = hay fever The Puritans forbade the singing of ______ ______, judging them to be out of keeping with the true spirit of Christmas = christmas Carols Albert Einstein was once offered the Presidency of _____. He declined saying he had no head for problems = israel Uri Geller, the professional psychic was born on December 20 ____ = 1946 As to the origin of his alleged powers, Uri Geller maintains that they come from the distant planet of = hoova Ralph and Carolyn Cummins had ____ children between 1952 and 1966, all were born on the 20 February = five = 5 John D. Rockefeller gave away over US$ _________ during his lifetime = 500,000,000 In the 1970's, the Rhode Island Legislature in the US entertained a proposal that there be a $2 tax on every act of _____ ____ in the State = sexual intercourse Widows in equatorial ____ actually wear sackcloth and ashes when attending a funeral = africa The British did not release the body of Napoleon Bonaparte to the French until _____ days after his death = twenty = 20 John Glenn, the American who first orbited the Earth, was showered with ______ tonnes of ticker tape when he got back = 3,529 American Red Indians used to name their children after the first thing they saw as they left their _____ subsequent to the birth. Hence such strange names as Sitting Bull and Running Water = tepees Catherine the First of Russia, made a rule that no man was allowed to get drunk at one of her parties before ____ o'clock = nine = 9 Queen Elizabeth I passed a law which forced everyone except for the rich to wear a flat cap on = sundays In 1969 the shares of the Australian company 'Poseidon' were worth $1, one year later they were worth $___ each = 280 Julius Caesar wore a laurel wreath to cover the onset of = baldness Ernest Bevin, Minister of Labour during World War II, left school at the age of = eleven = 11 It is illegal to be a prostitute in Siena,Italy, if your name is = mary The Turk's consider it considered unlucky to step on a = piece of bread The authorities do not allow tourists to take pictures of ______ in Zambia = pygmies The Dutch in general prefer their french fries with = mayonnaise Sir Isaac Newton was obsessed with the = occult and supernatural One of Queen Victoria's wedding gifts was a 3 metre diameter, half tonne = cheese Alexander Graham Bell, the inventor of the telephone, never phones his wife or his mother, they were both = deaf It was considered unfashionable for Venetian women, during the Renaissance to have anything but _____ _____ hair = silvery blonde Queen Victoria was one of the first women ever to use ________ to combat pain during childbirth = chloroform Atilla the Hun is thought to have been a = dwarf The warriors tribes of ________ used to hang the testicles of those they killed in battle on the ends of their spears = ethiopia There are over _____ religious denominations in the United States = two hundred = 200 Eau de Cologne was originally marketed as a way of protecting yourself against the = plague Charles the Simple was the grandson of Charles the Bald, both were rulers of = france Gabriel and Michael are the only 2 angels to be named in the = bible The women of an African tribe make themselves more attractive by permanently ______ their faces = scaring Some moral purists in the Middle Ages believed that women's ____ ought to be covered up because the Virgin Mary had conceived a child through them = ears Hindus don't like dying in bed, they prefer to die beside a = river While at Havard University, Edward Kennedy was suspended for cheating on a _____ exam = spanish It is a criminal offence to drive around in a dirty car in = russia The mad Emperor Caligula once decided to go to war with the Roman God of the sea, Poseidon, and ordered his soldiers to throw their spears into the _____ at random = water The Ecuadorian poet, José Olmedo, has a statue in his honour in his home country. But, unable to commission a sculptor, due to limited funds, the government brought a second-hand statue .. Of the English poet = lord byron In ______, at only 7 years old, Charles Sauson inherited the post of official executioner = 1726 In parts of _____, the women keep harems of men = malaya The childrens' nursery rhyme 'Ring-a-Ring-a-Roses' actually refers to the Black Death which killed about ______ people in the fourteenth-century = 30,000,000 During the reign of Elizabeth I, there was a tax put on men's = beards Idi Amin, one of the most ruthless tyrants in the world, before coming to power, served in the = british army Some Eskimos have been known to use _________ to keep their food from freezing = refrigerators It is illegal to play tennis in the streets of = cambridge Custer was the youngest General in US history, he was promoted at the age of = twenty three = 23 It costs more to send someone to reform school than it does to send them to = eton The American pilot Charles Lindbergh received the Service Cross of the German Eagle from _____ _____ in 1938 = hermann goering The active ingredient in Chinese Bird's nest soup is = saliva It was quite common for the men of Ancient Greece to exercise in public ...... = naked John Paul Getty, once the richest man in the world, had a ______ in his mansion = payphone Iceland is the world's oldest functioning = democracy The national flag of _____ was designed by Napoleon Bonaparte = italy The Matami Tribe of West Africa play a version of football, the only difference being that they use a ____ ____ instead of a more normal ball = human skull John Winthrop introduced the fork to the American dinner table for the first time on 25 June = 1630 Abraham Lincoln was shot with a = derringer When shipped to the US, the London bridge ( thought by the new owner to be the more famous Tower Bridge ) was classified by US customs to be a 'large = antique Sir Winston Churchill was born in a ladies' cloakroom after his mother went into labour during a dance at = blenheim palace In _____, David Atchison became President of the United States for just one day, and he spent most of the day sleeping = 1849 Between the two World War's, France was controlled by ___ different governments = forty The 'Crystal Palace' at the Great Exhibition of 1851, contained ______ square metres of glass = 92,900 It was the custom in Ancient Rome for the men to place their right hand on their testicles when taking an oath. The modern term _______ is derived from this tradition = testimony Sir Winston Churchill's mother was descended from a = red indian The study of stupidity is called = monology More money is spent each year on alcohol and cigarettes than on = life insurance In ____ - 3 men were hung for the murder of Sir Edmund Berry at Greenbury Hill, their last names were Green, Berry , and Hill = 1911 A firm in Britain sold fall-out shelters for = pets During the seventeen century , the Sultan of Turkey ordered his entire harem of women _____, and replace with a new one = drowned There are no clocks in Las Vegas _____ = casinos The Great Pyramid of Giza consists of _________ blocks each weighing 2.5 tons = 2,300,000 On 9 February ____, soap rationing began in Britain = 1942 Paul Revere was a = dentist A parthenophobic has a fear of = virgins South American gauchos were known to put raw steak under their saddles before starting a day's riding, in order to ______ the meat = tenderise There are _______ white dots in a Pacman arcade game = two hundred & forty = 240 In 1939 the US political party 'The American Nazi Party' had _________ members = 200,000 King Solomon of Israel had about ______ wives as well as hundreds of mistresses = seven hundred = 700 Urine was once used to = wash clothes North American Indian, Sitting Bull, died on 15 December = 1890 St Nicholas, the original Father Christmas, is the patron saint of thieves, virgins and communist = russia ______ is home of the Fairy Investigation Society = dublin People in Siberia often buy ____ frozen on a stick = milk Princess Ann was the only competitor at the 1976 Montreal Olympics that did not have to undergo a ____ test = sex Coffins which are due for cremation are usually made with ______ handles = plastic Blackbird, who was the chief of _______ Indians, was buried sitting on his favourite horse = omaha The two highest IQ's ever recorded (on a standard test) both belong to = women The Imperial Throne of Japan has been occupied by the same family for the last ______ _________ years = thirteen hundred = 1300 In the seventeenth-century a Boston man was sentenced to two hours in the stocks for obscene behaviour, his crime, kissing his wife in a public place on a = sunday President Kaunda of ______ once threatened to resign if his fellow countrymen didn't stop drinking so much alcohol = zambia Due to staggering inflation in the 1920's, 4,000,000,000,000,000,000 ____ _______ were worth 1 US dollar = german marks The city of ____ ____ contains a district called 'Hell's Kitchen' = new york The city of ______ left the Industrial Promotion Centre standing as a monument the atomic bombing = hiroshima During the Medieval Crusades, transporting bodies off the battlefield for burial was a major problem, this was solved by carrying a huge cauldron into the Holy wars, boiling down the bodies, and taking only the _____ with them = bones A Cicada passes eggs through its = ovipositor A pigs snout is called a = gruntle When they mate, a yak and a cow produce a = dzo A cat's whiskers are called = vibrissae Goats produce = cashmere The common term for a member of the bee genus 'Bombus' is the = bumblebee A turkey's furcula better known as a = wishbone A 'geep' is the resulting offspring of a sheep and a = goat The typical housefly cruses at ___ km/hr = eight = 8 The chemical ______, found in ripe fruit, causes jam to set when cooling = pectin In 1876, Sir Henery Wickham transported _______ rubber tree seeds from Brazil to Kew Gardens in London = 70,000 The compound carotene gives the _______ its colour = carrot Bananas grow pointing = upwards Kiwi eggs, when not completely white, are tinged with = green The cucumber is a _____, not a vegetable = fruit The flying fish has been known to glide up to _____ metres = ninety = 90 A starfish can turn its ______ inside out = stomach Sea otters have ______ coats of fur = two = 2 Hummingbirds can't = walk A moth has no = stomach Lobsters have blue = blood Despite the hump, a camels spine is = straight 1 kg of lemons contain more sugar than 1 kg of = strawberries The largest eggs in the world are laid by a = shark Oranges cease to _____ after picking = ripen More people are killed each year from _____ than snakes = bees The Siberian ____ accounts for more than 20% of all the worlds trees = larch Midges beat their wings about ______ time every second = 1,000 A ____ can last longer without water than a camel can = rat A mole can dig over ____ feet of tunnel in a single night = 250 The animal with the largest brain in relation to its body is the = ant Peanuts are used in the production of = dynamite What nuts are used in the production of dynamite = peanuts A crocodiles ______ is attached to the roof of its mouth = tongue Sharks are immune to = cancer The blood of a grasshopper is = white Golden ____ are so rare that a biological reserve has been specifically created for them = toads The walking _______ can live on land = catfish 96% of a _____ is water = cucumber The _____ got its name from the corruption of the words ' Day's eye' = daisy Crocodiles swallow ______ to help them dive deeper = stones When a rabbit scratches its dowsets, it has itchy = testicles Mexican jumping beans jump because of a ____ ______ inside the bean = moth larva A 6 pound sea-hare can lay ______ eggs in a single minute = 40,000 In 1978 the United Nations Food and Agricultural Organisation estimated that 'all the tea in China' amounted to approx. _______ tonnes = 356,000 The ________ is the only animal whose evidence is admissible in an American court = bloodhound Only the _____ nightingale sings = male A chameleon's _____ is twice the length of its body = tongue The black widow spider can devour as many as _______ 'mates' in a single day = twenty = 20 A woodchuck breathes only ___ times during hibernation = ten = 10 Goldfish can suffer _____ sickness = motion When young, black sea basses are mostly female, but at the age of _____ years many switch sexes to male = five = 5 Gorillas often sleep for up to _______ hours a day = fourteen = 14 Giraffes are unable to = cough A 2 year old horse has ___incisors = six = 6 The last of a cat's senses to develop is = sight The Ancient Egyptians trained ______ to wait at their tables = baboons In relation to its size, the ordinary house spider is ______ times faster than an Olympic sprinter = eight = 8 A millipede has ____ legs on each segment of its body = four = 4 Sloths spend ____% of their lives asleep = seventy five = 75 During its lifetime an ______ changes its sex from male to female and back several times = oyster The ____ was wrongly named after what was thought to be its country of origin = turkey About 70% of all living organisms in the world are = bacteria Camels are born without = humps Many male fish blow bubbles when they want to = copulate Crocodiles are ______ blind = colour Snails mate only once in their lifetime, but it can take up to ____ hours = twelve = 12 Dogs are ________ blind = colour Anteaters prefer _______ to ants = termites Baby eels are = elvers Whales increase in weight 30,000,000,000 times in their first ___ years = two = 2 There are more ____ in the world than all other animals together = insects On average, elephants sleep for about ________ per day = two hours = 2 hours A giraffe's neck contains the same number of ________ as a human = vertebrae An electric eel produces an average of ____________ volts = four hundred = 400 Spiders' webs are a traditional natural ______ _______, when applied to a cut they quickly stop the flow of blood = clotting agent The orang-utan's warning signal to would be aggressors is a loud = belch The Emu gets its name from the ______ word for ostrich = portuguese Squid can commit suicide by eating their own = tentacles A cheetah can accelerate from 0 to _____ in 2 seconds = seventy km/h = 70 km/h Although Cleopatra was meant to have died after a bite from an ____, the species does not exist in Egypt = asp The 'Alligator Pear' is better known as the = avocado Snails can sleep for _____ without eating = three years = 3 years An Elephant's trunk can hold over _____ litres of water = five = 5 The largest shark ever caught with a rod was just less than ______ feet long = seventeen = 17 Bees have ___ eyes = five = 5 The mullet fish only turns _____ after death = red The Goliath frog of West Africa is nearly _____ feet long = three = 3 To make half a kilo of honey, bees must collect nectar from over ___ _______ individual flowers = two million = 2,000,000 Vampire Bats can hear sound frequencies which are over _____ times higher than any picked up by the human ear = eight = 8 The male Californian sea-otter grips the nose of the female with his teeth during = mating The heart of giraffe is two feet long, and can weigh as much as ____ ____ pounds = twenty four = 24 The smallest trees in the world are Greenland = dwarf willows A myrmecologist studies = ants The original source of the dye 'sepia' was the = cuttlefish Dogs ______ through the pads of their feet = sweat Only male canaries can = sing The Kiwi is the only bird with _____ at the end of its bill = nostrils Shark's teeth are literally as hard as = steel Turkey's often look up at the sky during a = rainstorm The only wild camels in the world are found in = australia At full tilt, Pumas can leap a distance of about ____ feet = sixty = 60 Crocodiles carry their ____ in their mouths = young Tarantula's can survive ______ years without food = two & half = 2.5 It has been estimated that there are something like ____ _____ million cats in the U.S = twenty three = 23 A sloth can move ______ as fast in water as it can on land = twice India has a Bill of Rights for = cows What can run faster uphill than they can (or want to) downhill, because of the bone structure of their legs = bulls 99% of all life forms to exist on Earth are now = extinct The sperm of a _____ is longer than the sperm of an elephant = mouse A dog has = elbows Whales can't swim = backwards Penguins can jump over ____ feet = six = 6 Lions have been exported to _____ by Windsor Safari Park in England = africa An Elephant has the world's largest penis, weighing about _____ kg = twenty seven = 27 The aquatic animal, the Red Sponge, can be broken into a thousand pieces and still _______ itself = reconstitute The Moa bird of New Zealand, which became extinct four hundred years ago, was over ____ feet tall = ten = 10 Australian termites have been known to build mounds ______ feet high = twenty = 20 Australian termites have been known to build mounds at least ____ feet wide = one hundred = 100 The ________ is born under water = hippopotamus Catgut comes from _____ not cats = sheep Male cats have _____ on their genitals, which cause the females to cry out in pain on withdrawal = barbs The average porcupine has about _______ quills = 30,000 Cats have no facility for tasting = sugar Only ___ mosquitoes bite = female It takes _______ to hardboil an ostrich egg = four hours = 4 hours The nerve fibres in the squid are ____ times thicker than a humans' = five hundred = 500 A shark must keep ___ ____ to stay alive = moving forward Fish are the only vertebrates that outnumber = birds Some snakes can take as long as ______ _______ hours to copulate = twenty four = 24 Polar bears can run as fast as _______ km/h = forty = 40 A ______ is capable of devouring a pig whole = python The ordinary house-fly beats its wings nearly ____ _______ times a second = two hundred = 200 Rhinoceros horn, when powered, is believed in some countries to increase a persons = sexual potency A mosquito has _____ teeth = forty seven = 47 An anencephalous creature has no = brain A large whale needs more than _____ tonnes of food a day = two = 2 Pedals were added to the bicycle in = 1839 The early personal computer, the Sinclair ZX80, had _________ of internal memory = 1 kilobyte Allied bombers were issued with Biro pens as pens leaked at high altitude = fountain The Dotmatrix printer was developed for the 1964 Tokyo Olympic Games by the Japanese company = seiko The worlds longest escalator is in Leningrad Metro, ______ metres long = one hundred & twenty = 120 The fluorescent tube uses ___% of the power to produce an equal amount of light as a tungsten filament bulb = twenty = 20 The tip of a rotary mower travels at over _____ km/hr = two hundred = 200 In an atom, the electron weighs _________ th of the weight of the proton = 1/2000 The world's oldest man-made alloy is = bronze Rainfall is measured with a = ombrometer The 'screwdriver' was invented by _______, who used the tool to stir the drink = oilmen Polytetrafluoroethylene is more commonly known as = teflon Ammonia is the active ingredient in = smelling salts When hydrogen burns in the air, _______ is formed = water Near-sighted model, Grace Robin was the first to show off contact lenses in = 1930 The diameter of wool is measured in = microns Gunpowder is formed after mixing charcoal, ______ and sulphur = saltpetre Gunpowder is formed after mixing charcoal, saltpetre and _______ = sulphur Plutonium was developed by deuteron bombardment of uranium-238 in a = cyclotron Oxygen, carbon, hydrogen and nitrogen make up ______% of the human body = ninety = 90 The revolving door was invented in = 1888 Cars were first started with ignition keys in = 1949 The three primary colours are red, yellow and blue. The three secondary colours are = green, orange & purple The first washing machine was marketed by Hurley Machine Co in = 1907 Pearls melt in = vinegar Twenty two carat gold has _____ parts per thousand pure gold = 916 A shadow of a four-dimensional object would have _____ dimensions = three = 3 What color light has the greatest wavelength = red Brimstone, referred to in the Bible and some Alchemy text, is = sulphur Nicotinic acid is listed on breakfast cereal boxes as = niacin The hottest flame known is produced with carbon subnitride (C4N2), which can generate a temperature of ____°C = 4988 Ninety eight per cent of the weight of water is made up from = oxygen For every extra kilogram carried on a space flight, _____ kg of excess fuel are needed at lift-off = 530 Sugar was first added to chewing gum in ____ . . . by a dentist ( William Semple) = 1869 A full loaded supertanker travelling at normal speed takes a least ______ minutes to stop = twenty = 20 Paper money was first used in = china Some soft drinks are made sweeter by adding = coal Albert Einstein was thought to be suffering from dyslexia, as he couldn't speak properly until he was _____ years old = nine = 9 The Chinese used fingerprints as a method of identification as far back as AD = seven hundred = 700 Sir Isaac Newton was only _______ ______ years old when he discovered the law of universal gravitation = twenty three = 23 A flush toilet exists that dates back to = 2000 bc No matter its size or thickness, no piece of paper can be folded in half more than ______ times = seven = 7 Just after the first moon walk, Pan-American Airlines announced that they were willing to take enquires about future commercial flights to the moon. They received ________ requests almost immediately = 80,000 When cutting a diamond with a laser, _______ dust is formed = graphite The metal ______ will melt in the heat of your hands = gallium A Boeing 707 uses _____ _____ gallons of petrol in its take-off climb = four thousand = 4000 The study of soil is = paedology The Lie Detector was invented by John Augustus Larson in = 1921 Another term for pure china clay is = kaolin Soap was originally made by boiling fat and adding ____ to it = lye The active ingredient in smelling salts is = ammonia Light is ______ ______ radiation = electro magnetic Minus ______ degrees Celsius is exactly the same as minus _____ degrees Fahrenheit = forty = 40 The screwdriver was invented before the = screw The first taxi with metered fares were operational in = 1907 Rubber is an impertinent ingredient in the manufacture of = bubble gum The zeppelin 'The Hindenburg' which exploded in a huge fireball, was filled with = hydrogen Speleology is the study of = caves The wristwatch was invented by Louis Cartier in = 1904 The stratosphere is higher than the = troposphere It snowed in the Sahara desert on 18 February = 1979 On average, an iceberg weighs __________ tons = twenty million = 20,000,000 The far side of the moon was first photographed by a Russian satellite in = 1959 Captain Cook was the first man to set foot on all continents except = antarctica The diameter of the Moon is ______ km = 3,476 200 million years ago Earth contained 1 land mass called = pangaea The largest wave ever recorded was near the Japanese Island of Ishigaki in 1971. It was recorded at ______metres high = eighty five = 85 The largest iceberg recorded ( in 1956 ) was 200 miles long and 60 miles wide, larger that the country of = belgium The surface of the Dead Sea is _______ metres below the surface of the Mediterranean Sea, which is only 75 km away = four hundred = 400 The country of Benin changed its name from Dahomey in = 1975 The Nova Zemlya Glacier in the (former) USSR is over _____ km long = four hundred = 400 The coldest temperature ever recorded was ____ deg Celsius, in Siberia = -70 The US state of Florida first saw the cultivation of oranges in = 1539 The world's largest exporter of sugar is = cuba There are no rivers in = saudi arabia England's Stonehenge is ______ years older than Rome's Colosseum = fifteen hundred = 1500 In 1896, Britain and Zanzibar were at war for _____ minutes = thirty eight = 38 Numbering houses in London streets only began in = 1764 More than 75% of all the countries in the world are _____ of the equator = north Less than 1% of the ________ Islands are inhabited = caribbean On average, an iceberg weighs __________ tons = twenty million = 20,000,000 Fulgurite is formed when lightning strikes = sand Mountains are formed by a process called = orogeny Obsidian, used by American Indians for tools, weapons and ornaments, is dark _______ glass = volcanic It takes 8.5 minutes for light to get from the ____ to earth = volcanic 82% of the workers on the Panama Canal suffered from = malaria The tree dictated on the Lebanese flag is a = cedar The _____ Ocean covers the world's longest mountain range = atlantic In 1825 Upper Peru became = bolivia New York City contains ________ kilometres of shoreline = nine hundred & twenty = 920 There are 3 Great pyramids at = giza The world's largest delta was created by the river = ganges 60% of all US potato products originate in = idaho The northern most country claiming part of Antarctica is = norway The inhabitants of ________ are known as 'Monegasques' = monaco New York's Central Park opened in = 1876 The city of Istanbul straddles two separate continents, ______ & _______ = europe & asia At the nearest point , Russia and America are less than ____ km apart = four = 4 99% of the solar systems mass is concentrated in the = sun The furthest point from any ocean would be in = china The Tibetan Mountain people use yak's milk as their form of = currency The Spanish Inquisition once condemned the entire _______ to death for heresy = netherlands Earth is the only planet not named after a = god The Angel falls in Venezuela are nearly _________ times taller than Niagara falls = twenty = 20 The Channel between England and France grows about ______ millimetres each year = three hundred = 300 Until the 18th century ____ produced almost all the worlds diamonds = india As the Pacific plate moves under its coast, the ______ Island of New Zealand is getting larger = north If you travel from east to west across the Soviet Union, you will cross _____ time zones = seven = 7 Sahara mean _______ in Arabic = desert Every litre of water taken from the Red Sea would contain about _____ grams of salt = two hundred = 200 Lighting strikes the Earth about ______ times a second = two hundred = 200 Very hard rain would pour down at the rate of about _____ miles per hour = twenty = 20 No rain has ever been recorded falling in the Atacama desert in = chile The background radiation in ________ is twice that of the rest of Great Britain = aberdeen The southern most tip of Africa is not the 'Cape of Good Hope' but = cape agulhas Due to gravitational effects, you weigh slightly less when the moon is directly = overhead The tower of London, during its lifetime has served many purposes, including a = zoo There is a village near the Somme in ______ which is simply called Y = france In the north of _______, the sun shines constantly for about 14 weeks each summer = norway The Polynesian country of _______ is a 170 square kilometre limestone rock emerging 60 metres from the Pacific = niue Yugoslavia is bordered by _____ other countries = seven = 7 Within a few years of Columbus' discovery of America, the ______ had killed one and a half million Indians = spaniards Hawaii officially became apart of the US on June 14, = 1900 If the population of China walked past you in single file, the line would never end because of the rate of = reproduction The earth's average velocity orbiting the sun is _________ km per hour = 107,220 The sun has a core temperature of ________ Kelvin = 154,000,000 A day on Jupiter is about ____ hours, 50 minutes, 30 seconds at the equator = nine = 9 Carolyn Shoemaker has discovered 32 comets and approximately ______ asteroids = eight hundred = 800 The Earth is pear shaped, the North Pole radius being ____ mm longer than the South Pole radius = forty four = 44 'Taumatawhakatangihangakoauauotamateaturipukakapikimaungahoronukupokaiwhenuakitanatahu' is the name of a hill in = new Zealand The book by Copernicus, which suggested that the Sun and not the Earth was the centre of the solar system, was banned officially banned by the Papacy until = 1835 A quarter of Russia is covered by = forest South Africa produces two-thirds of the world's = gold What is the densest planet in the solar system = earth There is no point in England more than _____ miles from the ocean = seventy five = 75 The ____ sea is not mentioned in the Bible = red England is smaller than = new england The number of UFO sightings constantly increase when _____ is nearest the Earth = mars The first city in the world to have a population of over one million was = london New York was once = new amsterdam The fastest tectonic movement on earth is 240 mm per year, at the Tonga micro-plate near = samoa In 1980 Rhodesia became independent = zimbabwe The model ape used in the 1933 movie ' King Kong ' was ______ inches tall = eighteen = 18 There are over ______ verses in the Bible = 30,000 The tablecloth in da Vinci's Last supper was = white World heavyweight boxing champion, Gene Tunney also lectured on Shakespeare at _____ University later in his life = yale William Shakespeare's father's first name was = john Elzie Crisler Segar created the comic strip character Popeye in = 1919 The 'Thing', from the movie of the same name, had ___ blood = green Charles Dickens was an insomniac, who believed his best chance of sleeping was in the centre of a bed facing directly = north The writer, Rudyard Kipling, only ever used _____ ink = black John Lennon's middle name was = winston Over ________ different languages are spoken throughout what used to be known as the Soviet Union = two hundred = 200 The 3 largest newspaper circulations are = russian There are ______ semihemidemisemiquavers in a breve = 256 The subject of the first printed book in England was = chess Composer Ludwig van Beethoven was once arrested for = vagrancy Monaco's national _______ is bigger than its army = orchestra Of the two chipmunks, Chip and Dale, ____ has the black nose = chip Charlie Chaplain's cane was made of = bamboo Colophony is a = bow resin A Penny whistle has ____ finger holes = six = 6 Turkey has a ban on _______ in films = kissing EMI stands for = electrical and musical instruments Leonardo da Vinci played the = viola Some hotels in Las Vegas have _________ floating in their swimming pools = gambling tables A hydrodaktulopsychicharmonica is a variety of = musical glasses The first recorded reference to cricket dates back to = 1272 The highjump method of jumping head first and landing on their back is called the = fosbury flop Orienteering originated in = sweden Snooker originated in = india A soccer ball has _____ panels = thirty two = 32 To a yachtsman, a fresh breeze is about _____ knots = twenty = 20 The collecting of Beer mats is called = tegestology Grabatology is the collecting of = ties The Australian term for extras in cricket are = sundries When kicked in the groin, a soccer player has been = banjoed What is the middle name of Cassius Clay = marcellus Pistols were first used in the Olympic games shooting events in = 1984 Cystallite is the material _________ are made from = snooker balls How many million kilometres from the Sun is Saturn = 1427 Saturn is __________ kilometres in diameter = 119,000 Saturn is __________ miles in diameter = 74,000 In what year was Pluto discovered = 1930 Pluto is __________ kilometres in diameter = 6,000 Pluto is __________ miles in diameter = 3,728 Mercury is __________ kilometres in diameter = 4,880 Mercury is __________ miles in diameter = 3,032 Venus is __________ kilometres in diameter = 12,000 Venus is __________ miles in diameter = 7,456 Mars is __________ kilometres in diameter = 6,790 Mars is __________ miles in diameter = 4,219 One day on Mercury lasts _____ Earth days = fifty eight = 58 Venus is ____ million kilometres from the Sun = one hundred & eight = 108 Mars is ____ million kilometres from the Sun = two hundred & twenty eight = 228 Jupiter is _________ kilometres in diameter = 143,000 Jupiter is _________ miles in diameter = 89,000 Jupiter is ____ million kilometres from the Sun = seven hundred & seventy eight = 778 Neptune is _________ million kilometres from the Sun = 4,500 Uranus is ______ kilometres in diameter = 52,000 Uranus is ______ miles in diameter = 32,000 Uranus is ______ million kilometres from the Sun = 2,870 Earth is ______ kilometres in diameter = 3,476 Earth is ______ miles in diameter = 2,160 Earth is ______ million kilometres from the Sun = one hundred & fifty = 150 One "day" on Mercury lasts more than ___ Earth days = fifty eight = 58 Venus takes nearly ___ Earth days to circle the Sun = two hundred & twenty five = 225 Mars is sometimes called the ___ planet = red Mars takes ______ Earth days to circle the sun = six hundred & eighty seven = 687 Saturn takes just over ____ Earth years to circle the Sun = twenty nine = 29 When was the last Supernova in our galaxy = 1604 Gas from a Supernova can travel up to _________ kilometres per second = 10,000 Gas from a Supernova can travel up to _________ miles per second = 6,000 One light year is equivalent to how many million kilometres = 9,460,000 One light year is equivalent to how many million miles = 5,912,500 When was the first space shuttle launched = april, 1981 How many signs in the Zodiac = twelve = 12 What is the Zodiac sign of the scorpion = scorpio What is the Zodiac sign of the scales = libra What is the Zodiac sign of the virgin = virgo What is the Zodiac sign of the lion = leo What is the Zodiac sign of the crab = cancer What is the Zodiac sign of the twins = gemini What is the Zodiac sign of the bull = taurus What is the Zodiac sign of the ram = aries What is the Zodiac sign of the water carrier = aquarius What is the Zodiac sign of the archer = sagittarius What is the Zodiac sign of the goat = capricorn What is the Zodiac sign of the fish = pisces What is the largest of the dinosaur family = brachiosaurus What does the name "Brachiosaurus" mean = arm lizard Approximately how tall was the Brachiosaurus, in metres = twelve = 12 Approximately how long was the Brachiosaurus, in metres = twenty four = 24 Approximately how long was the Diplodocus, in metres = twenty seven = 27 While the earth's atmosphere is divided into 4 main zones, which is the highest zone = exosphere The main elements of the Exosphere are hydrogen and ______ = helium The Exosphere merges with the _______ = ionosphere The four main elements of eath's atmosphere are exosphere, ionosphere, stratosphere and __________ = troposphere What is the largest ocean on earth = pacific What type of rock is sandstone = sedimentary Sedimentary rocks are usually formed in _______ = layers Sedimentary rocks often contain ______ = fossils Where are metamorphic rocks usually found = mountain ranges An example of a metamorphic rock is ____ = slate = marble There are three types of rock - metamorphic, sedimentary and ______ = igneous An example of an igneous rock is____ = granite = quartz What is mercury also known as = quicksilver What is the hardest mineral = diamond What is the second hardest mineral = corundum What is one of the softest minerals = aluminite = talc Most precious stones are minerals, though some come from animals or plants, one of these is = pearl = amber = coral Gemstones are formed from slowly cooling __________ rocks = igneous Where are icebergs usually larger = antarctica What is the monetary unit of Algeria = dinar What is the monetary unit of Angola = kwanza What is the monetary unit of Botswana = pula What is the monetary unit of Cameroon = franc What is the monetary unit of Chad = franc What is the monetary unit of Egypt = egyptian pound What is the monetary unit of Ethiopia = birr What is the monetary unit of Gambia = dalasi What is the monetary unit of Ghana = cedi What is the monetary unit of Kenya = shilling In ancient Rome, auburn-haired puppies were sacrificed to ensure a plentiful ______ crop = corn Ostriches live about ____ years and can reproduce for 50 years = seventy five = 75 In England, the most commonly used guide dog for the blind is the__________ = yellow labrador retriever Contrary to popular belief, dogs do not sweat by salivating. They sweat through the____________ = pads of their feet In Korea, the _____ is a symbol of long life, and is often portrayed in the company of immortals = deer Cougars can kill animals ______ times their size = eight = 8 The venom of the king cobra is so deadly that one gram of it can kill ______ people = one hundred and fifty = 150 The Virginia opossum is the only marsupial (pouched mammal) indigenous to __________ = north america The Virginia opossum have a litter size of up to ____ = twenty two = 22 The opossum offspring say in their mothers pouch for the first __________ days = sixty = 60 The largest Great White Shark ever caught measured _______ feet = thirty seven = 37 The largest Great White Shark ever caught weighed ________ pounds = 24,000 The largest jellyfish in the world has a bell that can reach _______ feet across = eight = 8 The burrowing boodie of Australia is the only kangaroo in the world that lives _________ = underground The whistling swan has more than __________ feathers on its body = 25,000 Pandas spend about _____ hours a day eating bamboo = twelve = 12 The white elephant is the sacred animal of _______ = thailand The largest order of mammals, with about _____ species, is rodents = 1,700 Bats are second largest order of mammals, with about ______ species = nine hundred and fifty = 950 In one year, hens in America lay enough eggs to encircle the globe _____ times = one hundred = 100 The woolly mammoth, extinct since the Ice Age, had tusks almost _____ feet high = sixteen = 16 The largest species of seahorse measures _____ inches = eight = 8 In Pakistan, goats are often sacrificed to improve the performance of the________ = stock market The word "puppy" comes from the French poupee, meaning = doll The leech has 32 _______ = brains In Russia, dogs have been trained to sniff out ore deposits that contain _________ = iron sulfides The word "struthious" refers to something that resembles or is related to ______ = ostriches The leech will gorge itself up to _____ times its body weight and then just fall off its victim = five = 5 The chameleon has a tongue that is _______ times the length of its body = 1.5 In the air, puffins are powerful flyers, beating their wings 300 to 400 times a minute to achieve speeds up to _____ miles per hour = forty = 40 The world's fastest reptile (measured on land) is the spiny-tailed iguana of Costa Rica. It has been clocked at nearly _____ miles per hour = twenty two = 22 The life expectancy of the average mockingbird is ____ years = 10 Pigs, walruses, and light-colored horses can be = sunburned The lungfish can live out of water in a state of suspended animation for _____ years = three = 3 The chicken can travel up to _____ miles per hour = nine = 9 Pink elephants? In regions of India where the soil is red, elephants take on a permanent pink tinge because they regularly spray dust over their bodies to protect themselves against ________ = insects It is estimated that a single toad may catch and eat as many as ________ insects in the course of a summer = 10,000 Prior to migration a goose will consume the equivalent of up to ____ percent of its body weight per day, accumulating large amounts of fat = twenty five = 25during the migration, geese may cover up to ____ miles per day = six hundred = 600 It is estimated that manatees live a maximum of ___ years = sixty = 60 A "winkle" is an edible = sea snail A 42-foot sperm whale has about _____ tons of oil in it = seven = 7 A 4-inch-long abalone can grip a rock with a force of ______ pounds = four hundred = 400 A baby giraffe is about _____ feet tall at birth = 6 A baby gray whale drinks enough milk to fill more than ______ bottles a day = 2,000 A bear in hibernation loses up to _____ percent of its body weight = twenty five = 25 The world's largest mammal, the blue whale, weighs _____ tons at birth = fifty = 50 Fully grown, a blue whale can weigh as much as ______ tons = one hundred and fifty = 150 There are ________ known species of starfishes in the world = 1,600 The color of the blood of an octopus is = bluish green A bison can jump ___ feet = 6 There are _____ species of parrotsthree hundred and twenty eight = 328 Racehorses have been known to wear out new ______ in one race = shoes There are ________ muscles and tendons in an elephant's trunk = 40,000 Rats can swim for a 1/2 mile without resting, and they can tread water for _____ days straight = three = 3 There are ______ known species of bats = six hundred and ninety = 690 The massive skeleton of the African elephant accounts for about _______ percent of the body weight = fifteen = 15 The crayfish isn't a fish at all - it is related to the = lobster There are about _____ species of owl = one hundred and thirty = 130 It takes a lobster approximately ________ years to grow to be one pound = seven = 7 It takes about _______ hours for a snake to digest one frog = fifty = 50 There are about _______ species of coral known = 5,000 The Mola Mola, or Ocean Sunfish, lays up to _______ eggs at one time = five million = 5,000,000 Researchers don't know why killer whales like to rub their sensitive stomachs on the bottom of shallow beaches, but they think it may be a form of = grooming There are about 500 different kinds of cone snails around the world. All have a sharp, modified tooth that stabs prey with venom like a harpoon. Most cone snails hunt worms and other snails, but some eat fish. These are the ones most dangerous to people. The nerve toxin that stops a fish is powerful enough to also kill a = human The crocodile is surprisingly fast on land. If pursued by a crocodile, a person should run in a ______ motion, for the crocodile has little or no ability to make sudden changes of direction = zigzag What city has more homeless cats per square mile than any other city in the world = rome It takes approximately ____ venom extractions from the coral snake to fill a 1-pint container = 69,000 The most carnivorous of all bears is the ______ bear = polar It would require an average of ____ hummingbirds to weigh in at 1 ounce = eighteen = 18 The digestive juices of crocodiles contain so much __________ that they have dissolved iron spearheads and 6-inch steel hooks that the crocodiles have swallowed = hydrochloric acid Seals can withstand water pressure of up to _________ pounds per square inch = eight hundred and fifty = 850 Jackrabbits are powerful jumpers. A 20-inch adult can leap ___ feet in a single bound = twenty = 20 Javelinas are very noisy animals among each other and squeal, snort, woof, and click their teeth to = communicate There are close to ______ known species of frogs, including toads = 4,000 The most venomous of all snakes, known as the _____, has enough venom in one bite to kill more than 200,000 mice = taipan There are fewer than _____ Bactrian camels left in the wild = one thousand = 1,000 Bactrian camels have survived in a land with no water in an area used for nuclear testing. Their numbers, however, are falling dramatically as humans encroach farther and farther into China's ______ Desert = gobi The dog and the turkey were the only two domesticated animals in ancient = mexico The ________ is a fish that can actually walk on land = mudskipper Time magazine named the computer its "Man of the Year" in = 1982 A 1999 survey of 25,500 standard English-language dictionary words found that ______ percent of them have been registered as .coms = ninety three = 93 A ball of glass will bounce higher than a ball of = rubber A ball of solid steel will bounce higher than one made entirely of = glass A chest X-ray is comprised of 90,000 to 130,000 ______ volts = electron A NUKE InterNETWORK poll found that ________ percent of Internet users have cut back on watching TV in order to spend more time online; 12 percent have cut back on seeing friends = fifty two = 52 A standard 747 Jumbo Jet has ______ seats = four hundred and twenty = 420 After his death in 1937, Guglielmo Marconi, inventor of the ___________ was honored by broadcasters worldwide as they let the airwaves fall silent for two minutes in his memory = wireless telegraph Artificial rain was first used near _______, New Hampshire, in 1947 to fight a forest fire = concord At a glance, the Celsius scale makes more sense than the Fahrenheit scale for temperature measuring. But its creator, Anders Celsius, was an oddball scientist. When he first developed his scale, he made freezing 100 degrees and boiling 0 degrees, or upside down. No one dared point this out to him, so fellow scientists waited until Celsius died to change the scale. True or False = true At a jet plane's speed of ______ miles per hour, the length of the plane becomes one atom shorter than its original length = six hundred and twenty = 620 Bill Gates formed a company to sell a computerized traffic counting system to cities, which made $20,000 its first year. Business dropped sharply when customers learned Gates was only _______ years old = fourteen = 14 Colonel Waring, New York City Street Cleaning Commissioner, was responsible for organizing the first rubbish-sorting plant for recycling in the United States in = 1898 Cooking and salad _____ could lubricate machinery, such as cars and boats, according to Penn State chemical engineers = oils Dating back to the 1600s, thermometers were filled with ______ instead of mercury = brandy Early models of vacuum cleaners were powered by = gasoline Focus group information compiled by CalComp revealed that 50 percent of computer users do not like using a = mouse From 9,000 pounds of roses, which totals about 55,000 blossoms, ___ pounds of costly rose essence can be extracted to be used as a component in fragrances = two = 2 The condensed water vapor trail left behind by jets and rockets is called a = contrail The first automobile to cross the United States took ______ days in 1903 to travel from San Francisco to New York = fifty two = 52 The first commercial passenger airplane began flying in = 1914 The first commercial passenger airplane with a bathroom began flying in = 1919 The first parking meter was installed in Oklahoma City, Oklahoma, in = 1935 The first video game was _____, introduced in 1972 by Noel Bushnell, who then created Atari = pong The highest man-made temperature - _____ million degrees Celsius - was generated at Princeton University in a fusion power experiment in 1978 = seventy = 70 The last 12-cylinder car produced in the United States was the 1948 __________ = lincoln continental Gold salts are sometimes injected into the muscles to relieve = arthritis What is used as a center for golf balls and in antifreeze mixtures = honey Horn & Hardart opened the first U.S. automat in Philadelphia in = 1902 IBM was incorporated in 1911 as the Computer-Tabulating-Recording Co. with a product line that included time clocks, scales and punch card tabulators. The name was changed in _____ = 1924 In 1878, Wanamaker's of Philadelphia was the first U.S. department store to install = electric lighting In 1900, there were _______ cars in the United States = 8,000 By 1919, there were _________ cars in the U.S = six million = 6,000,000 The little "m's" on "M&M's"(r) Chocolate Candies weren't printed on the candies until ____ = 1950 The maximum flight speed of a Boeing 747-300 jetliner is __________ miles per hour = five hundred and eighty three = 583 The ______ is the U.S. Marine tilt-rotor transport that takes off and lands like a helicopter, but flies like an airplane = osprey The Osprey is also known as the = v-22 The rare metal gallium melts at _________ degrees Fahrenheit. It is safe to touch; if you hold a piece of it in your hand and wait, it will melt = eighty six = 86 The Studebaker auto company produced a car called the "_____" from 1927-1936 = dictator The Times Square "time ball" for the year 2000 was named the = star of hope The Times Square "time ball" for the year 2000 was specially made by _________ in Ireland = waterford crystal The Times Square "time ball" for the year 2000 contained _____ glass crystals cut into triangles = five hundred and four = 504 The Times Square "time ball" for the year 2000 contained _______ light bulbs = six hundred = 600 The Times Square "time ball" for the year 2000 contained _______ big lights = ninety six = 96 The Times Square "time ball" for the year 2000 contained _______ mirrors = ninety two = 92 In 1995, the average U.S. public school contained _____ computers = seventy two = 72 In computer-ese, "wysiwyg" is an acronym for = what you see is what you get In December of 1957 -- Shippingport, Pennsylvania became the site of the first full-scale ______ power plant in the U.S = nuclear In July of 1955 - Arco, Idaho, with a population of 1,000, became the first U.S. town powered by nuclear energy. The town's energy was supplied by an experimental boiling-water reactor called the = borax iii Half a byte is called a = nybble There are five types of simple machines: the _____, the pulley, the inclined plane, the screw, and the wheel and axle = lever There are five types of simple machines: the lever, the _____, the inclined plane, the screw, and the wheel and axle = pulley There are five types of simple machines: the lever, the pulley, the _________, the screw, and the wheel and axle = inclined plane There are five types of simple machines: the lever, the pulley, the inclined plane, the _____, and the wheel and axle = screw There are five types of simple machines: the lever, the pulley, the inclined plane, the screw, and the__________ = wheel and axle There are hefty pricetags on some Internet domain names. The highest-selling domain name to date, business.com, went for ________ in 1999 = $7.5 million There are more than ____ satellites orbiting earth solely for the purpose of private communications services, including pagers, telephones, and computers = two hundred = 200 Thirty-two percent of computer data loss cases are due to ______ error = human In Rome, the world's first paved streets were laid out in _____ B.C = one hundred and seventy = 170 In the military world, EGADS is an acronym for = electronic ground automatic destruct system In web site addresses on the Internet, "http" stands for = hypertext transfer protocol In years past, spermaceti oil - from the sperm whale - was used as _________ in Rolls-Royce automobiles = transmission oil Indonesia is the world's largest producer of = liquefied natural gas ISBN stands for = international standard book number It is estimated that ________ light bulbs are manufactured each year in the United States = 1.8 billion To make a daguerreotype, an early photograph, required a 15-minute average _______ time = exposure What cities expressways, built on a radial pattern dating from the eighteenth century, are so overloaded that traffic often grinds to a halt for 30 to 40 miles outside the city = tokyo A device invented as a primitive steam engine by the Greek engineer Hero, about the time of the birth of Christ, is used today as a = rotating lawn sprinkler During the U.S. Civil War, telegraph wires were strung to follow and report on the action on the battlefield. But there was no telegraph office in the White House, so President Lincoln trekked across the street to the ___________ to get the news = war department In ___, Namco released Pac-Man, the most popular arcade game of all time = 1979 WD-40 Company products are sold in more than ________ countries around the world = one hundred and fifty = 150 When the first escalator, or "inclined elevator," was installed in the department store Harrod's in London (near the turn of the century), ________ was served to passengers who felt faint = brandy It takes _____ watts to run an electric toaster = 1,100 It took Henry Ford's Motor Company seven years to manufacture 1 million automobiles. One hundred thirty-two working days after this figure was reached (in 1924), the company had made ______ more cars = nine million = 9,000,000 Microsoft CEO Bill Gates, launched his business career in 1969, at age 14 by forming a company named Lakeside Programming Group. Gates and his friend ______ signed an agreement with Computer Center Corporation to report bugs in PDP-10 software, in exchange for computer time = paul allen Nanotechnology has produced a guitar no bigger than a ______ = blood cell The smallest guitar made by Nanotechnology is _________ long, has six strummable strings = 10 micrometers Natural gas has no = smell Penicillin causes about ________ deaths in the United States every year = three hundred = 300 Forget-me-not pollen grains are so small that _______ of them can fit on the head of a pin = ten thousand = 10,000 The sale of lawn mowers got a great boost when lawn tennis came into vogue in England in = 1870 Skydivers accelerate to a terminal velocity of ______ mph = one hundred and twenty = 120 Some Chinese typewriters have ______ characters = 5,700 Steve Jobs and Steve Wozniak are best known as the creators of the _____ computer = apple Steve Jobs and Steve Wozniak designed a popular arcade game for Atari called = breakout An estimated __________ lab mistakes are made in the 100,000 laboratories in the U.S. every day = two million The average 60-minute audio cassette tape has _______ feet of tape in it = 562.5 The B-2 Stealth Bomber is designed to fly undetected by = radar In Milwaukee during the 1900s, ______ horses in the city left an estimated 133 tons of manure and urine on the streets per year = 12,500 In Milwaukee during the 1900s, 12,500 horses in the city left an estimated ____ tons of manure and urine on the streets per year = 133 Sharks and rays are the only animals known to man that cannot succumb to cancer. Scientists believe this is related to the fact that they have no bone - only = cartilage The duckbill platypus of Australia can store up to _____ worms in its large cheek pouches = six hundred = 600 There are more than ____ species of finches throughout the world = four hundred and fifty = 450 The ears of the Asiatic _____ bears are larger than those of other bear species = black Sharks can travel up to _____ miles per hour = forty = 40 The Nile crocodile averages about 45 years in the wild, and may live up to ____ years in captivity = eighty = 80 Sheep will not drink from __________. Hence, the line in the Twenty-third Psalm: "He leadeth me beside the still waters" = running water There are no wild deer of any kind in Australia, and the small red deer is the only one found in = africa The normal body temperature of the Clydesdale horse is ________ degrees Fahrenheit = one hundred and one = 101 There are _______ distinctive types of combs on chickens = seven = 7 The now-extinct ancestor of the horse, _______, had a short neck, a pug muzzle, and stood no higher than a medium-sized dog = eohippus The electric eel is the most shocking animal on Earth - no other animal packs such a big charge. If attacking a large prey, a 9-foot-long eel can discharge about _____ volts = eight hundred = 800 Since white tigers have pigmented stripes and blue eyes, they are not = albinos There are some _____ different species of sea snakes, and all of them are venomous = fifty = 50 The once popular dog name "Fido" is from Latin and means = fidelity Most lipstick contains = fish scales What is the longest word that alternates hands = skepticisms No piece of square dry paper can be folded more than _____ times in half = seven = 7 A group of geese on the ground is a gaggle, a group of geese in the air is a = skein Over _______ left handed people a year are killed from using products made for right handed people = 2500 There are more than _______ bricks in the Empire State Building = 10 million If you counted 24 hours a day, it would take ______ years to reach one trillion = 31,688 A crocodile always grows new _____ to replace the old ones = teeth The sun is _______ times larger than the earth = 330,330 Clinophobia is the fear of = beds A _____ is an actual unit of time for 1/100th of a second = jiffy Porcupines ______ in water = float The longest recorded flight of a chicken is = thirteen seconds = 13 seconds The world's termites outweigh the world's humans 10 to 1 = humans The electric chair was invented by a = dentist Windmills always turn counter-clockwise. Except for the windmills in = ireland A hedgehog's heart beats ______ times a minute on average = three hundred = 300 Camels have three ______ to protect themselves from blowing sand = eyelids The placement of a donkey's eyes in its' heads enables it to see all it's ___ at all times = feet A mole can dig a tunnel ________ feet long in just one night = three hundred = 300 A hummingbird weighs less than a = penny Until 1796, there was a state in the United States called Franklin. Today it's known as = tennessee The Earth weighs around _____________ tons = 6,588,000,000,000,000,000,000,000 A cockroach can live several weeks with its _______ cut off - it dies from starvation = head Every time you lick a _____, you're consuming 1/10 of a calorie = stamp The average person has over ________ dreams a year = 1,460 One in every _____ Americans has appeared on television = four = 4 The average American/Canadian will eat about 11.9 pounds of _______ per year = cereal It's against the law to burp, or sneeze in a certain church in = omaha, nebraska You're born with 300 bones, but when you get to be an adult, you only have = two hundred and six = 206 Human thigh bones are stronger than = concrete Over _________ birds a year die from smashing into windows = ten thousand = 10,000 The state of Florida is bigger than = england It was once against the law to have a pet dog in a city in = iceland Your heart beats over _________ times a day = one hundred thousand = 100,000 Thomas Edison, lightbulb inventor, was afraid of the = dark Some ribbon worms will eat _______ if they cant find any food = themselves Dolphins ________ with one eye open = sleep The worlds oldest piece of chewing gum is over _____ years old = nine thousand = 9000 In space, astronauts cannot cry, because there is no ________, so the tears can't flow = gravity There are more ______ flamingos in the U.S, than real ones = plastic About 3000 years ago, most _____ died by the time they were 30 = egyptians More people use ______ toothbrushes, than red ones = blue More people use blue toothbrushes, than ____ ones = red A sneeze travels out your mouth at over ______ m.p.h. = one hundred = 100 Your ribs move about _______ times a year, every time you breathe = five million In the White House, there are _______ knives, forks and spoons = 13,092 Recycling one glass jar, saves enough energy to watch T.V for ________ hours = three = 3 Lightning strikes about _______ times per minute on this planet = six thousand = 6,000 The average American/Canadian drinks about _______ sodas a year = six hundred = 600 It was once against the law to slam your car door in a city in = switzerland There wasn't a single pony in the Pony Express, just = horses Honeybees have a type of _______ on their eyes = hair A jellyfish is 95 percent = water In _______, kids as young as 15 can be jailed for cheating on their finals = bangladesh The katydid bug hears through holes in its ____ legs = hind A company in _______ makes dinnerware out of wheat, so you can eat your plate = taiwan More ______ money is printed in a year, than real money printed throughout the world = monopoly The elephant is the only mammal that can't = jump The penguin is the only bird who can swim, but not = fly What is the only letter in the alphabet that does not appear in the name of any of the United States! = q One quarter of the bones in your body are in your = feet Fortune cookies were actually invented in America, in ______, by Charles Jung = 1918 A man named Charles Osborne had the hiccups for _________ years = sixty nine = 69 A giraffe can clean its ears with its _____-inch tongue = twenty one = 21 Chewing gum while peeling onions will keep you from = crying How many pieces of mail are delivered each year in the U.S = 166,875,000,000 How many miles of telephone wire a strung across the U.S = 1,525,000,000 How many cars are being driven down the U.S's highways = 123,000,000 How many tons of paper are used each year in the U.S = 85,000,000 How many people go to Major League baseball each year = 56,000,000 Bats always turn _____ when exiting a cave = left The praying mantis is the only insect that can turn its = head In Tokyo, they sell toupees for = dogs There are over _______ dogs in the U.S = fifty eight million = 58,000,000 Dogs and cats consume over ________ worth of pet food a year = $11,000,000,000 You blink over _______ times a year = 10,000,000 Baby robins eat _____ feet of earthworms every day = fourteen = 14 In England, in the 1880's, ______ was considered a dirty word = pants Most dust particles in your house are made from = dead skin The blesbok, a South African ______, is almost the same color as grapejuice = antelope The average person laughs _______ times a day = thirteen = 13 Dogs can hear _______ that humans cant = sounds Men are 6 times more likely to be struck by _______ than women = lighting It is estimated that millions of trees in the world are accidentally planted by _______ who bury nuts and then forget where they hid them = squirrels Ernest Vincent Wright wrote a novel, "Gadsby", which contains over 50,000 words -- none of them with the letter = e A toothpick is the object most often _______ on by Americans = choked There are more than ________ earthquakes throughout the world every year = fifty thousand = 50,000 The original name for the butterfly was = flutterby The opposite sides of a dice cube always add up to = seven = 7 Nose prints are used to identify = dogs Bulls are _______ = colorblind Apples are more efficient than ______ in keeping people awake in the mornings = caffeine Smelling bananas and/or green apples (smelling, not eating) can help you lose = weight A hard working adult sweats up to ______ gallons per day = four = 4 The average ice berg weighs ___________ tons = 20,000,000 The poison-arrow frog has enough poison to kill about _______ people = 2,200 A lump of pure gold the size of a matchbox can be flattened into a sheet the size of a = tennis court After eating, a _______ regurgitates its food and then eats it again = housefly Which are the most used muscles in the body = eye muscles = eye Which Australian animal rolls into a spiky ball when in danger = echidna Which author wrote the Belgariad Series = david eddings Which blood vessels carry blood from the heart to the various parts of the body = arteries Which book has to do with instructions about sacrificial worship = leviticus Which book, first published in 1949, begins with the words: "it was a bright cold day in april & the clocks were striking thirteen" = 1984 Which boxer is heaviest a bantamweight, middleweight or welterweight = middleweight Which British sports car took its name from the Morris garages where it was developed = the mg Which Canadian province extends farthest north = quebec Which Canadian province has the largest population = ontario Which canary sings the male or female = the male Which capital city has a statue of a mermaid in its harbour = copenhagen Which cicada is the noisy one the male or the female = male Which city 50 miles from the coast is America's eighth busiest port = houston Which city does Orly airport serve = paris Which city has the famous opera house La Scala = milan Which city has the largest rodeo in the world = calgary Which city has the two steepest streets in the usa = san francisco Which city hosted the 1952 winter olympics = oslo Which city hosted the 1956 summer olympics = melbourne Which city hosted the 1968 summer olympics = mexico city Which city hosted the 1988 summer olympics = seoul Which city hosted the first modern olympics in 1896 = athens Which city hosted the first winter olympics in 1924 = chamonix Which city hosted the olympics in 1920 = antwerpen Which city hosted the second olympics in 1900 = paris Which city hosted the third olympics 1904 = st louis Which city is home to the White City tennis courts = sydney Which city is known as the windy city = chicago, illinois = chicago Which city is sacred to Jews, Christians, & Muslims = jerusalem Which city is the home of the sundae = two rivers Which colour goes before angel & bayou on Roy Orbison's singles = blue Which colourless, odourless light gas is used to lift airships = helium Which comes first in a butteryfly's life being a caterpillar or being a pupa = caterpillar Which comic strip was banned from "Stars & Stripes" = beetle bailey Which company made magic carpet = bullfrog Which company released it's first personal computer on august 12, 1981 = ibm Which continent stretches further Aouth Africa or South America = south america Which continent was the original home to the humble potato = south america Which countries occupy the Iberian Peninsula = spain & portugal Which countries occupy the Scandinavian Peninsula = norway & sweden Which country (as of 2000) has the lowest crime rate in the world = togo Which country are the Galapagos Islands part of = ecuador What geometric shape is a stop sign = octagon What geometric shape is a tennis court = rectangular What German military leader of the Afrika Korps was known as 'The Desert Fox' = erwin rommel = rommel What gland is found just below the optic chiasma of your brain = pituitary gland What gods are the planets of our solar system named after = roman gods = roman What goes with ruby, emerald & sapphire to make up the world's most valuable gems = diamond What golfer was nicknamed "super mex" = lee trevino What golfer was nicknamed 'the golden bear' = jack nicklaus What grain is Australia's most important crop = wheat What grain is used to make spaghetti = wheat What grass is the favourite food of pandas = bamboo What green vegetable comes in varieties such as cos & mignonette = lettuce What group landed in America in 1620 = pilgrims What group of superheroes display the number 4 on their chests = the fantastic four What gun cannot be silenced, due to all the noisy gasses which escape the cylinder gap at the rear of the barrel = revolver What habit forming plant did Columbus find on his first new world trip = tobacco What hair color of people do mosquitoes seem to be the most attracted to = blonde What hall of fame was opened in 1990 in Canastota, New York = boxing What Hans Christian andersen fairy tale character is immortalized in a famous statue in Copenhagen's Harbor = the little mermaid What happens to male ants soon after they mate = they die What happens when reverse peristalsis takes place = you puke What has a bitch done if she has just whelped = given birth What has been called "the land of frozen time" = antarctica What has been clinically demonstrated to work twice as well as dramamine for fighting motion sickness, with no side effects = ginger What has to be missing out of anhydrous compounds = water What Hawaii town lies very close to Volcanoes National Park = hilo What heavyweight boxing champion was nicknamed the Manassa Mauler = jack dempsey What hemisphere are you in if your water drains clockwise = southern What herb is most often used as a garnish = parsley What herb is used in the sauce traditionally served with roast lamb = mint What hip russian word means "giving voice to something" = glasnost What hobby was developed by the Palmer Paint Company of Detriot = paint by numbers What holiday is held on April 25 in Australia & New Zealand = anzac day What hormone is produced by the adrenal glands = adrenaline What household appliance has a tube & an agitator = washing machine What human organ does a pacemaker pace = heart What ice cream company registered the slogan "dedicated to perfection" = haagen daas What ill fated auto bit the dust in 1959 after losses of millions = edsel What illness is controlled by taking insulin = diabetes What important historic artifact was found in November of 1922 = king tuts tomb = king tutankhamun's tomb What indian tribe did Custer encounter at the Battle of Little Big Horn = sioux What indoor track milestone was Jim Beatty the first to break in 1962 = four minute mile What inert gas is used in fluorescent lights = argon What insect has a king as well as a queen = termites What instrument is used for measuring the distance between two points on a curved surface = caliper What instrument is used to measure the pressure of gases = manometer What instruments are used to measure air pressure = barometers What is "Banda Oriental" known as today = uruguay What is 113 in Roman numerals = cxiii What is 2 + 3 = 5 What is 30% of 90 = twenty seven What is 99% of 200 = one hundred & ninety eight = 198 What is a 300th anniversary called = tercentenary What is a artificial egg hatching device called = incubator What is a baby frog called = tadpole What is a baby goose called = gosling What is a baby kangaroo called = a joey What is a baby owl called = an owlet = owlet What is a baby rooster = cockerel What is a baby seal called = pup What is a baby swan called = cygnet What is a baby whale called = calf What is a bone specialist = osteopath What is a bowler credited with when no runs are scored off his over = maiden over What is a buckey ball made of = carbon What is a 'bunker' in a game of golf = sand trap What is a calm ocean region near the equator = doldrums What is a camberwell beauty = butterfly What is a cancer causing substance = carcinogen What is a cancerous tumour = malignant What is a castrated shy sheep called = wether What is a cob & a pen = swan What is a 'colander' = sieve What is a corrosive substance = acid What is a crime worse than a misdemeanor = felony What is a dactyologram = fingerprint What is a dried plum called = a prune What is a fear of germs = spermophobia What is a female bear = sow What is a female fox called = vixen What is a female swan called = pen What is a french policeman called = gendarme What is a group kangaroos = troop What is a group of baboons = troop What is a group of badgers called = cete What is a group of bears called = sleuth What is a group of cockroaches = intrusion What is a group of curs = cowardice What is a group of donkeys called = herd What is a group of elks called = gang Which railway station in London is known by two names = bank & monument Which religious group grew out of the Watch Tower Society = jehovah's witnesses Which river contains the most fresh water = amazon Which Russian city used to be called Leningrad & Petrograd = st petersburg Which saint has the deer as his iconographical emblem = st francis Which saying is attributed to author Henry David Thoreau = any fool can make a rule Which science fiction author used the penname Edward Powys Bradbury = michael moorcock Which science fiction novel has its title from the flash point of paper = farenheit 451 Which science fiction writer penned "The Martian Chronicles" & "Farenheit 451" = ray bradbury Which science studies weather = meteorology Which Scottish born machinist became America's first environmentalist = john muir Which sea is between Australia & Tasmania = tasman sea Which sea is the city of Beirut on = mediterranean Which shoulder go superstitious people throw salt over = left Which shoulder should you throw spilled salt over = left Which side of a book are the even numbered pages usually on = left Which side of a commercial jet does the pilot sit on = left Which side of the human body is controlled by the left side of the brain = right side Which side of the road do people drive on in France = right Which side of the road would be travel on if you were driving in Japan = left Which small animals reportedly march to the sea to drown = lemmings Which son of Zeus was a messenger & herald of the gods = hermes Which sport do Australia & NZ play when battling for the manning cup = hockey Which sport has a movement called a 'telemark' = skiing Which state has Cape Hatteras = north carolina Which state has the most hospitals = california Which state is called the pelican state = louisiana Which state is the wolverine state = michigan Which state of Australia lays claim to Lizard & Heron Islands = queensland Which state's official flag is in the shape of a pennant = ohio Which strong wine gets its name from a city in Northern Portugal = port Which superhero loves peace enough to kill for it = peacemaker Which terminal of a battery is indicated by a plus sign = positive Which town is the birthplace of Jesus Christ = bethlehem Which tree has the same name as a part of your hand = palm Which two countries became independent in 1957 = ghana & malaysia Which two countries form what used to be called Patagonia = chile & argentina Which two sports do you go backwards to win = rowing & tug of war Which type of paint dries faster oil or acrylic = acrylic Which tyre company withdrew from Formula 1 at the end of 1986 = pirelli Which US city is known as Beantown = boston Which US city is known as the biggest little city in the world = reno, nevada Which US city was named after the only person to be governor of 2 states = houston Which US coin has 118 ridges around the edge = dime Which US gangster town gave its name to the group who had a no 1 with 'if you leave me now' = chicago Which US government branch includes the president & vice president = executive branch Which US president lead his "rough riders" up San Juan hill = theodore roosevelt Which US president received & 10% of the profits when he was interviewed by david frost = richard nixon Which US president said, "the buck stops here" = harry truman Which US state borders a canadian territory = alaska Which US state is known as the "last frontier" = alaska Which US state was the 50th = hawaii Which was the first apostle to be stoned to death = stephen Which was the first book to tell the story of the lioness Elsa = born free Which was the first Japanese city bombed in 1945 = hiroshima Which was the sacred animal of ancient Egypt = cat Which was the second Japanese city bombed in 1945 = nagasaki Which was the world's first space station = salyut 1 Which weighs more hot water or cold water = hot water Which word is related to these three: rat, blue, cottage = cheese Which would usually live longer, a giraffe or a zebra = zebra Which writer & illustrator was fired in july 1992 by King Features over an abortion rights story line in his daily comic strip Popeye = bobby london Which writer said "a little learning is a dangerous thing" = alexander pope Which writer's widow changed the family's name to Holland after his death to avoid scandal = oscar wilde Which zodiacal sign is represented by a goat = capricorn While sleeping, one man in eight does what = snores While sleeping, one man in ten does what = grinds his teeth White, crystalline solid that has the formula c6h4(oh)cooh = salicylic acid Who abolished Christmas in 1647 = english parliament Who allegedly killed officer J D Tippett = lee harvey oswald Who allowed the bugging of the Democratic committee headquarters. = richard nixon Who always ended his show in a white e type jaguar = simon dee Who always tried to kill Krazy Kat = captain marvel Who appeared on the back of the $20 bill in 1875 = pocahontas Who appears when Aladdin rubs the lamp = genie Who are the Bronte Sisters in alphabetical order = anne, charlotte, emily Who assassinated President Kennedy = lee harvey oswald Who baptised Jesus = john the baptist Who beat Captain Robert Scott in the race to the south pole in 1912 = roald amundsen Who became famous for photographing the civil war = mathew brady Who became the first englishman to win the Nobel prize for literature in 1907 = rudyard kipling Who betrayed Jesus to the Romans = judas iscariot Who built Camelot = king arthur Who buys corner blocks = stamp collectors Who 'came whiffling through the tulgey wood' = jabberwock Who camped by a billabong = swagman Who carried on Charles Babbages work when he died = his son Who carved the worlds tallest totem pole out of one log = mungo martin Who claimed "Rosencrantz & Gguildenstern are dead" = tom stoppard Who climbed Mt Fuji at age 99 = teiichi igarashi Who coached the Celtics to 6 NBA championships = bob cousy Who coined the term, 'the evil empire' = ronald reagan Who coined the word 'bourgeoise' = karl marx Who collects coins or medals = numismatist Who collects match box labels = philluminist Who collects stamps = philatelist Who comes hopping down the bunny trail = peter cottontail Who commanded the rough riders in Cuba in the Spanish American War = leonard wood Who commanded the Russian navy on the Black Sea from 1788 1789 = john paul jones Who composed the music for Apollo 13 = james horner Who controls more than 80% of the world's rough diamond supply = de beers Who could distinguish 140 forms of tobacco ash = sherlock holmes Who counted himself out on march 31, 1968 = lyndon johnson Who created detective Nero Wolfe = rex stout Who created Jergens Lotion = andrew jergens Who created John Blackthorne = james clavell Who created master spy George Smiley = john le carre Who created Miss Marple = agatha christie Who created 'Peter Rabbit' = beatrix potter Who created the first flavored soda pop = townsend speakman Who created Winnie the Pooh = a a milne Who deciphered the Rosetta stone = jean champollion Who define heat in ergs per calorie = james joule Who denied Jesus three times = peter Who described the moon during Apollo 11 as "magnificent desolation" = buzz aldrin Who designed the uniforms for the Vatican City's Swiss guards = michelangelo Who designed the union buildings = sir herbert baker Who detailed her romance with Bill Clinton in the December Penthouse = jennifer flowers Who did Adolf Hitler marry = eva braun Who did Adolph Hitler keep a framed photograph on his desk of = henry ford Who did always rescue sweet Polly Purebred = underdog Who did Anne Boleyn lose her head over = henry viii Who did David kill = goliath Who did Joseph Stalin succeed as premier of Russia = lenin Who did Louis XVI give the Hope diamond to = marie antoinette Who did New York's Cosmos buy for $4.5 million in 1975 = pele Who did pussy cat go to see in London = queen Who did Queen Elizabeth II marry = prince phillip Who did Samson fall in love with = delilah Who did Simple Simon meet on the way to the fair = pieman Who did the Miracle Mets defeat to win the 1969 world series = thailand Who discovered Florida = ponce de leon Who discovered morphine = friedrich saturner Who discovered natural radioactivity = antoine becquerel Who discovered that galaxies were all red shifted i.e the universe was expanding = edwin hubble Who discovered the Mississippi River = hernando de soto Who discovered the neutron in 1932 = sir james chadwick Who discovered the planet Uranus = william herschel Who discovered x rays = wilhelm roentgen Who do Brazilians call Huguinho, Zezinho & Luisinho = huey dewey louie Who do the Japanese call Miki Mauso = mickey mouse Who dreamed about the sun, moon & stars bowing down to him = job Who edited the Joy of Sex = alex comfort Who ended his news broadcasts with "glad we could get together" = john cameron swayze Who fiddled while Rome burned = nero Who first calculated 'pi' around 250 bc = archimedes Who flew the rocket powered Bell X IAa at a record mach 2.5 = chuck yaeger Who followed Alexander Haig as US foreign secretary in 1982 = george schultz Who fought professionally in Roman arenas = gladiators Who founded Atari = nolan bushnell Who founded histology = marie bichat Who founded mormonism = joseph smith Who founded the boy scout movement = robert baden powell Who founded the boy scouts of America = daniel beard Who founded the foundation for infantile paralysis = franklin roosevelt Who founded the Persian empire = king cyrus Who gather together in covens = witches Who gave Joseph Keaton his nickname of "Buster" = harry houdini Who gave the U. S.A. the Statue of Liberty = france Who ghost wrote Xaviera Hollanders "The Happy Hooker" = yvonne dunleavy Who got his name because of a yellow & black striped shirt he wore until it literally fell apart = sting Who got the worlds worst circumcision = john bobbit Who had over 1000 patents registered to him when he died = thomas edison Who had six fingernails on one hand , but only five fingers on that hand = anne boleyn Who had teenage sidekicks Zan, Wendy, Jayna & Marvin = super friends Who had the vision that resulted in the book of Revelations = john Who has been nicknamed Dr Death in the 1990s = dr jack kevorkian Who has more chromosomes, Tarzan or Cheetah = cheetah Who has the biggest percentage of female heads of household = botswana Who hid in a house at 263 Princengracht, Amsterdam, for two years = anne frank Who hit a homerun in his first at bat in college, the olympics, & the major leagues = will clark Who holds the record for most touchdown passes in one season = dan marino Who hosts the Monza grand prix = italy Who in baseball was known as king kong = dave kingman What is this short for - HiOAg = hi ho silver What are u doing here - |r|e|a|d|i|n|g| = reading between the lines Einstein gave this successful prediction of the stock market when asked what it would do in the next year. He said = it would fluctuate Nearly a third of all bottled drinking water purchased in the US is contaminated with _________ = bacteria If you sneeze too hard, you can = fracture a rib If you try to suppress a sneeze, you can rupture a _________ in your head or neck and die = blood vessel Rats multiply so quickly that in 18 months, two rats could have over ________ descendents = one million = 1,000,000 The average chocolate bar has ____ insects' legs in it = eight = 8 The average human eats __________ spiders in their lifetime at night = eight = 8 The shortest war in history was between ___________ in 1896 = zanzibar & england The shortest war in history Zanzibar surrendered after _______ minutes = thirty eight = 38 A polar bear's skin is black. Its fur is not white, but actually = clear Elvis had a twin brother named ____, who died at birth = garon Donald Duck comics were banned in ______ because he doesn't wear pants = finland More people are killed by _______ annually than are killed in plane crashes = donkeys What is the longest word typed with only the left hand = stewardesses Shakespeare invented the words _________ & _______ = assassination & bump Marilyn Monroe had six = toes If you keep a goldfish in the dark room, it will eventually turn = white Women blink nearly ______ as much as men = twice Right handed people live, on average, ______ years longer than left handed people do = nine = 9 The name of all the _______ end with the same letter that they start with = continents The word "lethologica" describes the state of not being able to _______ the word you want = remember If the population of China walked past you in single file, the line would never end because of the rate of = reproduction The words _____ & _____ are the same whether they are read left to right or right to left = racecar & kayak A snail can sleep for _____ years = three = 3 American Airlines saved $40,000 in 1987 by eliminating one _______ from each salad served in first-class = olive How many World Championship points are awarded to a winning Grand Prix driver = ten = 10 In French what country is Ecosse = scotland Which Daniel Defoe character was born in Newgate Prison = moll flanders Why is Caspian Sea a misnomer = its a lake Which two ballet dancers were Marguerite and Armand created for = fonteyn and nureyev What fruit do viticulturists grow = grapes In which year did it become illegal to beat your sernants to death in the Mississippi basin area = 1985 What was the capital of Kenya prior to Nairobi = mombassa Which country supplies almost 80% of the worlds jute = bangladesh What was the nationality of the very first man to stand on the sumit of Mount Everest = new zealander What is added to chalk to produce cement = clay What part of the body does a cytologist study = cells Which language do Walloons speak a version of = french How did Eva Peron die = cancer What three words follow Shakespeare's Now is the winter of our discontent = made glorious summer What is a planet's apogee = furthest point from the earth What is the Russian word for citadel = kremlin What common word used by road users is derived from the Hindu god Jagganath = juggernaut Of which country is the flower the French Marigold a native = mexico Which country originated the Carpetbag Steak = australia Which former racing driver owns Austria's second largest airline company = niki lauda What nationality is Salman Rushdie = british What is a satellite's perigee = closest point to the earth What natural materials are tussah and habutai types of = silk Who won 4 tennis grand slams before she was 18 = monica seles Which town would you be leaving if you were sailing away from the capital of the Flemish coast = dunkirk Which 937 foot tanker caused the worlds worst oil pollution disaster in 1989 = the exxon valdez Where do hikers sleep, was founded by George Williams and sung about by Village People = ymca Which Richard Adams book featured a fox terrier called Snitter = the plague dogs Which of the American Marshal Islands became famous as a nuclear test site = bikini atoll A glow worm isn't a worm, its a = beetle Which river flows over the Victoria Falls = zambezi Which sci-fi author predicted artificial satellites in 1945 = arthur c clark Which writer became a Governor General of Canada = john buchan What was the original name of the communications satellite Intelsat = early bird Into which ocean does the Zambezi river flow into = indian What was the former name of Zambia = northern rhodesia Who led the unsuccessful Roman slave revolt in73 BC = spartacus Who was the famous son of Philip II of Macedonia = alexander the great What is the second most frequently used letter in the English language = t Where did Laika the first dog in space die = in space What was the former name of Zimbabwe = southern rhodesia Which Greek island was the home of the Minoan bull culture = crete What writer said "Youth is wasted on the young" = george bernard shaw What does the term Honking mean to a cyclist = standing out of the saddle What is the largest city in Switzerland = zurich Who said "A woman is only a woman, but a good cigar is a smoke" = rudyard kipling Which sport is Wayne Gretzky famous for = ice hockey Which city was the seat of the Holy Roman Emperors in the 17th century = vienna Which pharaoh built the great temple at Abu Simbel = ramses ii Which poet first said "A thing of beauty is a joy forever" = keats In which country did Alexander the Great mount his great campaign of 327-325 BC = india What words from Tennyson's Charge of the Light Brigade follow "Theirs is not to reason why = theirs is but to do or die What title is given to the head of state in Brunei = sultan Which London club saw the start of Phileas Fogg's journey = the reform club What do Americans call the silencer on a car = muffler What did Japanese athlete Suburaya do after failing to win in the 1964 Olympics = committed hari-kiri What was removed from Mussolini's body before it was buried = brain Who wrote Death of a Salesman = arthur miller Why was a last minute decision made to bomb Nagasaki rather than the chosen target of kokura = because it was obscured by cloud To which family of birds does the robin belong = thrush Who was the most senior serving Nazi to be tried at Nuremberg = hermann goering Which is the only city still in existence that is mentioned in Genesis = damascus Where did Anne Frank die = belsen Which city is the setting for Bizet's Carmen = seville What is the common name for Seborrheic Dermatitis = dandruff Which major planet spins in the opposite direction to the rest = venus What is the main river to flow through Hamburg = elbe What do architects claim Chicago's Tacoma Building to be the first of = skyscraper On which ship did Sir Francis Drake receive his knighthood = the golden hind Which country is called Misr by its natives = egypt What are a whales breathing organs called = lungs Which power station in Pennsylvania was the scene of a 1979 nuclear accident = three mile island How many cards are dealt to each player in gin rummy = ten = 10 What is made from a tree bark that turns a gin pink = angostura bitters Who said he could distinguish between 140 different types of tobacco ash = sherlock holmes Before 1992 what 4 letters were on Soviet Olympic vests = cccp Which country can be reached by bridge from Singapore = malaysia What material did cordwainers work with = leather What kind of dance is the Blue Danube = waltz What colour are blue tit's breasts = yellow Who cut off Van Gogh's ear = himself What city was called 'The Rose red city, half as old as time = petra In 1995 what nation restarted its nuclear weapon testing programme = france Why do woodpeckers peck trees = to catch insects What nationality was Mata Hari = javanese Which organ of the body produces bile = liver How many red stripes are there on the US flag = seven Which planets year is shorter than its day = venus Which South American city is built in the shape of an aeroplane = brasilia What 18th century tourist attraction is at the end of berlins Unter den Linden = brandenburg gate Who is the most widely translated French author = jules verne What is rust on copper called = verdigris Which country is part of ANZUS security alliance along with New Zealand & the USA = australia Which is the worlds second largest French speaking city = montreal What name is given to grotesquely carved church water spouts = gargoyles Where do gallinaceous birds nest = on the ground Which is the worlds hottest capital city = khartoum What was another name for the Atlantic Penguin which became extinct in 1844 = great auk How many pounds does the Olympic hammer weigh = sixteen Who built Peking's Winter Palace = kubla khan Which Italian tourist attraction was exposed as a mediaeval fake in 1988 = turin shroud In which country is the Atacama Desert where rain has never fallen = chile What was Moonie leader-Reverend Moon jailed for in 1976 = tax fraud In what year was the last manned moon landing = 1972 What was the real name of malcolm X = malcolm little Which of Arthurs knights undertook the Seige Perilous = galahad How many of the United States don't border the main land mass = two For which product was Millais' painting Bubbles used to advertise = pear's soap How many letters are there in the french alphabet = twenty five Who wrote the book the Jewel in the Crown = paul scott What kind of stick does an artist rest his brush on = maulstick Which organisation killed Aldo Moro = the red brigade What rare water creature gives its name to a river in Florida = manatee Who discovered gamma rays = ernest rutherford Who succeedeed president Marcos = mrs corazon aquino What did Disraeli refer to as a 'milestone round our necks' = colonies Whats the maximum number of wives permitted at any one time under Islamic law = four Which Paris building has a glass pyramid in its forecourt = the louvre By what name was Cambodian leader Saloth Sar better known = pol pot What was banned in bermuda until 1946 = cars What is the largest structure ever made by living creatures = great barrier reef In 1950 Stevie---------- , singer/songwriter (You are The Sunshine of My Love), born. = wonder In 1961 Gary---------- , 2 time Academy award winning actor, dies at 60. = cooper In 1965 Rolling Stones record "---------- ". = satisfaction In 1966 Rolling Stones release "---------- ". = paint it black In 1970 Beatles movie "---------- " premiers. = let it be In 1981 Pope John ---------- shot, wounded by assailant in St Peter's Square. = paul ii In 1988 Chet ---------- jazz trumpeter fell to death out of a hotel window at 59. = baker In 1989 Approx ---------- students begin hunger strike in Tiananmen Square, China. = 2,000 In 1944 George ---------- director (Star Wars), born. = lucas In 1952 David ---------- Dunbartin Scotand, rocker (Talking Heads-Psycho Killer) , born. = byrne In 1973 ---------- launched, the 1st Space Station. = skylab In 1987 Rita ---------- actress, dies at 68 of Alzheimer's disease. = hayworth In 1602 Cape Cod was discovered by the English navigator ---------- Gosnold. = batholomew In 1753 ---------- Term Day. = whitsunday In 1886 American poet Emily ---------- died. = dickinson In 1918 US Airmail begins between Wash DC, ---------- and New York. = philadelphia In 1919 Wladziu Valentino---------- , pianist, born = liberace In 1930 A United Air Lines flight from---------- , CA to Cheyenne, WY carried the first airline "air hostess" (stewardess) -- Ellen Church. = oakland In 1934 US Dept of Justice offers ---------- reward for Dillinger, dead or alive. = $25,000 In 1936 Paul---------- , playwright, born = zindel In 1940 Nylon ---------- on sale for the first time in the United States. = stockings In 1948 Brian---------- , singer, songwriter, born = eno In 1955 Lee---------- , actor, born = horsley In 1958 The Soviet Union launched---------- . = sputnik iii In 1770 Marie ---------- married the future King Louis XVI of France. = antoinette In 1801 William H.---------- , who bought Alaska at $0.02/acre, born. = seward In 1905 Henry ---------- (in Grand Island, Nebraska), actor, born. = fonda In 1920 Joan of Arc was ---------- in Rome. = canonized In 1929 First Academy Awards were given out. '---------- ' won best picture. Best Actor went to Emil Jennings; Best Actress, Janet Gaynor. = wings In 1952 Pierce ---------- (in Israel), actor (current James Bond), born. = brosnan In 1955 Debra ---------- (in Columbus, Ohio), actor, born. = winger In 1955 Olga ---------- (in Russia), gymnast, born. = korbut In 1966 Janet---------- , singer, Michael's sister, born. = jackson In 1969---------- , Venus landing. First successful landing on another planet. = venera 5 In 1970 Gabriela---------- , tennis professional, raqueteer, born. = sabatini In 1975 Japanese Junko ---------- became first woman to reach Mt Everest's summit. = tabei In 1990 Sammy---------- , Jr., "The Complete Entertainer", dies of throat cancer. = davis In 218 7th recorded ---------- passage of Halley's Comet. = perihelion In 352 ---------- begins his reign as Catholic Pope. = liberius In 884 St ---------- begins his reign as Catholic Pope. = adrian iii In 1444 Sandro---------- , Italian painter (Birth of Venus), born. = botticelli In 1620 1st merry-go-round seen at a fair (Philippapolis,---------- ). = turkey In 1630 Italian Jesuit Niccolo---------- , 1st to see 2 belts on Jupiter surface. = zucchi In 1672 Frontenac becomes governor of ---------- (Canada). = new france In 1809 Papal States annexed by---------- . = france In 1836 Joseph Norman ---------- discovered Helium/founded Nature magazine, born. = lockyer In 1900 Ayatollah Ruhollah ---------- Iran's spiritual leader, born. = khomeini In 1928 ---------- modern Olympic games opens in Amsterdam. = 9th In 1936 Dennis ---------- actor (True Grit, Blue Velvet, Easy Rider), born. = hopper In 1940 Germany occupies---------- , Belgium & begins invasion of France. = brussels In 1942 Taj ---------- NYC, singer/songwriter (The Real Thing), born. = mahal In 1950 Christian---------- , French couturier, born. = lacroix In 1956 "Sugar" Ray ---------- welter/middle/light-heavyweight boxing champion, born. = leonard In 1963 Brigitte ---------- actress (Red Sonja, Rocky IV, Domino), born. = nielsen In 1970 Thor ---------- crosses the Atlantic on reed raft 'Ra'. = heyerdahl In 1975 ---------- releases "I'm Not in Love". = 10cc In 1983 Israel & ---------- sign a peace treaty. = lebanon In 1989 Nelson ---------- recieves a BA from University of South Africa. = mandela In 526 St ---------- ends his reign as Catholic Pope. = john i In 1830 Edwin ---------- of England signs an agreement for manufacture of his invention, the lawn mower. = budding In 1868 ---------- last Russian tsar (1894-1917), born. = nicholas ii In 1872 Bertrand ---------- England, mathematician/philosopher (Nobel 1950), born. = russell In 1897 Frank ---------- movie director (Its a Wonderful Life, Arsenic & Old Lace), born. = capra In 1900 Britain proclaims protectorate over kingdom of---------- . = tonga In 1910 Passage of ---------- through tail of Halley's Comet causes near-panic. = earth In 1912 Perry ---------- singer/TV (Perry Como Show, What did Delaware?), born. = como In 1919 Dame Margot ---------- England, ballerina (partner of Nureyev), born. = fonteyn In 1960 Yannick ---------- France, tennis player (French 1983), born. = noah In 1965 Gene ---------- suggests 16 names including Kirk for Star Trek Captain. = roddenberry In 1980 Ian ---------- musician (Joy Division), dies. = curtis In 1980 Mount ---------- blows its top in Washington State. = st helens In 715 St ---------- begins his reign as Catholic Pope. = gregory ii In 1536 Anne ---------- wife of Henry VIII, beheaded. = boleyn In 1588 Spanish Armada sets sail for---------- , bound to England. = lisbon In 1611 Innocent XI ---------- Roman Catholic pope (1676-89), born. = 240th In 1890 ---------- trail blazer/leader of Vietnam (1946, 1969), born. = ho chi minh In 1925 ---------- Omaha NB, assassinated leader of black muslims, born. = malcolm x In 1928 "Firedamp" explodes in ---------- Pa coal mine killing 195 of 273 miners. = mather In 1935 T.E. ---------- (of Arabia) dies in a motorcycle crash. = lawrence In 1945 Peter ---------- singer (Who-Tommy), born. = townsend In 1964 US diplomats find at least 40 secret mics in the ---------- embassy. = moscow In 1966 Tortoise reportedly given to Tonga's king by Capt. ---------- (1773), dies. = cook In 1971 Ogden ---------- poet/TV panelist (Masquerade Party), dies at 68. = nash In 1971 USSR launches---------- , 1st spacecraft to crash land on Mars. = mars 2 In 1976 ---------- ownership legalized in Australia. = gold In 1979 Guitarist Eric Clapton marries Patti---------- . = boyd Nicosia is the capital of which island = cyprus The musician Stephane Grappelli was famous for playing which instrument = violin Of which country was Anwar Sadat the president = egypt Ferdinand Marcos was the president of which country = philippines The Dead Sea lies on the border of Israel and which other country = jordan Which character was played by Roger Mooore in 'The Saint' = simon templar The Golden Temple at Amritsar is sacred to which religion = sikhism In which field were the Boulting Brothers famous = cinema A yarmulka is a skullcap worn by followers of which religion = judaism What is a yarmulka = a skullcap TV /Movies:Hugo Drax was the villian of which James Bond book and film = moonraker TV /Movies:The song 'Evergreen' features in which Barbra Streisand film = a star is born The Ural mountains are mainly in which country = russia In which country are the Southern Alps = new zealand The Santa Maria was the ship of which explorer = christopher columbus What kind of bird is a macaw = parrot Typically, which type of literary form is an elegy = poem What is the modern name for Petrograd = st. petersburg What are seraphim and cherubim = ranks of angels A popular climbing plant with sweet smelling flowers = sweet pea On which continent is the Limpopo River = africa What is a bantam = chicken Where in the world is El Salvador = central america Complete the title of the Charles Dickens novel 'Martin = chuzzlewit TV /Movies:Who was the male star in the film The Beach = leonardo dicaprio What term is applied to the widow of a king = queen dowager Which car manufaturer produces a model called the 'Laguna' = renault What type of creature is a pollack = fish TV /Movies:What type of dog is Lady in the cartoon 'Lady and the Tramp' = cocker spaniel Which US state has the postal abbreviation IN = indiana Which Shakespeare play features the line 'Something is rotten in the state of Denmark = hamlet What is dermatophobia the fear of = skin In which century was the first skyscraper built = 19th What nationality is the novelist Norman Mailer = american What is the maximum number of consecutive terms that a US president can now serve = two TV /Movies:Who played the title roll in the 1938 film 'The Adventures of Robin Hood' = errol flynn TV /Movies:Who played the suave thief,Sir Charles Lytton, in the film The Pink Panther = david niven What nationality is the artist Jackson Pollock = american Andalusia is a region located in the south of which country = spain What is the technical name for the collarbone = clavicle Jupiter is the Roman name for which god of Greek mythology = zeus Which planet in our solar system was discovered most recently = pluto Which musical term means a mass for the dead = requiem The Greek warrior Odysseus played a major role in which war = trojan What type of creature is a basilisk = lizard In which European country do one hundred grochen equal one schilling = austria TV /Movies:Which film actor began his climb to stardom in the TV cowboy series 'Rawhide' = clint eastwood Charcoal is made by heating what = wood Instruments made from tea chests and washboards were a feature of which specific type of music = skiffle Sephardim are followers of which religion = judaism Which ballet company is based at the Maryinsky Theatre = kirov ballet In German towns, what is the S-bahn = railway A 'schipperke' is a breed of which animal = dog TV /Movies:Who is the star of the 2000 film 'The Patriot' = mel gibson In yoga how is the lotus position performed = sitting cross-legged What does the word 'maudlin' mean = self-pitying The cap and bells formed the insignia of which group of people = jesters TV /Movies:The song 'A Whole New World' comes from which Disney film = aladdin In the abbreviation P & O, what does the O stand for = oriental What type of book is a 'cahier' = note book Who wrote the novel the Valley of the Dolls = jaqueline susann Which American footballer won four Super Bowls with the San Francisco 49ers = joe montana Which country was once ruled by a Mikado = japan In literature who has a servant called Sanch Panza = don quixote The book "Cry the Beloved Country' was set in which country = south africa Who adopted the title 'Il Caudillo' = franco Tristan da Cunha is an island group in which ocean = atlantic What is the name of the hammer used by doctors to test a patient's reflexes = plexor Which canal was nationalised in 1956 = suez What is the name for a line of longitude on a map = meridian Which term connects a type of match and a nickname for the Devil = lucifer Which fish is pickled to make rollmops = herring A rune is a letter of what ancient alphabet = germanic What is the back of a ship called = stern The name Felicity means what = happiness What is a 'shillelagh' = cudgel Which dog name is a slang term for a detective = bloodhound Who would be described as a 'sophomore' = student What name is given to young herrings = whitebait Pitcairn Island was settled by crew members of which ship = bounty What was the favourite colour of the novelist Barbra Cartland = pink What type of drug would be used to treat hay fever = antihistamine What name is given to an army chaplain = padre Which compny makes the perfume 'Coco' = chanel Geographically, what is an eddy = whirlpool Paul Newman married which actress = joanne woodward Fianna Fail is a political party in which country = ireland What nationality is the singer/actress Bjork = icelandic The Rialto Bridge is associated with which canal = grand Complete the title of this Travis song 'Why Does it Always.... = rain on me What is the name for the load line on a ship = plimsoll line What is the smallest independent country in South America = surinam Who married the actor Liam Neeson = natasha richardson The word 'bus' is shortened from which other word = omnibus What was the last name of Isadora, the famous dancer = duncan Which 20th century king reigned for less than a year = edward viii Nantucket Island is part of which American state = massachusetts Sepia is obtained from which sea creature = cuttlefish What is the name given to a type of fossil resin = amber What was the first name of Baden-Powell the founder of the Scout movement = robert Mir is the highest point in which mountain range = hindu kush Peter Sellers had a top ten hit in 1960 with which other film star = sophia loren Which oscar winning actress married media mogul Ted Turner in 1991 = jane fonda Who won the 1980 Pullitzer prize for literature for 'The Executioner's Song' = norman mailer Jazz musician Bud Powell was best known for playing what instrument = piano A Victoria Cross is struck from gun metal of Russian cannons captured at which battle = sebastopol What is an 'ocarina' = wind instrument What is the largest species of toad = cane toad Albert Einstein worked at which American university = princeton Which New Mexico town adopted the name of a radio quiz show = truth or consequences In which year was the sculptor and painter Alberto Giacommetti born = 1901 Bougainville is part of which island group = solomon How many novels did Anthony Trollope write = 47 Papeete is the capital of which overseas area of France = french polynesia In which year was prohibition repealed in the USA = 1933 What nationality was the playwright August Strindberg = swedish What does the constellation Vulpecula represent = fox What kind of creature is a 'hoatzin' = bird A marigraph is an instrument which records what = tide level What kind of animal is a 'margay' = cat Bodrum in Turkey is on the site of which famous city = halicarnassus A 'dybbuk' is an evil spirit in which folklore = jewish The Admiralty islands are part of which country = papua new guinea The Cassini division is associated with which planet = saturn What was the real first name of Tennessee Williams = thomas Burke and Wills are most associated with the exploration of which continent = australia Nephrite is a variety of which substance = jade The conqueror Temujin was better known by what name = genghis khan In which country is the city of Samarkand = uzbekistan What is the birthstone of someone born on Christmas Day = turquoise The extinct elephant bird was native to which island = madagascar The coelacanth, once thought to be extinct, is what kind of creature = fish In mythology who continued to weep after she had been turned to stone = niobe In the Bible who was the mother of Ishmael = hagar Who wrote the novel 'King Solomon's Mines' = h rider haggard In India who or what is a 'dacoit' = robber What kind of creatures are 'ratites' = birds What nationality was the artist Hieronymus Bosch = dutch Which part of the body is affected by the condition "blepharism' = eyelid Who wrote the poem 'Hudibras' = samuel butler Malcolm Cooper is an Olympic gold medal winner in which sport = shooting Which island is called Kalaallit Nunaat in its own language = greenland King Ludwig of Bavaria was patron of which composer = wagner Which Pacific republic was formerly known as the New Hebrides = vanuatu Which musical instrument has a mouthpiece called a 'fipple' = recorder Which palace was built by Henry VIII on the site of a leper hospital = saint james's Worn in ancient Greece what was a 'petasus' = hat What does 'll Trovatore', as in the opera, mean = the troubador In which city would you find the Hancock Tower and Filene's bargain basement = boston, mass By what name is Jacopo Robusti better known = tintoretto Which two US Presidents shared 1924 as the year of their birth = george bush & jimmy carter Whose latest bestseller is entitled' look to Windward' = lain m banks Place the following musical terms in order of increasing speed Lento, Largo, Andante, Allegro and Presto = lento, largo, andante, allegro and presto Which Gilbert and Sullivan Opera is subtitled The Slave of Duty = pirates of penzance Where would you find the volcano loki = on jupiter's moon i0 What is the opening line of the Star Spangled Banner = oh, say can you see by the dawn's early light What was Winston Churchill's only novel = savrola Westminster Abbey was originally a monastery of which order of monks = benedictines Which word can precede the following; -count -bath -bank = blood Where would you find tricounis, clinkers and muggers = on the soles of old hiking boots Written by Richard Ford, and made into a box office smash at the cinema, which book won the 1996 Pulitzer Prize for Fiction = independence day Which German City is the home to the Becks brewery = bremen What was set up in 1792 due to the treat of a French Invasion, and published its first official series in 1805 = ordnance survey What is meant by the cookery term ' A La Crecy' = served with carrots Madonna has had 8 number 1 's in the UK singles chart, yet her best selling single only reached number 3. Name it = like a virgin Which polar explorer died in a plane crash, while searching for a missing Italian airship = ronald amundsen Known as 'England's Darling' who led a resistance against William the Conqueror, on the Isle of Ely in 1070 = hereward the wake Which beer was first brewed in Leuven in 1366 = stella artois Which American author wrote the million pound sellers 'The Witches of Eastwick' and 'The Poorhouse Fair' = john updike What was the name of the company founded by Prince Rupert in 1670 to trade furs with the North American Indians = hudson bay company Who succeeded U Thant as Secretary General of the United Nations in 1972 = kurt waldheim Which John Wayne film was a hit for Ultravox in 1982 = reap the wild wind At which sport did Yvonne McGregor win a bronze medal for Great Britain at the Sydney Olympics = cycling Which fictional character first appeared in 194 i, when she came to America from her Amazon homeland of Paradise Island to help the US defeat the Nazis = diana prince = wonder woman In which US City is the TV programme' Ali McBeal' based = boston Two islands are situated in San Francisco harbour. One is Alcatraz, name the other = angel island Which comic book hero, with an alter ego of Steve Rogers, did Joe Simon and Jack Kirby create = captain america Which railway station in London, designed by Edward Wilson and opened in 1874, serves East Anglia = :liverpool street From which London jail did Ronnie Biggs escape in 1965 = wandsworth Harrison Ford's performance in which film earned him his only Best Actor nomination = witness In the Bible, who is recorded in' Acts' as purchasing a plot of land known as Potter's Field = judas iscariot Which British animal has the scientific name Meles Meles = badger Who led the revolt against Henry III during the Baron Wars of 1264 to 1267 = simon de montfort Prior to their 1999 UK number 1 hit 'Maria', which record was Blondie's last number 1 in the UK singles chart = the tide is high Which band were named after a 1950 film starring Bette Davis, Anne Baxter, George Sanders and Celeste Holm = all about eve Which mythical land was supposed to lie between Cornwall and the Scilly Isles = l yonesse Which car manufacturer takes its name from the Latin for 'I roll' = volvo The battlefield of Austerlitz is in which modern day country = czech republic In which Isle of Wight town would you find the History of Smuggling Museum = ventnor Mentioned in many westerns, which Texan city, situated at the base of the Franklin Mountains, stands on the Rio Grande = el paso The roadrunner, found in the deserts of North America, is a member of which bird family = cuckooo In film, collectively, how were Gabriel Byme, Stephen Baldwin, Benico Del Toro, Kevin Pollock and Kevin Spacey known in 1995 = usual suspects What creature is the subject of Roald Dahl's children's book Esio Trot = tortoise Name the year. South African police kill 56 people in Shatpeville, Israeli intelligence agents capture Adolf Eichmann, and Nigeria gains its independence. = 1960 A bottle of wine originating from the Mondavi coastal vineyard is produced in which country = u s a Which British animal has the scientific name Lutra Lutra = otter In children's literature what was the name that appeared over the door of 'Winnie the Pooh's house = mr sanders During the time of the Roman occupation it was known as Lutetia. How is it known today = paris Name the year. Walt Disney dies, Hastings Banda becomes Malawi's first President, and Nelson's pillar in O'Connell Street, Dublin is blown up = 1966 The Romans called it Venta Belgarum. What is it called today = winchester According to the song, in which city was 'The House of the Rising Sun' = new orleans A bottle of wine originating from the 'Nobilo' vineyards, is produced in which country = new zealand In the Old Testament, who consulted the Witch of Endor = king saul Which one word links a Scottish river, the captain of the New Zealand Rugby Union team, and a BBC comedy programme = blackadder In which battle of 1571 did the combined Spanish and Venetian forces defeat the Turkish navy = lepanto Which is the largest member of the kingfisher family = kookaburra Which one word connects Illinois, Mary O'Brien and 'The Simpsons' = springfield In Greek mythology, who turned Odysseus's men into pigs = circe In which battle of 1709 did the allied English army under Marlborough defeat French force led by Marshal Villars = malplaquet The pop star 'Moby' is the great-grandson ofwhich famous author = herman mel ville Which Marx Brothers film features a bunch of Nazis trying to make off with stolen treasure = a night in casablanca What term can describe a cheap disreputable nightclub, and a style of ragtime piano playing = honky tonk 76. The first UK Top 10 chart appeared in November 1952 when A1 Martino was No 1 with 'Here in my Heart'. Who had three songs in that first top 10 chart = vera lynn Which fishing port in California was the setting for Steinbeck's novels 'Cannery Row' and 'Tortilla Flat' = monterrey Fu Manchu was the adversary of which Scotland Yard detective = nayland smith What was the name given to the 3rd Indian Division that operated under the command of Brigadier Wingate during World War II = chindits Which gangster was shot dead by the FBI on 22 July 1934 as he came out of the Biograph cinema in Chicago = john dillinger Prior to David Coulthard this year, who was the last British driver to win the Monaco Grand Prix = jackie stewart Bill Clinton is the first Democrat to be elected to two terms in office since which American President = franklin d roosevelt Which Turkish town was the birthplace of St Paul = tarsus What kind of animal is a civet = cat Outside which building in London was Charles I executed = the banqueting house What is the fruit of the 'Rubus fructicosus' = blackberry What is the currency of Israel = shekel Which country has the e-mail address '.ie' = republic of ireland Who wrote the opera Rodelinda = handel Which saint's emblem is two breasts on a dish = st agatha Who carried the Union Jack at the opening ceremony in the Sydney Olympics = matthew pinsent In which South/Central American country is the active volcano Arenal = costa rica Whose debut album is named Hot = melanie b Which bird family are the Anserinae = geese Which Shakespeare play has only two female characters, Portia and Calpurnia = julius caesar What is the common name of the plant Myosotis = forget me not Where was the first beachhead established in the Falklands' War = san carlos Who wrote From this Moment On = cole porter What shape is the pasta conciglie = shell-shaped The word ghetto is derived from the area in Venice where the Jews were confined. What does ghetto mean in Venetian dialect = foundry In which opera do Ping, Pang and Pong appear = turandot What can be described as a Flemish Bond = brickwork Who in the 60s produced the 'Space Age' collection = pierre cardin Who invented the World Wide Web = tim berners-lee In which part of Britain did Boadicea's Iceni live = east anglia An emmet is which type of a creature in old language = ant In which city is the Grosvenor Museum situated = chester What is the title of Geri Halliwell's recent book = if only = Which London house was the home of the Duke Of Wellington. It was also known as No. 1 London. = apsley house What kind of animal is a peccary = pig What is the fruit of the Prunus Armeniaca = apricot What is the currency of Holland = guilder What country has the e-mail address '.za' = south africa Who wrote the opera Fortinbras = schubert Which saint's emblem is two eyes in a dish = st lucy Who lit the Olympic flame at Sydney = cathy freeman In which South/Central American country is the active volcano Cotopaxi = ecuador Whose recent album is called Painting It Red = beautiful south Who discovered the cholera micro-organism = koch Whose last words were: 'Die, my dear Doctor, that is the last thing I shall do!' = lord palmerston Which bird family are the Columbidae = doves and pigeons Which Shakespeare play has female characters, Bianca and Katherine = the taming of the shrew What is the common name of the plant Nigella = love in a mist Name the British nuclear submarine that sank the General Belgrano = hms conqueror Who wrote Mountain Greenery = rodgers and hart What shape is the pasta Farfalle = butterfly-shaped Which two of New York's boroughs are actually islands = manhattan & staten island What colour is the left hand circle of the Olympic rings = blue For which state was Al Gore a senator = tennessee Which knight of the Round Table fought the Green Knight = sir gawain In which musical is the song Day by Day = godspell In which country is Bukhara = usbekistan In which palace was Queen Elizabeth I born = greenwich What is the name of the straits that separate Sicily from Italy = messina According to the Old Testament, who was the wife of Abraham and mother of Isaac = sarah Who is the new President of Yugoslavia = vojislav kostunica Which eponymous animal in a children's story had friends called Merrylegs and Ginger = black beauty Don John of Austria, the victorious admiral at Lepanto, was the illegitimate son of which Holy Roman Emperor = charles v If a dish is described as "a la forestiere" which ingredient must it contain = mushrooms Name Dolly Parton's Smokey Mountain theme park = dollywood Name the fourth book of the Old Testament = numbers What type of fruit are "Lancashire Lad" & "Langley Gage" = gooseberries In the key of D major, which note is "Me" = f sharp What is the capital of Slovenia = ljubljana What is the main grape used in Chianti = sangiovese What structure consisting of many blood cells, feeds the foetus during pregnancy = placenta Which inert gas is a fission product of uranium nuclear reactors = xenon Mexico shares a southern border with Guatamala and which other country = belize Who makes the last speech in Shakespeare's "Macbeth" = malcolm Traditionally what anniversary is your 55th = emerald What car did Caractacus Potts drive = chitty chitty bang bang Which European capital city has a bridge called the "Halfpenny Bridge" = dublin What is the name of the staff carried by a bishop = crosier Which British town did the Romans call DUBRIS = dover Who composed "Ein Heldenleben" = richard strauss Which Gilbert & Sullivan operetta contains the aria "The moon and I" = mikado How many BTU's in a therm = 100,000 The love story of which writer is portrayed in the film ""Shadowlands" = c s lewis Which two cities are linked by the M8 = glasgow & edinburgh Which South American national flag consists of nine blue and white horizontal stripes with a golden sun in the top left corner = uruguay Name 10 CC's first UK No 1 = rubber bullets Who painted "Van Gogh painting sunflowers" = paul gauguin What was the then title of the man who married Elizabeth Bowes-Lyon in 1923 = duke of york The common garden flower TAGETES is a variety of which popular plant = marigold Ramon Mercador assassinated whom = leon trotsky Who makes the last speech in Shakespeare's "Julius Caesar" = octavian Which Roman fortification ran from the Firth of Clyde to the Firth of Forth = antonine wall Which Gilbert & Sullivan operetta contains the aria "Oh, Foolish Fay" = iolanthe What is the capital of Macedonia = skopje Why was Naga Jolokia in the news in August this year = the hottest chilli known A dish described as "Lyonnaise" has which ingredient = onions Don Carlos, subject of an opera by Verdi, was the half brother of which king of Spain = philip iii What name is given to the small sac or cavity from which a hair grows = follicle Which father and daughter starred in the film "Tiger Bay" = john & hayley mills Which European city has a bridge called the "Donkey Bridge" = amsterdam Name the fifth largest of the Channel Islands. = herm The Timor sea and the Arafura sea are off the north coast of which country = australia By what name was Robert Stroud better known = birdman of alcatraz Which nation recently won the Alfred Dunhill Cup at St. Andrews = spain What name is given to the cap worn by a cardinal = biretta Which birth sign precedes Cancer = gemini Who composed "Emilia de Liverpool" = donizetti What is the key signature of E flat major = 3 flats How many inches in a cubit = eighteen = 18 In country music, who is the "Coalminer's Daughter" = loretta lynn What type of fruit are "Malling Jet" & "Wellington XXX" = blackcurrants What is the main grape used in Barolo and Barbaresco = nebbiolo What food did Tigga in Winnie the Pooh finally find he liked = extract of malt Who is the second highest wicket taker in Test Cricket history = kapil dev In which American state is the Three Mile Island nuclear reactor = pennysilvania Which group's first album was called "Piper at the Gates of Dawn" = pink floyd To which instrument does an orchestra normally tune = oboe How precisely is the bird CYGNUS CYGNUS better known = whooper swan Who in 1497 established the first Portuguese colony in India = vasco da gama In Greek myth how were the children of Uranus & Gaea collectively known = the titans Who wrote in 'Sonnets from the Portuguese "How do I love thee = let me count the ways."' = elizabeth browning Which German city was the target of the first 'thousand bomber raid = cologne Which battle took place in Leicestershire in 1485 = bosworth field Oberon is a moon of which planet = uranus Who was the husband of Jennie Van Westphalen = karl marx On which Spanish costa would you find the resort of Alicante = costa blanca Which king was known as 'The Hammer of the Scots' = edward i Who wrote "The Nine Tailors" = dorothy l sayers Which element, atomic number 74, takes its name from the Swedish for Heavy Stone = tungsten Which holiday company is currently using the song "The Time of My Life" from "Dirty Dancing" to advertise its holidays = sandals Fort Baxter, Kansas was the fictional setting for which TV series = seargant bilko = phil silvers show As at 29 October 2000 who is the Prime Minister of Canada = jean chretien Which part of the body is affected by the condition stomatitis = mouth In which town does the comic hero Desperate Dan live = cactusville Which crime fiction writer's titles have an alphabetical theme. e.g A is for Alibi and E is for evidence. = sue grafton In the Bible, who sold his birthright for a mess of potage = esau A radian is a unit used to measure what = angle As at 29 October 2000 who is the Prime Minister of Australia = john howard Which film role links Mary Elizabeth Mastrantonio, Olivia de Havilland and Audrey Hepburn = maid marian Which crime fiction writer's titles have a numerical theme. e.g. 1 for the money, 2 for the Dough = janet evanovich Name the English king who met King Francis 1st of France on the Field of the Cloth of Gold. = henry vii Circle, Parabola and Hyperbola are three 3 of the four curves known as the conic sections. Name the fourth. = ellipse In which U.S. state is Fort Knox = kentucky In the Bible, who built the Golden Calf for the Israelites to worship = aaron Which part of the body is affected by the condition nephritis = kidneys Which element, atomic number 55, takes its name from the Greek for heavy = barium Who wrote "The Four Just Men" = edgar wallace Charter 77 was an association of individuals devoted to the assertion of lost human rights in which European country = czechoslovakia Which internet company is currently using the song "Puppy Love" to advertise its search engine = lycos Which film and TV role links, Leslie Howard, David Niven, Barry K Barnes and Anthony Andrews = the scarlet pimpernel Which European city is served by Cointrin airport = geneva Where would you find the only freshwater seals in the world = lake baikal What is the common name of the herb Melissa Officanalis = lemon balm The Beach Boys had two British number one hits, "Good Vibrations" and which other = do it again Who painted " Girl with Red Scarf and Black Trousers" = l s lowry In which county is the National Trust property "Cotehele" = cornwall Whose face was described as being "like a woollen mat" = noel coward Name the seaport of Tokyo = yokohama The prefix 'thio' in chemical compounds indicates the presence of which element = sulphur Which actress appeared in "A Handful of Dust", "The English Patient" and "The Horse Whisperer" = kirstin scott thomas Who designed the Palace of Westminster = sir charles barry According to the Beatles, how many holes are there in Blackburn = 4000 Whose recent biography is entitled "Out of the Frying Pan" = keith floyd Which Planet is 228 million kilometres from the Sun and has an orbital time of 1.88 years = mars The English translation is "Ocean of Wisdom". How do we know the more common original = dalai lama What was the pen-name of Eric Arthur Blair = george orwell Who painted Six Bells, Abertillery, Rhondda = l s lowry What does the SI prefix 'pico' mean = one millionth millionth In which county is the National Trust property "Arlington Court" = devon The United Nations building in New York was designed by whom = le corbusier Which actor appeared in "Pulp Fiction", Reservoir Dogs" and "The Piano" = harvey keitel According to the Beatles, upon what will there be a show for the benefit of Mr. Kite = trampoline What is the name of the seaport of Perth, Western Australia = freemantle An English translation is "Wind and Water". How do we know the more common original = feng shui Which Planet is 2872 million kilometres from the Sun and has an orbital time of 84 years = uranus Whose recent biography is entitled "Life in the Jungle" = michael hezeltine Who was the last Danish king of England = hardicanute Who directed John Wayne's last film "The Shootist" = don siegel In what year was Joan of Arc burned at the stake = 1431 With what branch of the arts is Rudolf Von Laban associated = dance = ballet Who won this year's Nobel peace prize = kim dae jung Who was known as "The King of Swing" = benny goodman In Greek mythology who was the wife of Orpheus, the subject of the earliest surviving opera by Peri = eurydice Name The Year: Pres. Reagan shot and wounded by John W. Hinckley Jr. = 1981 Name The Year: Third Space Shuttle Mission - Columbia 3 returns to Earth. = 1982 Name The Year: U.S. ends participation in multinational peace force in Lebanon. = 1984 Name The Year: Portugal regains independence after 60 years of Spanish rule. = 1640 Name The Year: Charles and M.N. Roberts ascend 2,000' in a hydrogen balloon. = 1783 Name The Year: Santo Domingo (Dominican Rep) proclaims independence from Spain. = 1821 Name The Year: Dom Pedro crowned emperor of Brazil. = 1822 Name The Year: Sino-Portuguese treaty recognizes Portugal's control of Macao. = 1887 Name The Year: Continuous moving assembly line introduced by Ford (1 car every 2.5 minutes). = 1913 Name The Year: Iceland becomes independent state under the Danish crown. = 1918 Name The Year: Lady Astor sworn-in as 1st female member of British Parliament. = 1919 Name The Year: BINGO invented by Edwin S Lowe. = 1929 Name The Year: Sergei Kirov Josef Stalin's collaborator, assassinated in Leningrad. = 1934 Name The Year: Woody Allen [Allen Stuart Konigsberg] (Zelig, Annie Hall, Manhattan Murder Mystery), born. = 1935 Name The Year: Lee Trevino, golfer (US Open 1968,71), born. = 1939 Name The Year: Bell Labs tests coaxial cable for TV use. = 1936 Name The Year: Richard Pryor, comedian/actor (Lady Sings the Blues, Stir Crazy), born. = 1940 Name The Year: Bette Midler, singer/actress (Do You Want to Dance?), born. = 1945 Name The Year: Aleister Crowley, occultist dies at 74. = 1947 Name The Year: Central African Rep made autonomous member of French Commonwealth (Nat'l Day). = 1958 Name The Year: 12 nations sign a treaty for scientific peaceful use of Antarctica. = 1959 Name The Year: 1st color photograph of Earth from outer space. = 1959 Name The Year: Australia grants self-government to Papua New Guinea. = 1973 Name The Year: David Ben-Gurion founding father of Israel dies in Tel Aviv at 87. = 1973 Name The Year: Digging begins to link England and France under the English Channel. = 1987 Name The Year: Benazir Bhutto named 1st female PM of a Moslem country (Pakistan). = 1988 Name The Year: East Germany drops the communist monopoly from its constitution. = 1989 Name The Year: USSR Pres Mikhail S Gorbachev meets Pope John Paul II at the Vatican. = 1989 Name The Year: British and French workers meet in English Channel's tunnel (Chunnel). = 1990 Name The Year: Iraq accepts Bush's offer for talks. = 1990 Name The Year: Lithauania, Estonia and Latvia hold their 1st joint session. = 1990 Name The Year: Inaugural AIDS awareness day. = 1991 Name The Year: Ukranian people vote for independence. = 1991 Name The Year: Napoleon becomes 1st French emperor, placing crown on his own head. = 1804 Name The Year: Napoleon defeats Russians & Austrians at Austerlitz. = 1805 Name The Year: Franz Josef I becomes emperor of Austria and King of Hungary. = 1848 Name The Year: 2nd French empire established; Louis Napoleon becomes emperor. = 1852 Name The Year: Georges Seurat France, painter/pointillist (Grande Jatte), born. = 1859 Name The Year: Peter Carl Goldmark, developed color TV & LP records, born. = 1906 Name The Year: The opera "Samson et Dalila" is produced (Rouen). = 1877 Name The Year: Gillette patents 1st disposable razor. = 1901 Name The Year: Randolph Hearst newspaper publisher, born. = 1915 Name The Year: 1st controlled nuclear chain reaction (University of Chicago). = 1942 Name The Year: Benelux treaty signed by Belgium, Netherlands & Luxembourg. = 1958 Name The Year: Fidel Castro declares he's a Marxist, & will lead Cuba to Communism. = 1961 Name The Year: Soviet Mars 3 is 1st to soft land on Mars. = 1971 Name The Year: Abu Dhabi, Ajman, Dubai, Fujeira, Sharjah & Umm ak Qiwain form the United Arab Emirates. = 1971 Name The Year: Monica Seles tennis star (French Open-1990), born. = 1973 Name The Year: Laos People's Democratic Republic founded (National Day). = 1975 Name The Year: 1st permanent artificial heart successfully implanted (U of Utah) in retired dentist Barney Clark; lived 112 days with the Jarvic-7 heart. = 1982 Name The Year: Desi Arnaz actor (Ricky Ricardo-I Love Lucy), dies at 69. = 1986 Name The Year: "Naked Gun" premieres, a movie based on TV's "Police Squad". = 1988 Name The Year: 1st parlimentary election in newly reunified Germany. = 1990 Name The Year: Charles VI [the Well-Beloved], king of France (1380-1422), born. = 1368 Name The Year: Sir Thomas Herriot introduces potatoes to England, from Colombia. = 1586 Name The Year: Galileo perfects the telescope. = 1621 Name The Year: Edmund Halley receives MA from Queen's College, Oxford. = 1678 Name The Year: Ludvig Baron Holberg, a founder of Danish & Norwegian literature, born. = 1684 Name The Year: Sir Rowland Hill introduced 1st adhesive postage stamp (1840). = 1795 Name The Year: John Backus, inventor (FORTRAN computer language), born. = 1924 Name The Year: Tennessee Williams play "A Streetcar Named Desire", opens. = 1947 Name The Year: Ozzy Osbourne heavy metal musician, born. = 1948 Name The Year: Mel Smith author/comedian, born. = 1952 Name The Year: 1st human heart transplant performed (Dr Christian Barnard, S Africa). = 1967 Name The Year: Pioneer 10 passes Jupiter (1st fly-by of an outer planet). = 1973 Name The Year: 2,000 die from Union Carbide poison gas emission in Bhopal, India. = 1984 Name The Year: A new war government takes office in France. = 1793 Name The Year: The Bulgarian Government adopts a Communist Constitution. = 1947 Name The Year: (AUSTRIA) Wolfgang Amadeus Mozart, composer, dies aged 37. = 1791 Name The Year: (USA) Walt Disney born. = 1901 Name The Year: (FRANCE) Claude Monet, imperssionist painter, dies. = 1926 Name The Year: (USA) Ira Gershwin, composer, born. = 1896 Name The Year: NASA launch Pioneer III. = 1958 Name The Year: The Roman politician, Cicero, is slain. = 43BC Name The Year: USA declares war on the Prussian-Hungarian forces, joining World War One on the side of the Allies. = 1917 Name The Year: Japanese bomb Pearl Harbour. The USA join Allies in fighting the Axis forces soon after. = 1941 Name The Year: (USA) Rube Goldberg, inventor and cartoonist, dies. = 1971 Name The Year: Weekly airmail route starts between Australia and the United Kingdom. = 1934 Name The Year: The Allies, along with Free France declare war on Japan. = 1941 Name The Year: Saidi, great Persian poet (Orchard, Rose Garden) dies. = 1292 Name The Year: Gustavus II Adolphus, king who made Sweden a major power (1611-32) = 1594 Name The Year: John Milton, poet and puritan (Paradise Lost) born. = 1608 Name The Year: Emmett Kelly, circus clown (Weary Willie) born. = 1898 Name The Year: Margaret Hamilton, actor (Wizard of Oz -- Wicked Witch of the West) born. = 1902 Name The Year: Douglas Fairbanks, Jr, actor (Ghost Story) born. = 1909 Name The Year: Kirk Douglas, actor (Gunfight at the OK Corral, 7 Days in May) = 1918 Name The Year: Redd Foxx, actor (Sanford and Son), comedian, born. = 1922 Name The Year: Robert J.L. Hawke (Labor), prime minister of Australia (1983-1991) born. = 1929 Name The Year: Donny Osmond, singer born. = 1957 Name The Year: UN Gen Assembly unanimously approves Convention on Genocide. = 1948 Name The Year: Tanganyika gains independence from Britain. = 1961 Name The Year: Pioneer Venus 2 drops 5 probes into atmosphere of Venus. = 1978 Name The Year: Martin Luther publicly burned papal edict demanding that he recant. = 1520 Name The Year: Thomas H. Gallaudet, pioneer of educating the deaf, born. = 1787 Name The Year: Cesar Franck (in Belgium), composer, born. = 1822 Name The Year: George Macdonald, Scottish novelist (Lilith), born. = 1824 Name The Year: Emily Dickinson (in Amherst, Mass), poet, born. = 1830 Name The Year: Melvil Dewey, created the Dewey Decimal System for libraries = 1851 Name The Year: Alfred Nobel dies; Swedish Nobel Prize ceremony on this date. = 1896 Name The Year: Spanish-American War ends -- U.S. acquires Guam, Puerto Rico, the Phillipines, and Cuba from Spain. = 1898 Name The Year: First Nobel Peace Prizes (to Jean Henri Dunant, Frederic Passy). = 1901 Name The Year: Pres. Theodore Roosevelt (first American) awarded Nobel Peace Prize. = 1906 Name The Year: Dorothy Lamour (in New Orleans, LA), actor (Road to Bali), born. = 1914 Name The Year: UN General Assembly adopts Universal Declaration on Human Rights. = 1948 Name The Year: First Human Rights Day. = 1949 Name The Year: Susan Dey, actor (First Love, LA Law), born. = 1952 Name The Year: Nia Peeples born. = 1961 Name The Year: Zanzibar gains independence from Britain. = 1963 Name The Year: Israeli hmuel Yosef Agnon wins Nobel Prize for literature. = 1966 Name The Year: Singer Otis Redding dies in plane crash at 26. = 1967 Name The Year: Soyuz 26 carries 2 cosmonauts to Salyut 6 space station. = 1977 Name The Year: Soyuz T-5 returns to Earth, 211 days after take-off. = 1982 Name The Year: South African Bishop Desmond Tutu received his Nobel Peace Prize. = 1984 Name The Year: (US) Gugliemo Marconi sends the first wireless message across the Atlantic. = 1911 Name The Year: (Ireland) Martial Law Declared. = 1920 Name The Year: A general election results in a hung Parliament in the UK. James MacDonald, of the UK Labor Party wins the follow-up election next year. = 1923 Name The Year: (UK) King George V crowned Emperor of India and founds New Dehli to replace Calcutta as the capital. = 1911 Name The Year: (Germany) Hugo Junkers completes the first all metal airplane. = 1915 Name The Year: The first "motel" opens in California's San Luis Obispo. = 1925 Name The Year: Kenyan Independance Day. = 1964 Name The Year: US, Japan, UK & France sign a treaty aimed at controlling naval build-up in the Pacific Ocean. = 1921 Name The Year: Lebanon and Syria are made independent. = 1945 Name The Year: Amundsen of Norway reaches the south pole. = 1911 Name The Year: Women get the vote in Turkey = 1934 Name The Year: Portugese President Sidonio Paes is assassinated. = 1918 Name The Year: (Geneva, Switzerland) China and Austria added to Leauge of Nations = 1920 Name The Year: Catherine of Aragon, first wife of Henry VIII, born. = 1485 Name The Year: English Parliament adopts Bill of Rights after Cromwell's Revolution. = 1689 Name The Year: Ludwig von Beethoven (in Bonn, Germany), composer, born. = 1770 Name The Year: Big Tea Party in Boston Harbor. Indians welcome. British unwelcome. = 1773 Name The Year: Jane Austen (in England), novelist (Pride and Prejudice), born. = 1775 Name The Year: George Santayana, philosopher, poet, humanist, born. = 1863 Name The Year: Anton Dvorak's "New World Symphony" premieres. = 1893 Name The Year: Sir Noel Coward, playwright, born. = 1899 Name The Year: "Variety", covering all phases of show business, first published. = 1905 Name The Year: South Pole first reached, by Norwegian Roald Amundsen. = 1911 Name The Year: Arthur C. Clarke, science-fiction author (2001, Childhood's End), born. = 1917 Name The Year: Liv Ullman (in Tokyo, Japan), actor (Cries and Whispers), born. = 1939 Name The Year: Battle of the Bulge begins. = 1944 Name The Year: Gemini VI returns to Earth. = 1965 Name The Year: Pope Paul III excommunicated England's King Henry VIII. = 1538 Name The Year: France recognizes the independance of the 13 US colonies. = 1777 Name The Year: Sir Humphry Davy, discovered several chemical elements = 1778 Name The Year: Aztec calendar stone discovered in Mexico City. = 1790 Name The Year: New York City traffic regulation creates the first one-way street. = 1791 Name The Year: "A Christmas Carol" by Charles Dickens published in London. = 1843 Name The Year: Violent bread riots at Montreal. = 1875 Name The Year: At 10:35AM, for 12 seconds, first sustained motorized aircraft flight (36 meters) by Wright Brothers at Kitty Hawk, NC. = 1903 Name The Year: Willard Frank Libby, invented carbon-14 "atomic clock", born. = 1908 Name The Year: Japan receives League of Nations mandate over Pacific islands. = 1920 Name The Year: First movie opening simultaneously in major cities "On The Beach". = 1959 Name The Year: Budweiser rocket car reaches 1190 kph (record for wheeled vehicle). = 1979 Name The Year: Charles Wesley, cofounder of Methodist movement = 1707 Name The Year: Elizabeth, empress of Russia (to Peter the Great and Catherine I) = 1709 Name The Year: Antonio Stradivari, violin maker, dies. = 1737 Name The Year: Joseph Grimaldi, English pantomimist, "greatest clown in history" = 1779 Name The Year: William Bond obtains first photograph of moon through a telescope. = 1849 Name The Year: Paul Klee, Swiss abstract painter = 1879 Name The Year: Edwin Armstrong, radio pioneer (invented FM) = 1890 Name The Year: Auto speed record set -- 63 kph. = 1898 Name The Year: George Grant wins patent for his golf tee. = 1899 Name The Year: Willy Brandt (SD), chancellor of West Germany (1969-74) (Nobel '71) = 1913 Name The Year: Betty Grable (in St. Louis, MO), actor = 1916 Name The Year: Mohammed Ali, boxer = 1942 Name The Year: Keith Richard, guitarist (Rolling Stones) = 1943 Name The Year: Uruguay joined the United Nations. = 1945 Name The Year: Steven Spielberg, director (Jaws, Raiders of the Lost Ark, etc.) = 1947 Name The Year: Japan admitted to the United Nations. = 1956 Name The Year: Niger gains autonomy within French Community (National Day). = 1958 Name The Year: India annexes Portuguese colonies of Goa, Damao and Diu. = 1961 Name The Year: Borman and Lovell Splash down in Atlantic. Ends two week Gemini VII flight. = 1965 Name The Year: The British parliament voted for the abolition of death penalty. = 1969 Name The Year: US began its heaviest bombing of North Vietnam. = 1972 Name The Year: Soyuz 13 launched into earth orbit for 8 days. = 1973 Name The Year: Soviet Premier Alexei Kosygin suffered a fatal heart attack at 76. = 1980 Name The Year: UN Security Council unanimously condemns 'acts of hostage-taking'. = 1985 Name The Year: Philip V, first Bourbon king of Spain (1700-46) = 1683 Name The Year: Robinson Crusoe leaves his island after 28 years (as per Defoe). = 1686 Name The Year: Benjamin Frankin begins publication of "Poor Richard's Almanack". = 1732 Name The Year: Vitus Bering, Dutch navigator and explorer, died. = 1741 Name The Year: Thomas Paine publishes his first 'American Crisis' essay. = 1776 Name The Year: Sir William Parry, British Arctic explorer = 1790 Name The Year: Mary Ashton Livermore, American reformer, women's suffrage leader = 1821 Name The Year: Leonid Brezhnev (in Ukraine), Soviet statesman, in the Ukraine = 1906 Name The Year: Jean Genet, French novelist and playwright (The Blacks) = 1910 Name The Year: British Broadcasting Corp. began transmitting overseas. = 1932 Name The Year: War broke out in Indochina as Ho Chi Minh attacked the French. = 1946 Name The Year: Jennifer Beals (in Chicago), actor (Flashdance) = 1963 Name The Year: Alyassa Milano, actor (Who's the Boss) = 1972 Name The Year: Apollo 17 returns to Earth. = 1972 Name The Year: USSR frees dissident Andrei Sakharov from internal exile. = 1986 Name The Year: Russian calendar adjusted so Russian New Year occurs on January 1st, instead of September 1st. = 1699 Name The Year: Turkey cedes the island of Crete to the Egyptians. = 1832 Name The Year: Assorted Russian republics form together to found the Union of Soviet Socialist Republics. = 1922 Name The Year: (USA) John Steinbeck, author, died. = 1968 Name The Year: Five year truce between England and Scotland began. = 1491 Name The Year: Benjamin Disraeli, British Statesman, born. = 1804 Name The Year: Josef Stalin, Russian dictator, born. = 1879 Name The Year: (England) F. Scott Fitzgerald, author, died. = 1940 Name The Year: Ecuador and Iraq join the United Nations. = 1945 Name The Year: N.A.S.A. launch Apollo 8 to fly around the moon. = 1968 Name The Year: Dominican Order of Friars confirmed by the Pope. = 1216 Name The Year: J. Arthur Rank, USA filmmaker, born. = 1888 Name The Year: Menelik II, Emperor of Ethiopia, died. = 1913 Name The Year: Coelacanth, a fish thought to have been extinct for 65 million years caught of the coast of South Africa. = 1938 Name The Year: St. Philip of Moscow martyred by Ivan the Terrible. = 1569 Name The Year: (Italy) Giovanni Cassini discovers Rhea, a satellite of Saturn. = 1672 Name The Year: (England) John Flamsteed observes Uranus but doesn't realize it's undiscovered. = 1690 Name The Year: (Scotland) Richard Arkwright, invented spinning frame. = 1732 Name The Year: Alexander I, tsar of Russia (1801-25), born. = 1777 Name The Year: George Washington resigned as US Army's commander-in-chief. = 1783 Name The Year: Jean-Francois Champollion, deciphered Egyptian hieroglyphics. = 1790 Name The Year: (USA) Joesph Smith, founder of the Mormon church, born. = 1805 Name The Year: Joseph Hansom of London receives patent for Hansom cabs. = 1834 Name The Year: (Italy) Jose Greco, dancer, born. = 1918 Name The Year: Ireland divided into two parts, each with its own parliament. = 1920 Name The Year: (USA) Transistor invented by Bardeen, Brattain and Shockley in Bell Labs. = 1947 Name The Year: PM Tojo, 6 other Japanese hung for war crimes by US. = 1948 Name The Year: Borman, Lovell and Anders first men to orbit moon. = 1968 Name The Year: Earthquake destroys central Managua, Nicaragua. = 1972 Name The Year: Six Persian Gulf nations double their oil prices. = 1973 Name The Year: (USA) Rutan and Yeager make first around-the-world flight without refueling. = 1986 Name The Year: Vasco da Gama Portuguese navigator dies in Cochin, India. = 1524 Name The Year: "Silent Night" composed by Franz Joseph Gruber; 1st sung next day. = 1818 Name The Year: James Prescott Joule, physicist, born. = 1818 Name The Year: Giusseppi Verdi's "Aida" premieres in Cairo, at Suez canal opening. = 1871 Name The Year: Bicycle with a back-pedal brake patented. = 1889 Name The Year: Howard Hughes, reclusive billionaire/inventor, born. = 1905 Name The Year: Fritz Leiber, writer, born. = 1910 Name The Year: Anwar El Sadat, president of Egypt, born. = 1918 Name The Year: Ava Gardner, actress, born. = 1922 Name The Year: Albania becomes a republic. = 1924 Name The Year: FDR appoints Gen Eisenhower supreme commander of Allied forces. = 1943 Name The Year: 4th French republic established. = 1946 Name The Year: United Kingdom of Libya gains independence from Italy via the UN. = 1951 Name The Year: Laos gains its independence. = 1954 Name The Year: Shooting begins on "The Cage" the pilot for Star Trek. = 1964 Name The Year: Saddam says Israel will be Iraq's 1st target. = 1990 Name The Year: Mikhail Gorbachev resigns as head of the Soviet Union. = 1991 Name The Year: 1st Spanish settlement in New World founded by Columbus = 1492 Name The Year: Charles Babbage, English inventor (calculating machine), born = 1792 Name The Year: Jimmy Connors, tennis player, born = 1952 Name The Year: Lars Ulrich, Danish/US heavy metal drummer (Metallica-Kill 'em All), born = 1963 Name The Year: Beatles release "I Want To Hold Your Hand/I Saw Her Standing There" = 1963 Name The Year: Harry Truman, 33rd US Pres (1945-53), dies in KC Mo at 88 = 1972 Name The Year: 2 Skylab 3 astronauts walk in space for a record 7 hours = 1973 Name The Year: (USA) Plane broke height altitude record at 11474 feet. = 1910 Name The Year: (England) Charles Babbage, mathematician and developer of the speedometer, born. = 1791 Name The Year: (USA) Coffee Percolator invented. = 1865 Name The Year: Louis Pasteur, French bacteriologist, born. = 1822 Name The Year: (France) Ether first used as an anasthetic in childbirth = 1845 Name The Year: (Germany) Marlene Dietrich, actress, born. = 1901 Name The Year: (France) Gerard Depardieu, actor, born. = 1948 Name The Year: Thomas Beckett, archbishop, assassinated by 4 knights of King Henry II = 1170 Name The Year: Bowling ball invented = 1862 Name The Year: Mary Tyler Moore, Brooklyn, actress (Mary Tyler Moore, Ordinary People), born = 1937 Name The Year: Jon Voight, Yonkers, NY, actor (Deliverance, Midnight Cowboy), born = 1938 Name The Year: Germany begins dropping incendiary bombs on London (WW II) = 1940 Name The Year: Ted Danson, SD Calif, actor (Sam Malone-Cheers, 3 Men & a Baby), born = 1947 Name The Year: Nikolai Andrianov, USSR, gymnist (Olympic-gold-1972, 76, 80), born = 1952 Name The Year: Star Trek's "Trouble With Tribbles" 1st airs = 1967 Name The Year: Andrei Tarkovski, Russian director (Stalker), dies at 54 = 1986 Name The Year: Frank Thring, Australian actor (El Cid, Ben-Hur), dies at 68 = 1994 Name The Year: Shane Warne takes a hat-trick v England at cricket MCG = 1994 Name The Year: (USA) Richard Clayderman, pianist, born. = 1953 Name The Year: (USA) Denzel Washington, actor, born. = 1954 Name The Year: (Messina, Italy) Earthquake (nearly 80,000 died). = 1908 Name The Year: Chinese troops crossed the 38th Parallel in Korea. = 1950 Name The Year: John Wycliffe English religious reformer, bible translator, dies. = 1384 Name The Year: (France) Henri Matisse, impressionist painter, born. = 1869 Name The Year: (USA) Edison gives 1st public demonstration of his incandescent lamp. = 1879 Name The Year: (USA) The opera "The Pirates of Penzance" is produced (NYC). = 1879 Name The Year: Ellis Island (NYC) opens as a US immigration depot. = 1890 Name The Year: Brooklyn's last day as a city, it incorporates into NYC (1/1/1898). = 1897 Name The Year: (USA) For the 1st time a ball drops at Times Square to signal the new year. = 1907 Name The Year: (Wales) Anthony Hopkins, actor, born. = 1937 Name The Year: (England) Ben Kingsley, Scarborough, actor, born. = 1943 Name The Year: (USA) John Denver singer, born. = 1943 Name The Year: (USA) Ratification of UN Charter completed. = 1945 Name The Year: (USA) President Truman officially proclaims end of WW-II. = 1946 Name The Year: (USA) 1st battery to convert radioactive energy to electrical announced. = 1951 Name The Year: (USA) Val Kilmer, actor, born. = 1959 Name The Year: (USA) 1st performance of the Beach Boys. = 1961 Name The Year: Katanga becomes part of the Democratic Republic of the Congo. = 1962 Name The Year: Donald Woods, a banned white editor flees South Africa. = 1977 Name The Year: Brunei gains complete independence from Britain. = 1983 Name The Year: Clark Gable, actor (didn't give a damn about Scarlet O'Hara) = 1901 Name The Year: The first armored car is introduced. = 1920 Name The Year: First telecast of atomic explosion. = 1951 Name The Year: First X-ray moving picture process demonstrated. = 1951 Name The Year: Rick James, musician = 1952 Name The Year: Princess Stephanie of Monaco = 1965 Name The Year: Lisa Marie Presley, actress = 1968 Name The Year: Sonny and Cher resumes on TV despite real life divorce. = 1976 Name The Year: Talleyrand, French statesman and diplomat, born. = 1754 Name The Year: First shipload of Chinese immigrants arrives in San Francisco, CA. = 1848 Name The Year: Mexico sells U.S. Texas, California, New Mexico and Arizona. = 1848 Name The Year: National Baseball League formed with 8 teams. = 1876 Name The Year: S.S. Strathleven arrives in London with first successful shipment of frozen mutton from Australia. = 1880 Name The Year: James Joyce, writer, born. = 1882 Name The Year: Lie detector first used in court at Portage, WI. = 1935 Name The Year: Farrah Fawcett-Major (in Texas), actress (Charlie's Angels), born. = 1947 Name The Year: Christie Brinkley, model, born. = 1954 Name The Year: 8 of the 9 planets aligned for the first time in 400 years. = 1962 Name The Year: "Late Night with David Letterman" premiers. = 1981 Name The Year: Horace Greeley, editor (told young men to go west), born. = 1811 Name The Year: World's first commercial cheese factory is established in Switzerland. = 1815 Name The Year: Gertrude Stein, writer, born. = 1874 Name The Year: Norman Rockwell, artist, born. = 1894 Name The Year: James Michener, writer, born. = 1907 Name The Year: Yalta Conference agreed that Russia would enter WWII against Japan. = 1945 Name The Year: Morgan Fairchild (in Dallas, TX), actress, born. = 1950 Name The Year: "Meet the Beatles" goes gold. = 1964 Name The Year: Luna 9, first lunar landing. = 1966 Name The Year: Tenth Space Shuttle Mission - Challenger 4 is launched. = 1984 Name The Year: J.W. Goodrich introduces rubber galoshes to the public. = 1824 Name The Year: At Montgomery, Alabama, the Confederate States are organized. = 1861 Name The Year: Charles A. Lindburgh, 'Lucky Lindy', born. = 1902 Name The Year: First Winter Olympics held at Lake Placid, NY. = 1932 Name The Year: First radioactive substance produced synthetically (radium E). = 1936 Name The Year: Roosevelt, Churchill, and Stalin began a conference at Yalta. = 1945 Name The Year: Dan Quayle, U.S. Vice-president (1989-1992), alleged twit, born. = 1947 Name The Year: Alice Cooper, musician, born. = 1948 Name The Year: Ceylon (now Sri Lanka) gains independence from Britain. = 1948 Name The Year: The Reshef - Israel's missile boat - is unveiled. = 1973 Name The Year: Abolhassan Bani-Sadr was installed as president of Iran. = 1980 Name The Year: Dwight Lyman Moody, evangelist, born. = 1837 Name The Year: John Boyd Dunlop, developed the pneumatic rubber tire, born. = 1840 Name The Year: Belle Starr, entertainer of the wild west, born. = 1848 Name The Year: Andre-Gustave Citroen, French automaker, born. = 1878 Name The Year: Giuseppe Verdi's opera "Otello" premiers. = 1887 Name The Year: Adlai E. Stevenson, statesman, born. = 1900 Name The Year: Red Buttons, comedian, actor, born. = 1919 Name The Year: N.Y. Yankees purchase 20 acres in the Bronx for Yankee Stadium. = 1921 Name The Year: FDR proposes enlarging Supreme Court, plan failed because critics feared it would allow "court packing" in his favor. = 1937 Name The Year: Charlotte Rampling (in England), actress, born. = 1946 Name The Year: Barbara Hershey (in Atlanta, Georgia), actress, born. = 1948 Name The Year: Bobby Brown, singer, married to Whitney Houston, born. = 1969 Name The Year: New Zealand Day = 1840 Name The Year: English Admiral Robert Fitzroy issues storm warnings for ships putting out to sea, creating the first "weather forecast." = 1861 Name The Year: George Herman 'Babe' Ruth, born. = 1895 Name The Year: Spanish-American War ends. = 1900 Name The Year: Ronald Reagan, 40th President (1981-1989), born. = 1911 Name The Year: Zsa Zsa Gabor (in Budapest), actress, ex-con, born. = 1919 Name The Year: Tom Brokaw, journalist, born. = 1940 Name The Year: Natalie Cole (in Los Angeles, CA), singer, born. = 1950 Name The Year: Elizabeth II becomes queen of Great Britain. = 1952 Name The Year: Jean-Claude "Papa Doc" Duvalier is forced to leave Haiti. = 1986 Name The Year: John Deere, pioneer manufacturer of agricultural implements, born. = 1804 Name The Year: Charles Dickens (in England), author, born. = 1812 Name The Year: Ballet introduced to the U.S. at Bowery Theatre, New York City. = 1827 Name The Year: Last bare knuckle champion John L. Sullivan KOs Paddy Ryan in Miss. = 1882 Name The Year: The assault on Anzio begins. = 1944 Name The Year: Garth Brooks, C&W singer, born. = 1962 Name The Year: Soyuz 24 is launched. = 1977 Name The Year: Mary Queen of Scots executed. = 1587 Name The Year: Jules Verne (in France), author who pioneered what later became science fiction, born. = 1828 Name The Year: Louis Waterman begins experiments that invent the fountain pen. = 1883 Name The Year: Chester F. Carlson, invented xerography, born. = 1906 Name The Year: Gypsy Rose Lee (in Seattle, WA), stripper, born. = 1914 Name The Year: Lana Turner (in Wallace, Idaho), actress, born. = 1920 Name The Year: Radio arrives in the White House. = 1922 Name The Year: Jack Lemmon, actor, born. = 1925 Name The Year: James Dean, actor (Rebel Without a Cause), born. = 1931 Name The Year: Nick Nolte, actor, born. = 1940 Name The Year: Mia Farrow (in Los Angeles, CA), actress, born. = 1945 Name The Year: Gary Coleman, actor, born. = 1968 Name The Year: Skylab 4's astronauts land. = 1974 Name The Year: Soyuz T-10 is launched. = 1984 Name The Year: Christian V became King of Denmark and Norway. = 1670 Name The Year: Russian cruisers Variag and Korietz sunk off Korean coast by Japanese. = 1904 Name The Year: (USA) Gypsy Rose Lee, stripper, born. = 1914 Name The Year: Treaty of Paris ends the French and Indian War. = 1763 Name The Year: Charles Lamb (in England), writer, born. = 1775 Name The Year: YWCA is founded in New York City. = 1870 Name The Year: Jimmy Durante, comedian, born. = 1893 Name The Year: Bertolt Brecht, born. = 1898 Name The Year: Robert Wagner, actor, born. = 1930 Name The Year: Roberta Flack (in North Carolina), singer, born. = 1940 Name The Year: Glenn Miller's "Chattanooga Choo Choo" goes gold. = 1942 Name The Year: Mark Spitz, swimmer, born. = 1950 Name The Year: Greg Norman, golfer, born. = 1955 Name The Year: Thomas Alva Edison (in New Jersey), inventor who lit up your life, born. = 1847 Name The Year: Major streets lit by coal gas for first time. = 1854 Name The Year: Eva Gabor (Budapest, Hungary), actress (Green Acres), born. = 1921 Name The Year: Vatican City (world's Smallest Country) is made an enclave of Rome. = 1929 Name The Year: Pumping begins to build Treasure Island, San Francisco. = 1936 Name The Year: Burt Reynolds, actor, born. = 1936 Name The Year: Sergio Mendes, artist, born. = 1941 Name The Year: Tenth Space Shuttle Mission - Challenger 4 returns to Earth. = 1984 Name The Year: Santiago, Chile founded. = 1541 Name The Year: Abraham Lincoln, 16th President (1861-1865), born. = 1802 Name The Year: Charles Darwin, evolutionist, born. = 1809 Name The Year: First New York to Paris auto race (via Alaska and Siberia) begins. George Schuster wins after 88 days behind the wheel. = 1908 Name The Year: Lorne Greene, actor (Bonanza, Battlestar Galactica), born. = 1915 Name The Year: Cornerstone laid for Lincoln Memorial in Washington, DC. = 1915 Name The Year: Gershwin's "Rhapsody In Blue" premieres in Carnegie Hall. = 1924 Name The Year: Maud Adams (in Sweden), born. = 1946 Name The Year: Joanna Kerns, actress, born. = 1953 Name The Year: "Blue Danube" waltz premiers in Vienna. = 1867 Name The Year: Emanuel Ungaro, fashion designer, born. = 1933 Name The Year: George Segal, actor, banjo player, born. = 1934 Name The Year: "Prince Valiant" comic strip appears; known for historical detail and fine detail drawing. Thanks Hal. = 1937 Name The Year: Stockard Channing (in New York), born. = 1944 Name The Year: Israel acquires 4 of the 7 dead sea scrolls = 1955 Name The Year: Alexander Graham Bell files an application for a patent for the telephone. = 1876 Name The Year: John Barrymore, actor, born. = 1882 Name The Year: Benjamin Kubelski (AKA Jack Benny), actor, comedian, born. = 1894 Name The Year: James Pike, clergyman, born. = 1913 Name The Year: Jimmy Hoffa, missing labor leader, born. = 1913 Name The Year: USSR calendar conversion. = 1918 Name The Year: St. Valentine's Day Massacre in Chicago, 7 gangsters killed by rivals disguised as cops (The Night Chicago Died). = 1929 Name The Year: Florence Henderson (in Indiana), actress (Alice in The Brady Bunch), born. = 1934 Name The Year: Gregory Hines, actor, dancer, born. = 1946 Name The Year: Element 103, lawrencium, first produced in Berkeley, California lab. = 1961 Name The Year: Mrs. Kennedy gives the TV audience a tour of the White House. = 1962 Name The Year: John and Yoko guest-host The Mike Douglas Show for the entire week. = 1972 Name The Year: Galileo Galilei, astronomer, born. = 1564 Name The Year: English standing army first founded. = 1645 Name The Year: Henry Engelhard Steinway, piano maker, born. = 1797 Name The Year: Susan B. Anthony, Woman's suffaregette, born. = 1820 Name The Year: John Barrymore, actor, born. = 1882 Name The Year: First Teddy Bear introduced in America. Made by Morris and Rose Michtom, Russian immigrants. = 1903 Name The Year: Cesar Romero, actor who played the Joker in the TV version of Batman, born. = 1907 Name The Year: Yuan Shik-K'ai elected the first President of the Republic of China. = 1912 Name The Year: Harvey Korman, actor, born. = 1927 Name The Year: Jane Seymour [Joyce Frankenberg], Middlesex England, actress (Dr Quinn, East of Eden, Lassiter), born. = 1951 Name The Year: Production announced-first pilot plant to produce man-made diamonds. = 1955 Name The Year: Li Hung Chang, chinese statesman, born. = 1823 Name The Year: Ladies Home Journal begins publication. = 1883 Name The Year: Robert Flaherty, father of the documentary film, born. = 1884 Name The Year: First airplane flight to Los Angeles from San Francisco. = 1914 Name The Year: Howard Carter finds the Pharoah Tutankhamun. = 1923 Name The Year: First radio-telescope news messages transmitted from U.S. to England. = 1923 Name The Year: Nylon patented by W. H. Carothers. = 1937 Name The Year: US forces land on Corregidor (complete conquest on March 3). = 1945 Name The Year: First commercially designed helicopter tested at Bridgeport, CT. = 1946 Name The Year: James Ingram. actor, born. = 1956 Name The Year: Fidel Castro became Premier of Cuba. = 1959 Name The Year: John McEnroe, tennis player, brat, born. = 1959 Name The Year: Andy Taylor, guitarist with Duran Duran, born. = 1961 Name The Year: First all solid propellant rocket put into orbit from Wallops Island, VA. = 1961 Name The Year: Beatles second appearance on Ed Sullivan. = 1964 Name The Year: Heathen religions banned in Poland. = 1387 Name The Year: Moliere, French Dramatist, died = 1673 Name The Year: Frederick Krupp, Arms manufacturer, born = 1854 Name The Year: Samuel S. McClure, organizer of the first news syndicate, born = 1857 Name The Year: Sardines were first canned, in Eastport, Maine. = 1876 Name The Year: First telephone exchange in San Francisco opens with 18 phones. = 1878 Name The Year: Geronimo, Apache Indian Chief, died = 1909 Name The Year: Battle of Eniwetok Atoll (U.S. victory on Feb 22). = 1944 Name The Year: St. Clare of Assissi declared patron saint of television. = 1958 Name The Year: Hal Holbrook, actor, born. = 1925 Name The Year: Alan Bates, actor, born. = 1934 Name The Year: Pat Morrow (in Los Angeles), actor (Peyton Place), born. = 1945 Name The Year: Lou Diamond Philips, actor, born. = 1962 Name The Year: Michael Jordan, gravity-defying hoopster, born. = 1963 Name The Year: Queen Mary I, first reigning queen of Great Britain, born. = 1516 Name The Year: Count Alessandro Giuseppe Antonio Anastasio Volta, invented the electric battery, born. = 1745 Name The Year: Louis Comfort Tiffany, glassmaker, born. = 1848 Name The Year: C.S.A. President Jefferson Davis is inaugurated at Montgomery, AL. = 1861 Name The Year: Andres Segovia, guitarist, born. = 1894 Name The Year: Jack Palance, actor (Believe It......Or Not), born. = 1920 Name The Year: Helen Gurly Brown in Portland Maine, publisher (Cosmopolitan), born. = 1922 Name The Year: Planet Pluto discovered by Clyde W. Tombaugh. = 1930 Name The Year: Yoko Ono (in Tokyo, Japan), singer, wife of John Lennon, born. = 1933 Name The Year: Kim Novak (in Chicago, Illinois), actress, born. = 1933 Name The Year: Golden Gate International Exposition opens on Treasure Island (built for the occasion) in San Francisco Bay. = 1939 Name The Year: Cybill Shepherd (in Memphis, TN), actress (Moonlighting, Sybil), born. = 1950 Name The Year: Premier of first 3-D feature film, "Bwana Devil", at NY city. = 1953 Name The Year: John Travolta, actor, born. = 1954 Name The Year: Vanna White (in South Carolina), game show hostess (Wheel of Fortune), born. = 1957 Name The Year: Matt Dillon, actor, born. = 1964 Name The Year: Gambian National Day = 1965 Name The Year: Molly Ringwald (in Los Angeles, CA), actress (Pretty in Pink), born. = 1968 Name The Year: Nicolaus Copernicus (in Poland), astronomer, born. = 1473 Name The Year: Thomas Alva Edison patents the phonograph. = 1878 Name The Year: Merle Oberon (in Tasmania), born. = 1911 Name The Year: Lee Marvin, actor, born. = 1924 Name The Year: Smokey Robinson, singer, born. = 1940 Name The Year: Marines land on Iwo Jima. = 1945 Name The Year: Margaux Hemingway (in Portland, Oregon), actress, daughter of Ernest, born. = 1955 Name The Year: Prince Andrew, Second eldest son of HRH Queen Elizabeth II, born. = 1960 Name The Year: Hana Mandlikova, tennis player, born. = 1962 Name The Year: Justine Bateman (in Rye, NY), actress (Family Ties), born. = 1966 Name The Year: President Ford pardons Iva Toguri D'Aquino ("Tokyo Rose"). = 1977 Name The Year: Enzo Ferrari, car designer and manufacturer, born. = 1898 Name The Year: Ansel Adams, knows how to handle a camera, born. = 1902 Name The Year: Alexei Kosygin, Soviet Premier, born. = 1904 Name The Year: Gloria Vanderbilt, fashion designer (Don't my jeans look great!), born. = 1924 Name The Year: Sydney Poitier, actor, born. = 1924 Name The Year: Robert Altman, director (M = A = S = H), born. = 1925 Name The Year: Bobby Unser, auto racer, born. = 1934 Name The Year: First automobile/airplane combination tested, Santa Monica, Ca = 1937 Name The Year: John Glenn aboard Friendship 7, is first American to orbit the Earth. = 1962 Name The Year: Charles Barkley, Star player for the Pheonix Suns and Houston Rockets basketball teams, born. = 1963 Name The Year: President Nixon visits China. = 1972 Name The Year: Clarence Nash, the voice of Donald Duck, died. = 1985 Name The Year: First self-propelled locomotive on rails demonstrated, in Wales. = 1804 Name The Year: First electric burglar alarm is installed in Boston, Mass. = 1858 Name The Year: First Telephone book is issued, in New Haven, Conn. = 1878 Name The Year: WW I Battle of Verdun begins. = 1916 Name The Year: Larry Hagman, actor, born. = 1931 Name The Year: Camera exposure meter patented, W. N. Goodwin. = 1932 Name The Year: Nina Simone (in Tyron, NC), born. = 1933 Name The Year: David Geffen, record company president, born. = 1943 Name The Year: Tyne Daly (in Madison, WI), actress (Cagney and Lacey), born. = 1946 Name The Year: First instant develop camera demonstrated in NY City by E. H. Land. = 1947 Name The Year: Malcolm X assassinated. = 1965 Name The Year: Richard Nixon first American president to visit China. = 1972 Name The Year: Charles Rocket clearly says a four-letter word on Saturday Night Live. = 1981 Name The Year: Popcorn is introduced by an Indian named Quadequina to the English colonists at their first Thanksgiving dinner. = 1630 Name The Year: George Washington, father figure for U.S., President (1789-1796), born. = 1732 Name The Year: Johann Nikolaus Forkel, musicologist and first biographer of Bach, born. = 1749 Name The Year: Empress of China sets sail on first New York to China route. = 1784 Name The Year: Frederic Chopin, composer, born. = 1810 Name The Year: Lord Robert Baden-Powell, founder of the Boy Scouts, born. = 1857 Name The Year: Edna St. Vincent Millay, poet, born. = 1892 Name The Year: Hawaii becomes a U.S. territory. = 1900 Name The Year: Julius "Magic" Erving, basketball player, born. = 1950 Name The Year: Drew Barrymore, actress, member of the Barrymore clan, born. = 1975 Name The Year: USA beats the Russians in Olympic Hockey (4-3). = 1980 Name The Year: Gutenberg Day = 1584 Name The Year: George Frideric Handel, Baroque composer in Germany, born. = 1685 Name The Year: Alamo is besieged by Santa Anna, entire garrison eventually killed. = 1836 Name The Year: Rotary Club is founded by four men in Chicago. = 1905 Name The Year: First rocket air mail flight, Greenwood Lake, NY. = 1935 Name The Year: Peter Fonda, actor, son of Henry, born. = 1940 Name The Year: US Marines raise flag on Iwo Jima, famous photo and statue. = 1945 Name The Year: Howard Jones, musician, born. = 1955 Name The Year: Wilhelm Karl Grimm, story teller, born. = 1786 Name The Year: Admiral Chester Nimitz, in charge of Pacific Fleet in WWII, born. = 1885 Name The Year: First rocket to reach outer space launched at White Sands, NM. = 1949 Name The Year: Steven Jobs, co-founder of Apple Computer, born. = 1955 Name The Year: Cassius Clay beats Sonny Liston for the heavyweight championship. = 1964 Name The Year: USA beats Finland and wins the Olympic Gold Medal (4-2). = 1980 Name The Year: Voyager 2, first Uranus flyby. = 1986 Name The Year: Funeral for Japan's Emperor Hirohito costs his government $80-billion dollars, attracts all major world leaders. = 1989 Name The Year: United Airlines 747 jet rips open in flight from Honolulu to New Zealand, 9 passengers sucked out of plane, many injuries. = 1989 Name The Year: A London pedestrian walks 20 miles backward and 20 miles forward in 8 hours. = 1838 Name The Year: Renoir, painter, born. = 1841 Name The Year: Enrico Caruso (in Italy), singer, born. = 1873 Name The Year: First aircraft carrier "Ranger" launched. = 1933 Name The Year: George Harrison, singer (Beatles), born. = 1943 Name The Year: Paul McCartney releases "Give Ireland back to the Irish" single. = 1972 Name The Year: Soyuz 24 returns to Earth. = 1977 Name The Year: Soyuz 32 is launched. = 1979 Name The Year: Victor Hugo, French author, born. = 1802 Name The Year: William "Buffalo Bill" Cody (near Davenport, Iowa), frontiersman, born. = 1846 Name The Year: Second French Republic was proclaimed. = 1848 Name The Year: John Harvey Kellogg, English Corn Flake inventor. = 1852 Name The Year: First New York City subway line was opened to the public. = 1870 Name The Year: S.S. Ceylon begins first round-the-world cruise from Liverpool. = 1881 Name The Year: Jackie Gleason, Comedian, born. = 1916 Name The Year: U.S. Congress established Grand Canyon National Park in Arizona. = 1919 Name The Year: Tony Randall, actor (The Odd Couple), born. = 1920 Name The Year: Fats Domino, singer, born. = 1928 Name The Year: Johnny Cash, singer, The Man in Black, born. = 1932 Name The Year: Golden Gate Bridge ground-breaking ceremony held at Crissy Field. = 1933 Name The Year: PM Winston Churchill announced that Britain had its own atomic bomb. = 1952 Name The Year: First typesetting machine (photo engraving) used at Quincy, Mass. = 1954 Name The Year: First aviator to bail out at supersonic speed - G. F. Smith. = 1955 Name The Year: Levi Eshkol dies, Golda Meir becomes premier of Israel. = 1969 Name The Year: Beatles release "Beatles Again" a.k.a. "Hey Jude" album. = 1970 Name The Year: Philippines President Ferdinand E. Marcos flees in defeat, takes up residence in Hawaii with wife Imelda. = 1986 Name The Year: Barings Bank disaster. Nick Leeson loses billions of Pounds Sterling in offshore investments, ruining Barings Bank. = 1995 Name The Year: Constantine = 1583 Name The Year: Henry Wadsworth Longfellow (in Portland, Maine), poet, born. = 1807 Name The Year: Dominican Republic gains it's independence. = 1844 Name The Year: Oscar Hammerstein patents first practical cigar-rolling machine. = 1883 Name The Year: John Steinbeck, writer, born. = 1902 Name The Year: U.S. Supreme Court unanimously upheld the 19th Amendment (woman's right to vote). = 1922 Name The Year: Joanne Woodward (in Thomasville, GA), actress, wife of Paul Newman, born. = 1930 Name The Year: Elizabeth Taylor (in London, England), actress, had many husbands, born. = 1932 Name The Year: Reichstag building in Berlin was set afire, Nazis blame Communists. = 1933 Name The Year: Chaim Weizmann becomes the first Israeli president. = 1949 Name The Year: President Nixon and Chinese Premier Chou En-lai issued the Shanghai Communique. = 1972 Name The Year: American Indian Movement occupy Wounded Knee in South Dakota. = 1973 Name The Year: American Republican Party formed at Ripon, Wisconsin. = 1854 Name The Year: First vaudeville theater opens. = 1883 Name The Year: Zero Mostel, actor (Fiddler on the Roof), born. = 1915 Name The Year: Svetlana Stalina, daughter of Josef Stalin, born. = 1926 Name The Year: Gavin MacLeod, actor (Love Boat, Mary Tyler Moore Show), born. = 1930 Name The Year: Stephane Beacham, actrss, born. = 1947 Name The Year: Bernadette Peters (in Queens, New York), actress, Tony winner, born. = 1948 Name The Year: Forrester issued a patent for computer core memory. = 1956 Name The Year: The final episode of TV show "M_A_S_H." = 1983 Name The Year: It was made legal in Scotland for women to propose to men. = 1288 Name The Year: Pope Paul III, born. = 1468 Name The Year: Pope Hilarius, born. = 1584 Name The Year: Britian and US signed treaty on seal hunting in Bering Sea. = 1892 Name The Year: Jimmy Dorsey, bandleader, born. = 1904 Name The Year: Howard Nemerov, poet, born. = 1916 Name The Year: Al Rosen, baseball player and executive, born. = 1924 Name The Year: Arthur Wellsley Duke of Wellington, British PM (C) (1828-30), born. = 1769 Name The Year: Mozart's opera "Marriage of Figaro" premiers in Wien (Vienna). = 1786 Name The Year: Chardonnet, France inventor (rayon), born. = 1839 Name The Year: 1st adhesive postage stamps ("Penny Blacks" from England) issued. = 1840 Name The Year: 1st International Workers Day, according to the 2nd International. = 1889 Name The Year: Walter Susskind, Praha (Prague) Czechoslovakia, conductor, born. = 1913 Name The Year: Harry Belefonte calypso singer (The Banana Boat Song), born. = 1927 Name The Year: Paul Doumer Pres of France, assassinated by Russia's Paul Gargalov. = 1932 Name The Year: Philippine legislature accepts US proposal for independence. = 1934 Name The Year: Batman Comics hit the street. = 1939 Name The Year: Judy Collins, singer (Send in the Clowns, Clouds), born. = 1939 Name The Year: "Citizen Kane," directed & starring Orson Welles, premiers in NY. = 1941 Name The Year: Joanna Lumley, Kashmir India, actress (Absolutely Fabulous, The Avengers), born. = 1946 Name The Year: Ray Parker Jr rocker (Ghostbusters theme), born. = 1954 Name The Year: Oscar Torp, Norwegian premier, dies. = 1959 Name The Year: Russia shoots down Francis Gary Powers' U-2 spy plane over Sverdlovsk. = 1960 Name The Year: Fidel Castro announces there will be no more elections in Cuba. = 1961 Name The Year: Tanganyika granted full internal self-government by Britain. = 1961 Name The Year: Indonesia takes control of Irian Jaya (west New Guinea) from Netherlands. = 1963 Name The Year: 1st BASIC program run on a computer (Dartmouth). = 1964 Name The Year: Priscilla & Elvis Presley wed. = 1967 Name The Year: Elton John becomes 1st pop star to perform in Israel. = 1979 Name The Year: Home rule introduced to Kalaallit Nunaat (Greenland). = 1979 Name The Year: Marshall Islands (in the Pacific) become self-governing. = 1979 Name The Year: 135 acre Disney's MGM studio officially opens to the public. = 1989 Name The Year: Leonardo Da Vinci artist/scientist, dies at 67. = 1519 Name The Year: Alessandro Scarlatti, Palermo Italy, composer (Tigrane), born. = 1660 Name The Year: Treaty of Aix-la-Chapelle, ends War of Devolution. = 1668 Name The Year: Catherine II (the Great) empress of Russia (1762-96), born. = 1729 Name The Year: France & Spain agree to donate arms to American rebels. = 1776 Name The Year: William Herschel discovers 1st binary star, Xi Ursae Majoris. = 1780 Name The Year: Uprising against French occupation begins in Madrid. = 1808 Name The Year: Leo XIII, 257th Roman Catholic pope (1878-1903), born. = 1810 Name The Year: Theodor Herzl, founded Zionist movement, born. = 1840 Name The Year: Congo Free State established by King Leopold II of Belgium. = 1885 Name The Year: Manfred Freiherr von Richthofen [the Red Baron], German WW I ace, born. = 1892 Name The Year: Benjamin Spock, pediatrician/author (Common Sense Book of Baby Care), born. = 1903 Name The Year: Bing Crosby (claimed this to be his Birthdate: True Date May 3 1903), crooner/actor (Going My Way), born. = 1904 Name The Year: Faisal II, King of Iraq, born. = 1935 Name The Year: Russia takes Berlin. = 1945 Name The Year: Prisoners revolt at Alcatraz, 5 die. = 1946 Name The Year: 1st commercial jet plane, BOAC Comet. = 1952 Name The Year: US Lab detects high-temperature microwave radiation from Venus. = 1956 Name The Year: Joseph McCarthy commie hunting senator, dies at 47. = 1957 Name The Year: Beatles' Second album goes #1 & stays #1 for for 5 weeks. = 1964 Name The Year: Apple records closes down (Beatles first record company). = 1975 Name The Year: "Quadrophenia" premieres in London. = 1979 Name The Year: Joseph Doherty & 3 other IRA men arrested for murder. = 1980 Name The Year: David Rappaport 3'11' actor (wizard), shoots himself at 38. = 1990 Name The Year: First regular steam train passenger service starts. = 1830 Name The Year: Francois Coty, perfumemaker, born = 1874 Name The Year: Golda Meir, Israeli leader, born = 1898 Name The Year: James Brown, singer (I Feel Good . . . HEH!), born. = 1933 Name The Year: Frankie Valli, seasonal singer, born = 1947 Name The Year: First landing by an airplane at geographic North Pole. = 1952 Name The Year: Alexander VI divides non-Christian world between Spain & Portugal. = 1493 Name The Year: Indians sell Manhattan Island for $24 in cloth & buttons. = 1626 Name The Year: French manufacturer debuts 1st folding umbrella (Paris). = 1715 Name The Year: Thomas Henry Huxley scientist/humanist/Darwinist, born. = 1825 Name The Year: War of the Reform (Mexico); Liberals establish capital at Vera Cruz. = 1858 Name The Year: Haymarket riot in Chicago; bomb kills 7 policemen. = 1886 Name The Year: Sherlock Holmes "dies" at Reichenbach Falls. = 1891 Name The Year: Kakuei Tanaka Japanese PM convicted of bribe-taking, born. = 1918 Name The Year: Academy of Motion Picture Arts & Sciences forms. = 1927 Name The Year: Audrey Hepburn (Hepburn-Ruston Edda van Heemstra), Brussels, Belguim (Breakfast at Tiffany's, My Fair Lady), born. = 1929 Name The Year: Douglas Hyde (a protestant) becomes 1st president of Eire. = 1938 Name The Year: Keith Haring Kutztown Pa, graffiti artist (Vanity Fair, Paris Review), born. = 1958 Name The Year: Randy Travis country singer (Always & Forever), born. = 1959 Name The Year: 4 students at Kent State University killed by Ohio National Guard during anti-war protest. = 1970 Name The Year: Kiss performs their 1st concert. = 1976 Name The Year: Margaret Thatcher becomes prime minister of England. = 1979 Name The Year: Angela Bowie reveals that ex husband David slept with Mick Jagger. = 1990 Name The Year: Latvia's parliment votes 138-0 (1 abstention) for Independence. = 1990 Name The Year: Pres Bush is hospitalized for erratic heartbeat. = 1991 Name The Year: 2nd Council of Constantinople (5th ecumenical council) opens. = 553 Name The Year: Leopold I Emperor of Holy Roman Empire, dies at 64. = 1705 Name The Year: Síren Kierkegaard Denmark, philosopher, founded Existentialism, born. = 1813 Name The Year: Karl Marx philospher (Communist Manifesto, Das Kapital), born. = 1818 Name The Year: Battle of Puebla; Mexicans defeat Maximilian's forces (Cinco de Mayo). = 1867 Name The Year: Carnegie Hall opens in NYC with Tchaikovsky as guest conductor. = 1891 Name The Year: Spencer Tracy actor (Guess Who's Coming to Dinner), born. = 1900 Name The Year: Rex Harrison England, actor (My Fair Lady, Cleopatra), born. = 1908 Name The Year: 5th modern Olympic games opens in Stockholm. = 1912 Name The Year: Tyrone Power actor (Mark of Zorro, Alexander's Ragtime Band), born. = 1913 Name The Year: John T Scopes arrested for teaching evolution in Tennessee. = 1925 Name The Year: 1st woman to fly solo from England to Australia takes-off (Amy Johnson). = 1930 Name The Year: Michael Palin comedian (Monty Python, Fish Called Wanda), born. = 1943 Name The Year: Netherlands & Denmark liberated from Nazi control. = 1945 Name The Year: 1st large-scale US Army ground units arrive in South Vietnam. = 1965 Name The Year: Bobby Sands IRA activists dies in his 66th day of his hunger strike. = 1981 Name The Year: Paul Hogan & Linda Koslowski wed in Byron Bay, Eastern Australia. = 1990 Name The Year: conjunction of Sun, Mercury, Venus, Mars, Jupiter, Saturn & Moon. = 2000 Name The Year: Sack of Rome - End of the Renaissance. = 1527 Name The Year: Karl Marx, author of the Communist Manifesto, born = 1818 Name The Year: Patent granted to Dr. John Farrie for a "refrigeration machine". = 1851 Name The Year: Sigmund Freud, cigar smoker, psychiatrist, born = 1856 Name The Year: Rudolph Valentino, silent film star, sheik = 1895 Name The Year: T. H. White, writer, born = 1915 Name The Year: Orson Welles, actor (Citizen Kane), director, genius, born = 1915 Name The Year: The dirigible Hindenburg explodes in flames at Lakehurst, NJ. = 1937 Name The Year: Roger Bannister breaks the 4 min mile in 3:59:4 = 1954 Name The Year: Seventeenth Space Shuttle Mission - Challenger 7 returns to Earth. = 1985 Name The Year: Robert Browning, poet, born. = 1812 Name The Year: Beethoven's Ninth Symphony presented for first time. = 1824 Name The Year: Johannes Brahms, composer, born. = 1833 Name The Year: Peter Ilich Tchaikovsky, composer, born. = 1840 Name The Year: Gary Cooper, actor (High Noon, The Plainsman), born. = 1901 Name The Year: Edwin Land, founded instant photography (Polaroid), born. = 1909 Name The Year: Eva Peron, Argentine first lady, born = 1919 Name The Year: World War II ends in Europe. Nazis surrender to General Eisenhower at Reims, France. = 1945 Name The Year: Traci Lords, actress, born. = 1968 Name The Year: John II ends his reign as Catholic Pope. = 535 Name The Year: St Boniface IV ends his reign as Catholic Pope. = 615 Name The Year: St Benedict II ends his reign as Catholic Pope. = 685 Name The Year: Jack Cade's Rebellion-Kentishmen revolt against King Henry VI. = 1450 Name The Year: Hernando de Soto discovers Mississippi River. = 1541 Name The Year: Edward Gibbon England, historian (Decline & Fall of Roman Empire), born. = 1737 Name The Year: Miguel Hidalgo father of Mexican independence, born. = 1753 Name The Year: William Walker filibuster, president of Nicaragua (1856-57), born. = 1824 Name The Year: Jean Henri Dunant Switzerland, founded Red Cross, YMCA (Nobel 1901), born. = 1828 Name The Year: Australian Rules Football is created. = 1866 Name The Year: Harry S Truman Missouri, 33rd US President (D) (1945-1949), born. = 1884 Name The Year: Atlanta pharmacist John Styth Pemberton invents Coca Cola. = 1886 Name The Year: China cedes Taiwan to Japan under Treaty of Shimonoseki. = 1895 Name The Year: Richard F Attenborough environmentalist/zoologist/filmmaker (BBC), born. = 1926 Name The Year: Gary Snyder beat poet (Rip Rap & Cold Mountain Poems), born. = 1930 Name The Year: Gary Glitter [Paul Gadd] England, rocker (Rock & Roll Part II), born. = 1944 Name The Year: Nazi General Von Keitel surrenders to Russian Marshal Zhukov near Berlin. = 1945 Name The Year: V-E Day; Germany signs unconditional surrender, WW II ends in Europe. = 1945 Name The Year: Beatles release "Let it Be" album. = 1970 Name The Year: USSR announces it will not participate in LA Summer Olympics. = 1984 Name The Year: Robert A Heinlein sci-fi writer, dies of heart failure at 80. = 1988 Name The Year: Columbus left Spain on his 4th and final trip to the New World. = 1502 Name The Year: James M. Barrie (in Scotland), Author (Peter Pan) = 1860 Name The Year: Howard Carter, Egyptologist who found King Tutankhamen = 1873 Name The Year: Albert Finney, actor (Oliver, The Twelve Chairs) = 1936 Name The Year: Italy annexed Ethiopia. = 1936 Name The Year: Czechoslovak National Day. = 1945 Name The Year: Candice Bergen (in Beverly Hills), actor (Murphy Brown) = 1946 Name The Year: Billy Joel (in the Bronx, NY), singer, pianoman = 1949 Name The Year: EEC Anniversary. = 1950 Name The Year: House Judiciary Committee begin formal hearings on Nixon impeachment. = 1974 Name The Year: John Wilkes Booth assassin of Abraham Lincoln, born. = 1838 Name The Year: Sir Thomas Lipton tea magnate (Lipton Tea), born. = 1850 Name The Year: Gen Stonewall Jackson dies from wounds received at Chancellorsville. = 1863 Name The Year: Victoria Woodhull becomes 1st woman nominated for US president. = 1872 Name The Year: Meteor falls near Estherville, Iowa. = 1879 Name The Year: Fred Astaire Omaha Neb, tap dancer/actor (Easter Parade, Swingtime), born. = 1899 Name The Year: David O Selznick Pittsburgh Pa, producer (Gone With the Wind), born. = 1902 Name The Year: 1st Mother's Day held (Phila). = 1908 Name The Year: Comet Halley's closest approach to Earth. = 1910 Name The Year: William Huggins discoverer of stellar nature of Andromeda, dies. = 1910 what is the best IRC Network = www.nothingbutstyle.net Name The Year: Winston Churchill succeeds Neville Chamberlain as British PM. = 1940 Name The Year: England's House of Commons detroyed in a blitz. = 1941 Name The Year: Donovan (Leitch) Scotland, rock singer (Mellow Yellow), born. = 1943 Name The Year: Mark David Chapman assassin of John Lennon, born. = 1955 Name The Year: Sid Vicious [John Beverly], bassist (Sex Pistols), born. = 1957 Name The Year: Bono aka Paul Hauson, rocker (U2), born. = 1960 Name The Year: Decca signs the Rolling Stones on advice of Beatle George Harrison. = 1963 Name The Year: Tommy Lee drummer of Motley Crue marries Heather Locklear. = 1986 Name The Year: Constantinople (later Istanbul) founded. = 330 Name The Year: Waltz introduced into English ballrooms. Most observers consider it disgusting & immoral. No wonder it caught on! = 1812 Name The Year: Irving Berlin [Isadore Balin], Temun, Russia, composer (White Xmas), born. = 1888 Name The Year: Dame Margaret Rutherford, England, actress (Murder Most Foul), born. = 1892 Name The Year: Salvador Dali, Spain, surrealist artist (Crucifixion), born. = 1904 Name The Year: Einstein's Theory of General Relativity presented. = 1916 Name The Year: Britain grants Royal Letters Patent to New Zealand. = 1917 Name The Year: Jackie Milburn, English soccer star (Newcastle United). = 1924 Name The Year: Mort Sahl comedian/political satirist/beatnik (Big Party). = 1927 Name The Year: Eric Burdon, rocker (Animals-House of the Rising Sun). = 1941 Name The Year: Laos accepts constitution for parliamentary democracy. = 1947 Name The Year: By a vote of 37-12, Israel becomes 59th member of UN. = 1949 Name The Year: Siam renames itself Thailand. = 1949 Name The Year: Jay Forrester patents computer core memory. = 1951 Name The Year: Elvis Presley's 1st entry on UK charts with "Heartbreak Hotel". = 1959 Name The Year: Israeli soldiers capture Adolf Eichmann in Buenos Aires. = 1960 Name The Year: John D Rockefeller Jr philanthropist, dies at 86. = 1960 Name The Year: Bob Marley reggae singer, dies at 36 of brain & lung cancer. = 1981 Name The Year: Emilio Estevez actor (Breakfast Club, Young Guns). = 1962 Name The Year: Bangladesh windstorm kills 17,000. = 1965 Name The Year: Israel signs an agreement with European Economic Market. = 1975 Name The Year: Ted Turner manages an Atlanta Braves game. = 1977 Name The Year: St Stephen I begins his reign as Catholic Pope. = 254 Name The Year: Edward Lear England, landscape painter, writer of nonsense verse, born. = 1812 Name The Year: Florence Nightingale Florence, Italy, nurse (Crimean War), born. = 1820 Name The Year: Treaty of Bardo, Tunisia becomes a French protectorate. = 1881 Name The Year: Burt Bacharach composer (I'll Never Fall in Love Again), born. = 1929 Name The Year: George VI's coronation in Great Britain. = 1936 Name The Year: Beryl Burton UK, won record (7) women's cycling titles, born. = 1937 Name The Year: Nazi blitz conquest of France began by crossing Muese River. = 1940 Name The Year: Axis forces in North Africa surrender. = 1943 Name The Year: Steve Winwood England, rocker (A Higher Love), born. = 1948 Name The Year: West begins Berlin Airlift to get supplies around Soviet blockade. = 1949 Name The Year: Shozo Fujii judo champion, born. = 1950 Name The Year: Elvis Presley appears on a Frank Sinatra TV special. = 1960 Name The Year: Emilio Estevez actor (Young Guns), born. = 1962 Name The Year: Bob Dylan walks off Ed Sullivan Show. = 1963 Name The Year: Israel & West Germany exchange letters beginning diplomatic relations. = 1965 Name The Year: Paul McCartney & Wings release "Mary Had a Little Lamb". = 1972 Name The Year: 1st quadrophonic concert (Pink Floyd in London). = 1977 Name The Year: In Fatima Portugal, a Spanish priest with a bayonet is stopped prior to his attempt to attack Pope John Paul II. = 1982 Name The Year: South African prisoner Nelson Mandela sees his wife for 1st time in 22 years. = 1984 Name The Year: Nora Dunn & Sinead O'Connor boycott Saturday Night Live to protest Andrew "Dice" Clay's hosting. = 1990 Name The Year: St Agapitus I begins his reign as Catholic Pope. = 535 Name The Year: Innocent XIII, 244th Roman Catholic pope (1721-24), born. = 1655 Name The Year: Empress Maria Theresa, Austria (Wife of Napolean), born. = 1717 Name The Year: Sir Arthur Sullivan, London, England, of Gilbert & Sullivan fame, born. = 1842 Name The Year: Peter Henry Emerson, 1st to promote photography as an independent art, born. = 1856 Name The Year: Brazil abolishes slavery. = 1888 Name The Year: Daphne du Maurier novelist (Rebecca, The Parasites), born. = 1907 Name The Year: Royal Flying Corps established in England. = 1912 Name The Year: 1st 4 engine aircraft built & flown (Igor Sikorsky-Russia). = 1913 Name The Year: Jim Jones, reverend, poisoned over 100 in Guyana, born. = 1931 Name The Year: Churchill says "I have nothing to offer but blood, toil, tears & sweat". = 1940 Name The Year: Ritchie Valens, singer (Donna, La Bamba), born. = 1941 Name The Year: Peter Gabriel, rocker (Genesis-Against All Odds), born. = 1950 Name The Year: Diner's Club issues its 1st credit cards. = 1950 Name The Year: Stevie Wonder, singer/songwriter (You are The Sunshine of My Love), born. = 1950 Name The Year: Gary Cooper, 2 time Academy award winning actor, dies at 60. = 1961 Name The Year: Rolling Stones record "Satisfaction". = 1965 Name The Year: Rolling Stones release "Paint it Black". = 1966 Name The Year: Beatles movie "Let it Be" premiers. = 1970 Name The Year: Pope John Paul II shot, wounded by assailant in St Peter's Square. = 1981 Name The Year: Chet Baker jazz trumpeter fell to death out of a hotel window at 59. = 1988 Name The Year: Approx 2,000 students begin hunger strike in Tiananmen Square, China. = 1989 Name The Year: George Lucas director (Star Wars), born. = 1944 Name The Year: David Byrne Dunbartin Scotand, rocker (Talking Heads-Psycho Killer) , born. = 1952 Name The Year: Skylab launched, the 1st Space Station. = 1973 Name The Year: Rita Hayworth actress, dies at 68 of Alzheimer's disease. = 1987 Name The Year: Cape Cod was discovered by the English navigator Batholomew Gosnold. = 1602 Name The Year: Whitsunday Term Day. = 1753 Name The Year: American poet Emily Dickinson died. = 1886 Name The Year: US Airmail begins between Wash DC, Philadelphia and New York. = 1918 Name The Year: Wladziu Valentino Liberace, pianist, born = 1919 Name The Year: US Dept of Justice offers $25,000 reward for Dillinger, dead or alive. = 1934 Name The Year: Paul Zindel, playwright, born = 1936 Name The Year: Nylon stockings on sale for the first time in the United States. = 1940 Name The Year: Brian Eno, singer, songwriter, born = 1948 Name The Year: Lee Horsley, actor, born = 1955 Name The Year: The Soviet Union launched Sputnik III. = 1958 Name The Year: Marie Antoinette married the future King Louis XVI of France. = 1770 Name The Year: William H. Seward, who bought Alaska at $0.02/acre, born. = 1801 Name The Year: Henry Fonda (in Grand Island, Nebraska), actor, born. = 1905 Name The Year: Joan of Arc was canonized in Rome. = 1920 Name The Year: Pierce Brosnan (in Israel), actor (current James Bond), born. = 1952 Name The Year: Debra Winger (in Columbus, Ohio), actor, born. = 1955 Name The Year: Olga Korbut (in Russia), gymnast, born. = 1955 Name The Year: Janet Jackson, singer, Michael's sister, born. = 1966 Name The Year: Venera 5, Venus landing. First successful landing on another planet. = 1969 Name The Year: Gabriela Sabatini, tennis professional, raqueteer, born. = 1970 Name The Year: Japanese Junko Tabei became first woman to reach Mt Everest's summit. = 1975 Name The Year: Sammy Davis, Jr., "The Complete Entertainer", dies of throat cancer. = 1990 Name The Year: Later that day, Jim Henson, creator of the Muppets, dies from pneumonia and complications. = 1990 Name The Year: 7th recorded perihelion passage of Halley's Comet. = 218 Name The Year: Liberius begins his reign as Catholic Pope. = 352 Name The Year: St Adrian III begins his reign as Catholic Pope. = 884 Name The Year: Sandro Botticelli, Italian painter (Birth of Venus), born. = 1444 Name The Year: 1st merry-go-round seen at a fair (Philippapolis, Turkey). = 1620 Name The Year: Italian Jesuit Niccolo Zucchi, 1st to see 2 belts on Jupiter surface. = 1630 Name The Year: Frontenac becomes governor of New France (Canada). = 1672 Name The Year: Edward Jenner England, physician, started vaccination, born. = 1749 Name The Year: Papal States annexed by France. = 1809 Name The Year: Joseph Norman Lockyer discovered Helium/founded Nature magazine, born. = 1836 Name The Year: Ayatollah Ruhollah Khomeini Iran's spiritual leader, born. = 1900 Name The Year: 9th modern Olympic games opens in Amsterdam. = 1928 Name The Year: Dennis Hopper actor (True Grit, Blue Velvet, Easy Rider), born. = 1936 Name The Year: Germany occupies Brussels, Belgium & begins invasion of France. = 1940 Name The Year: Taj Mahal NYC, singer/songwriter (The Real Thing), born. = 1942 Name The Year: Christian Lacroix, French couturier, born. = 1950 Name The Year: "Sugar" Ray Leonard welter/middle/light-heavyweight boxing champion, born. = 1956 Name The Year: Brigitte Nielsen actress (Red Sonja, Rocky IV, Domino), born. = 1963 Name The Year: Thor Heyerdahl crosses the Atlantic on reed raft 'Ra'. = 1970 Name The Year: 10cc releases "I'm Not in Love". = 1975 Name The Year: Israel & Lebanon sign a peace treaty. = 1983 Name The Year: Nelson Mandela recieves a BA from University of South Africa. = 1989 Name The Year: St John I ends his reign as Catholic Pope. = 526 Name The Year: Napoleon became Emperor of France. = 1804 Name The Year: Edwin Budding of England signs an agreement for manufacture of his invention, the lawn mower. = 1830 Name The Year: Nicholas II last Russian tsar (1894-1917), born. = 1868 Name The Year: Bertrand Russell England, mathematician/philosopher (Nobel 1950), born. = 1872 Name The Year: Frank Capra movie director (Its a Wonderful Life, Arsenic & Old Lace), born. = 1897 Name The Year: Britain proclaims protectorate over kingdom of Tonga. = 1900 Name The Year: Passage of Earth through tail of Halley's Comet causes near-panic. = 1910 Name The Year: Perry Como singer/TV (Perry Como Show, What did Delaware?), born. = 1912 Name The Year: Pope John Paul II 264th Roman Catholic pope (1978- ), born. = 1918 Name The Year: Dame Margot Fonteyn England, ballerina (partner of Nureyev), born. = 1919 Name The Year: Yannick Noah France, tennis player (French 1983), born. = 1960 Name The Year: Gene Roddenberry suggests 16 names including Kirk for Star Trek Captain. = 1965 Name The Year: Ian Curtis musician (Joy Division), dies. = 1980 Name The Year: Mount St Helens blows its top in Washington State. = 1980 Name The Year: St Gregory II begins his reign as Catholic Pope. = 715 Name The Year: Anne Boleyn wife of Henry VIII, beheaded. = 1536 Name The Year: Spanish Armada sets sail for Lisbon, bound to England. = 1588 Name The Year: Innocent XI 240th Roman Catholic pope (1676-89), born. = 1611 Name The Year: About midday, near-total darkness descends on much of New England to this day it's cause is still unexplained. = 1780 Name The Year: Ho Chi Minh trail blazer/leader of Vietnam (1946, 1969), born. = 1890 Name The Year: Malcolm X Omaha NB, assassinated leader of black muslims, born. = 1925 Name The Year: "Firedamp" explodes in Mather Pa coal mine killing 195 of 273 miners. = 1928 Name The Year: T.E. Lawrence (of Arabia) dies in a motorcycle crash. = 1935 Name The Year: Peter Townsend singer (Who-Tommy), born. = 1945 Name The Year: US diplomats find at least 40 secret mics in the Moscow embassy. = 1964 Name The Year: Tortoise reportedly given to Tonga's king by Capt. Cook (1773), dies. = 1966 Name The Year: Ogden Nash poet/TV panelist (Masquerade Party), dies at 68. = 1971 Name The Year: USSR launches Mars 2, 1st spacecraft to crash land on Mars. = 1971 Name The Year: Gold ownership legalized in Australia. = 1976 Name The Year: Guitarist Eric Clapton marries Patti Boyd. = 1979 Name The Year: NASA launches Intelsat V. = 1983 Name The Year: Syzygy: 8 of 9 planets aligned on same side of sun. = 2161 Name The Year: Earthquake kills 250,000 in Antioch, Syria. = 526 Name The Year: Christopher Colombus explorer, dies in poverty in Spain at 55. = 1506 Name The Year: England passes Act of Grace, forgiving followers of James II. = 1690 Name The Year: Honore de Balzac France, novelist (Pere Goriot), born. = 1799 Name The Year: John Stuart Mill philosopher/political economist/Utilitarian, born. = 1806 Name The Year: Levi Strauss markets blue jeans with copper rivets, price $13.50 doz. = 1874 Name The Year: 2nd modern Olympic games opens in Paris (lasted 5 months). = 1900 Name The Year: Jimmy Stewart, actor (Mr Smith Goes to Wash, Wonderful Life), born. = 1908 Name The Year: Moshe Dayan Israeli general/politician, born. = 1915 Name The Year: Thomas Edison says Americans prefer silent movies over talkies. = 1926 Name The Year: Great Britain via treaty grants Saudi Arabia's kingdom independence. = 1927 Name The Year: Joe Cocker England, rock musician (You can leave your hat on), born. = 1944 Name The Year: Cher, rocker/actress (I got you babe, Jack Lalane, Mask), born. = 1946 Name The Year: Atomic fusion (thermonuclear) bomb dropped from plane-Bikini Atoll. = 1956 Name The Year: BBC bans Beatle's "A Day in the Life" (drug references). = 1967 Name The Year: The Beatles' "Let it Be" movie premiers in UK. = 1970 Name The Year: Republic of Cameroon declared as constitution is ratified. = 1972 Name The Year: 1st western pop star to tour the USSR-Elton John. = 1979 Name The Year: FBI arrests John A Walker Jr, convicted of spying for USSR. = 1985 Name The Year: China declares martial law in Beijing. = 1989 Name The Year: Gilda Radner comedienne (Sat Night Live), dies at 42 of cancer. = 1989 Name The Year: B.C. Plato (Aristocles), philosopher/writer, Athens, born. = 427 Name The Year: Albrecht Dürer Nörnberg Germany, Renaissance painter/print maker, born. = 1471 Name The Year: Philip II king of Spain (1556-98) & Portugal (1580-98), born. = 1527 Name The Year: Alexander Pope England, poet, born. = 1688 Name The Year: Fats Waller jazz pianist, composer (Ain't Misbehavin'), born. = 1904 Name The Year: Federation Internationale de Football Association (FIFA), Soccer's World governing body forms. = 1904 Name The Year: Lindburgh lands in Paris, after 1st solo air crossing of Atlantic. = 1927 Name The Year: Leo Sayer [Gerard], England, singer (When I Need Love), born. = 1948 Name The Year: Mr T [Lawrence Tero], Chicago, actor, (A-Team, Rocky III, T & T), born. = 1952 Name The Year: "Empire Strikes Back" premieres. = 1980 Name The Year: David Bowie's "Let's Dance" single goes #1. = 1983 Name The Year: Rajiv Gandhi Indian Prime Minster, assassinated. = 1991 Name The Year: 14th recorded perihelion passage of Halley's Comet. = 760 Name The Year: Townsend Speakman 1st sells fruit-flavored carbonated drinks (Phila). = 1807 Name The Year: Richard Wagner Leipsig Germany, composer (Ring, Flying Dutchman, Ride of the Valkries), born. = 1813 Name The Year: Confederaci¢n Granadina (now Colombia) forms. = 1858 Name The Year: Sir Arthur Conan Doyle UK, brought Sherlock Holmes to life twice, born. = 1859 Name The Year: Great Train Robbery; 7 men make off with $98,000 in cash. = 1868 Name The Year: Charles Aznavour Paris France, singer (Monsieur Carnavel), born. = 1924 Name The Year: Hitler & Mussolini sign "Pact of Steel". = 1939 Name The Year: Morrissey, (Steven Patrick) British rocker (The Smiths & solo artist), born. = 1959 Name The Year: Beatles' "Ticket to Ride," single goes #1. = 1965 Name The Year: Egyptian president Nassar closes Straits of Tiran to Israel. = 1967 Name The Year: Ceylon becomes Republic of Sri Lanka as its constitution is ratified. = 1972 Name The Year: Final European scheduled run of the Orient Express (94 years). = 1977 Name The Year: Rocky Graziano boxer, dies at 71, of heart failure. = 1990 Name The Year: Joan of Arc captured by Burgundians who sold her to the English. = 1430 Name The Year: King Henry VIII and Catherine of Aragon marriage declared null and void by the newly formed Church of England. = 1533 Name The Year: Captain Kidd is hanged in London after convicted of piracy and murder. = 1701 Name The Year: Douglas Fairbanks, first and greatest of Hollywood's swashbucklers, born = 1883 Name The Year: Canada's North West Mounted Police force (The Mounties) first established. = 1873 Name The Year: Italy declared war on Austria-Hungary in World War I. = 1915 Name The Year: Joan Collins (in London), actor, born = 1933 Name The Year: Bonnie and Clyde shot in Louisana ambush. = 1934 Name The Year: Industrialist John D. Rockfeller died in Ormond Beach, FL. = 1937 Name The Year: Nazi Himmler committed suicide while in prison at Luneburg, Germany. = 1945 Name The Year: West Germany was proclaimed. = 1949 Name The Year: 'Marvelous' Marvin Hagler (in New Jersey), boxer, born = 1952 Name The Year: Israel announced capture of Nazi Adolf Eichmann in Argentina. = 1960 Name The Year: Gabriel Daniel Fahrenheit, invented the thermometer = 1686 Name The Year: Jean-Paul Marat, philosopher = 1743 Name The Year: Queen Victoria, British ruler = 1819 Name The Year: Samuel F.B. Morse taps out "What Hath God Wrought". = 1844 Name The Year: The first auto repair shop opens in Boston, MA. = 1899 Name The Year: Robert Zimmerman (a.k.a. Bob Dylan) (in Minnesota), folk singer = 1941 Name The Year: Patti Labelle, singer = 1944 Name The Year: Priscilla Presley, actor, wife of Elvis = 1945 Name The Year: First rocket to exceed 150 mile altitude - White Sands, NM. = 1954 Name The Year: First house with built-in bomb shelter exhibited, Pleasant Hills, Pa. = 1959 Name The Year: M. Scott Carpenter aboard Aurora 7 is launched. = 1962 Name The Year: Beatles' third appearance on Ed Sullivan. = 1964 Name The Year: Soyuz 18B is launched. = 1975 Name The Year: B.C. 1st known prediction of a solar eclipse. = 585 Name The Year: St Gregory VII, pope (1073-85), dies (birth date unknown). = 1085 Name The Year: Alfonso VI of Castile captured Toledo, Spain, and brought the Moorish center of science into Christian hands. = 1085 Name The Year: Ralph Waldo Emerson US, essayist/philosopher, born. = 1803 Name The Year: Argentina declares independence from Napoleonic Spain (Natl Day). = 1810 Name The Year: Bill "Bojangles" Robinson, actor (Stormy Weather, Little Colonel), born. = 1878 Name The Year: Igor Sikorsky developed a working helicopter, born. = 1889 Name The Year: Revolution in Mexico overthrows President Diaz. = 1911 Name The Year: British House of Commons passes Irish Home Rule. = 1914 Name The Year: Miles Davis trumpeter; pioneered cool jazz (Porgy & Bess), born. = 1926 Name The Year: Robert Ludlum spy novelist (Bourne Identity), born. = 1927 Name The Year: Henry Ford stops producing Model T car (begins Model A). = 1927 Name The Year: Jesse Owens equals or breaks 6 world records in one hour. = 1935 Name The Year: 5,000 drown in a storm at Ganges Delta region in India. = 1941 Name The Year: Sally Jesse Raphael TV talk show host (Sally), born. = 1943 Name The Year: Frank Oz Heresford England, puppeteer (Sesame St, Muppet Show), born. = 1944 Name The Year: Arther C Clarke proposes relay satellites in geosynchronous orbit. = 1945 Name The Year: Jordan gains independence from Britain (Natl Day). = 1946 Name The Year: Paul Weller guitar (Jam-This is the Modern World, Style Council), born. = 1958 Name The Year: John Lennon takes delivery of his psychedelic painted Rolls Royce. = 1967 Name The Year: "Star Wars" released. = 1978 Name The Year: "Return of the Jedi" (Star Wars 3) released. = 1983 Name The Year: William of Ockham forced to flee from Avignon by Pope John XXII. = 1328 Name The Year: Abraham De Moivre French mathematician (De Moivre's theorem), born. = 1667 Name The Year: Alexander S Pushkin Russia, writer (Eugene Onegin), born. = 1799 Name The Year: Al Jolson jazz singer/silent film actor (Mamie, Swanee), born. = 1886 Name The Year: Last Czar of Russia, Nicholas II, crowned. = 1896 Name The Year: John Wayne "Duke", [Marion Michael Morrison] actor (True Grit), born. = 1907 Name The Year: Peter Cushing England, actor (Hound of the Baskervilles), born. = 1913 Name The Year: Georgian Social Democratic Republic declared independence from Russia. = 1918 Name The Year: 1st Le Mans Grand Prix d'Endurance was run. = 1923 Name The Year: San Francisco Bay's Golden Gate Bridge opens. = 1937 Name The Year: Patent filed in US for H-Bomb. = 1946 Name The Year: Stevie Nicks Phoenix Az, rocker (Fleetwood Mac-Bella Donna), born. = 1946 Name The Year: South Africa elects a nationalist govt with apartheid policy. = 1948 Name The Year: Siouxsie [Susan Ballion], (Siouxsie & the Bandshee-Wild Thing), born. = 1957 Name The Year: British Guiana gains independence, takes the name Guyana. = 1966 Name The Year: Apollo 10 returns to Earth. = 1969 Name The Year: John & Yoko begin their 2nd bed-in (Queen Elizabeth Hotel, Montreal). = 1969 Name The Year: Achsah Young becomes 1st woman known to be executed as a witch (MA). = 1647 Name The Year: Wild Bill Hickok cowboy, born. = 1837 Name The Year: Samuel F.B. Morse completes 1st telegraph line. = 1844 Name The Year: Vincent Price St Louis Mo, actor (The Fly, Laura), born. = 1911 Name The Year: Ropert L Ripley cartoonist (Believe It or Not), dies at 55 in NY. = 1949 Name The Year: Neil Finn rocker (Split Enz-I Got You, Crowded House), born. = 1958 Name The Year: Pat Cash tennis player (Wimbeldon 1987), born. = 1965 Name The Year: Dr. Joseph Guillotin, guess what he invented, born. = 1738 Name The Year: First indoor swimming pool opens at Goodman's Fields, London. = 1742 Name The Year: Jim Thorpe, Olympic athlete, born. = 1886 Name The Year: Ian Fleming, writer (James Bond series) = 1908 Name The Year: First all color talking picture "On With The Show" exhibited, NYC. = 1929 Name The Year: Gladys Knight, singer, born. = 1944 Name The Year: Premier of first animated 3-D cartoon in Technicolor - "Melody". = 1953 Name The Year: First animals fired into space and rescued, Cape Canaveral. = 1959 Name The Year: Amnesty International Anniversary. = 1961 Name The Year: Mars 3 launched. First spacecraft to soft land on Mars. = 1971 Name The Year: St Paul I begins his reign as Catholic Pope. = 757 Name The Year: Constantinople falls to Muhammad II (Turks); ends Byzantine Empire. Also considered the end of the Middle Ages. = 1453 Name The Year: Charles II king of England (1660-85), born. = 1630 Name The Year: Ebenezer Butterick inventor (tissue paper dress pattern), born. = 1826 Name The Year: Birth of Charles W. Fry, the English musician who, along with his three sons, formed the first Salvation Army brass band. = 1837 Name The Year: Trademark "Escalator" registered by Otis Elevator Co.. = 1900 Name The Year: 1st running of the Indianapolis 500. = 1911 Name The Year: John Fitzgerald Kennedy, 35th U.S. President (1961-1963), born. = 1917 Name The Year: Ecuador becomes independent. = 1922 Name The Year: Al Unser, auto racer (Indianapolis 500-1970, 71), born. = 1939 Name The Year: Hillary & Tenzing become 1st humans to reach top of Mt Everest. = 1953 Name The Year: Annette Bening, actress (Bugsy, Valmont), born. = 1958 Name The Year: Sue Press is 1st woman golfer to hit consecutive holes-in one. = 1977 Name The Year: Larry Bird beats out Magic Johnson for NBA rookie of year. = 1980 Name The Year: Eric Morecambe comedian (Morecambe & Wise), dies at 58. = 1984 Name The Year: 35 die in rioting between British & Italians at European Cup soccer. = 1985 Name The Year: Michael Jackson attempts to buy Elephant Man's remains. = 1987 Name The Year: Joan of Arc burned at the stake in Rouen, France at age 19. = 1431 Name The Year: The French philosopher Voltaire died. = 1778 Name The Year: Benny Goodman, bandleader, king of swing, born = 1909 Name The Year: Indianapolis 500 car race run for first time. Winning driver Ray Harroun takes it with a blazing 75 mph. = 1911 Name The Year: Christine Jorgensen, pioneer transsexual, born = 1926 Name The Year: Mariner 9 launched. = 1971 Name The Year: Three Japanese PFL terrorists kill 24, wound 72 at Tel Aviv's Lydda airport. = 1972 Name The Year: Spain became the 16th member of North Atlantic Treaty Organization . = 1982 Name The Year: Lady Godiva takes a ride through Coventry. = 1678 Name The Year: Composer Franz Josef Hayden died in Vienna, Austria. = 1809 Name The Year: Walt Whitman (in West Hills, NY), poet, born. = 1819 Name The Year: First recorded bicycle race, 2 kilometers in Paris. = 1868 Name The Year: First electric railway opens at Berlin Trades Exposition. = 1879 Name The Year: Don Ameche, actor (Cocoon, Trading Places), born. = 1908 Name The Year: Union of South Africa was founded. = 1910 Name The Year: During WW I British and German fleets fought the Battle of Jutland. = 1916 Name The Year: Prince Rainier of Monaco, born. = 1923 Name The Year: Clint Eastwood, actor (Dirty Harry, Heartbreak Ridge, City Heat), born. = 1930 Name The Year: Tom Berenger, actor, born. = 1950 Name The Year: South Africa became an independent republic. = 1961 Name The Year: WW II Gestapo official Adolf Eichmann hung in Israel. = 1962 Name The Year: Brooke Shields (in New York City), actor, model, born. = 1965 Name The Year: Tens of thousands of people in Peru died in an earthquake . = 1970 Name The Year: Israel and Syria sign an agreement concerning the Golan Heights. = 1974 Name The Year: Martin Luther, protestant reformer = 1699 Name The Year: Spencer Perceval (Tory), British PM (1809-12) = 1762 Name The Year: Viscount Goderich (Tory), British PM (1827-28) = 1782 Name The Year: Sir Benjamin Lee, Baronet Guinness, Irish brewer, Dublin mayor = 1798 Name The Year: First published reference to the game of Poker describes it as a Mississippi riverboat game. = 1834 Name The Year: Jose' Santos Zelaya (L), ruler of Nicaragua (1893-1910) = 1853 Name The Year: Stephen Crane, poet (Red Badge of Courage) = 1871 Name The Year: Carlos Saavedra Lamas, Argentine jurist (Nobel Peace Prize 1936) = 1878 Name The Year: Sholem Asch, Yiddish novelist, playwright (Three Cities) = 1880 Name The Year: Hermann Broch, Austrian novelist (Sleepwalkers, Bewitchment) = 1886 Name The Year: Philip John Noel-Baker, statesman, disarmament advocate (Nobel '59) = 1889 Name The Year: Alexander Alekhine of Russia, world chess champion (1927-46) = 1892 Name The Year: Wernher von Braun named head of German liquid-fuel rocket program. = 1932 Name The Year: Gary Player (in South Africa), golfer = 1935 Name The Year: Larry Flint, publisher (Hustler) = 1942 Name The Year: First black player in National Basketball Association, Fort Wayne, IN. = 1950 Name The Year: First atomic explosion witnessed by troops, NM. = 1951 Name The Year: First hydrogen device exploded at Eniwetok Atoll in the Pacific. = 1952 Name The Year: Daniel Boone frontiersman/explorer, born. = 1734 Name The Year: Marie-Antoinette Queen of France, born. = 1755 Name The Year: Burt Lancaster NYC, actor (From Here to Eternity, Elmer Gantry), born. = 1913 Name The Year: Ken Rosewall, Sydney, Australia, tennis star (US Open 1956), born. = 1934 Name The Year: Montgomery (Br) defeats Rommel (Ger) in battle of Alamein (WW II). = 1942 Name The Year: Howard Hughes' "Spruce Goose" flies for 1st (and last) time. = 1947 Name The Year: Hungary appeals for UN assistance against Soviet invasion. = 1956 Name The Year: Crew of Soyuz 29 returns to Earth aboard Soyuz 31. = 1978 Name The Year: President Reagan signs a bill establishing Martin Luther King day. = 1983 Name The Year: Mexican radio station erronously reports Mike Tyson dies in car crash. = 1988 Name The Year: Jermaine Jackson releases "Word to the Badd!!" anti Michael song. = 1991 Name The Year: Great panic occurs in Europe over the close approach of a comet. = 1679 Name The Year: John Montague 4th Earl of Sandwich, inventor of sandwich, born. = 1718 Name The Year: 1st opium war-2 British frigates engage several Chinese junks. = 1839 Name The Year: Jack the Ripper kills last victim. = 1888 Name The Year: Columbia grants Panama independence. = 1903 Name The Year: Austro-Hungarian Empire disolves. = 1918 Name The Year: Poland proclaims independence from Russia after WW I. = 1918 Name The Year: Charles Bronson Penns, actor (Death Wish, Dirty Dozen), born. = 1922 Name The Year: Turkey switches from Arabic to Roman alphabet. = 1928 Name The Year: 1st commercially produced synthetic rubber manufactured. = 1931 Name The Year: George II returns to Greece and regains monarchy. = 1935 Name The Year: Roy Emerson Australia, tennis player (Wimbeldon '64, '65), born. = 1936 Name The Year: Lulu, Glasgow, Scotland, singer/actress (To Sir With Love), born. = 1948 Name The Year: Clarence Birdseye markets frozen peas. = 1952 Name The Year: Roseanne Barr Arnold Salt Lake City, comedienne/TV star (Roseanne), born. = 1952 Name The Year: Adam Ant [Stuart Goddard], punk rocker (If I Strip For You), born. = 1954 Name The Year: Australia takes control of the Cocos Islands. = 1955 Name The Year: USSR launches Sputnik 2 with a dog (Laika), 1st animal in orbit. = 1957 Name The Year: Dolph Lundgren actor, born. = 1959 Name The Year: Body of assassinated Indian PM Indira Gandhi cremated. = 1984 Name The Year: Lebanese magazine Ash Shirra reveals secret US arms sales to Iran. = 1986 Name The Year: Soviet Union agrees to allow teaching of Hebrew. = 1988 Name The Year: Bill Clinton elected US President. = 1992 Name The Year: King William III of Orange = 1650 Name The Year: Lighthouse established on Alcatraz Island. = 1854 Name The Year: Eden Phillpotts, English novelist, poet, playwright = 1862 Name The Year: Kingdom of Italy annexes Venetia. = 1866 Name The Year: James Ritty patents the first cash register, to combat stealing by bartenders in his Dayton, Ohio saloon. = 1879 Name The Year: Art Carney, actor (Honeymooners, Harold and Maude) = 1918 Name The Year: Howard Carter discovers the tomb of Tutankhamen. = 1922 Name The Year: Loretta Swit (in Passaic, NJ), actor (M = A = S = H) = 1937 Name The Year: First air conditioned automobile (Packard) exhibited, Chicago, IL. = 1939 Name The Year: UN Educational, Scientific, and Cultural Organization formed. (UNESCO) = 1946 Name The Year: Israeli troops reach the Suez Canal for the first time. = 1956 Name The Year: Nicaragua holds first free elections in 56 years; Sandinistas win 63%. = 1984 Name The Year: Christopher Columbus learns of maize (corn) from the Indians of Cuba. = 1492 Name The Year: Gunpowder Plot; Catholics try to blow up English Parliament. Plot uncovered & leader Guy Fawkes hanged. = 1605 Name The Year: El Salvador's 1st battle against Spain for independence. = 1811 Name The Year: Honduras declares independence of Central American Federation. = 1838 Name The Year: Italy attacks Turkey, takes Tipoli & Cyrenaica. = 1911 Name The Year: Ludwig III crowned king of Bavaria. = 1913 Name The Year: Vivien Leigh, actress (Gone With Wind), born. = 1913 Name The Year: Britain annexes Cyprus. = 1914 Name The Year: Parker Brothers launches game of Monopoly. = 1935 Name The Year: Art Garfunkel, singer/actor (Sounds of Silence, Carnal Knowledge), born. = 1942 Name The Year: US launches Mariner 3 toward Mars; no data returned. = 1964 Name The Year: Iranian PM Jaafar Sharif-Emami resigns to Shah Mohammed Reza Pahlavi. = 1978 Name The Year: Jacques Tati actor/director, dies of pulmonary embolism. = 1982 Name The Year: Iceberg twice the size of Rhode Island sighted in Antarctic. = 1987 Name The Year: Charles II, last Habsburg king of Spain (1665-1700) = 1661 Name The Year: Colley Cibber, English dramatist, poet laureate (Love's Last Shift) = 1671 Name The Year: Aloys Senefelder, inventor of lithography = 1771 Name The Year: Spain grants Dominican Rep independence. = 1844 Name The Year: Charles Henry Dow, co-founder of Dow Jones, first editor of the Wall Street Journal = 1851 Name The Year: John Phillip Souza, march king (Stars and Stripes Forever, etc) = 1854 Name The Year: Ignace Jan Paderewski, Polish composer, pianist, patriot = 1860 Name The Year: James A. Naismith, inventor of basketball = 1861 Name The Year: Marie Curie, discoverer of radium = 1867 Name The Year: Heinrich Himmler, Nazi SS leader = 1900 Name The Year: Bolshevik revolution begins. = 1917 Name The Year: Joan Sutherland, opera star = 1926 Name The Year: Joni Mitchell, singer = 1943 Name The Year: Sally Field (in Pasadena, CA), actor (Flying Nun, Norma Rae) = 1946 Name The Year: US explodes world's first hydrogen bomb at Eniwetok Atoll. = 1952 Name The Year: Maria Shriver, Newscaster, married to Arnold Schartznegger = 1955 Name The Year: Pres Reagan was elected to a second term, winning 49 states. = 1984 Name The Year: 22nd Space Shuttle Mission - Challenger 9 returns to Earth. = 1985 Name The Year: Muhammad ibn Hazm, historian, jurist, writer of Islamic Spain = 994 Name The Year: Francisco de Zurbarin, Spanish Baroque painter = 1598 Name The Year: Abolitionist Elijah Lovejoy murdered by mob at Alton, Ill. = 1837 Name The Year: London Gazette, oldest surviving journal, is founded. = 1865 Name The Year: Madame Marie Sklodowska Curie, discovered radium (Nobel 1903, 1911) = 1867 Name The Year: Mary Celeste sails from NY to Genoa; found abandoned 4 weeks later. = 1872 Name The Year: Verney Cameron is first European to cross equatorial Africa from sea to sea. = 1875 Name The Year: Leon Trotsky, Russian Communist theorist, Bolshevik = 1879 Name The Year: Konrad Lorenz, zoologist, ethologist, writer (Nobel 1973) = 1903 Name The Year: Bolsheviks overthrew Russian govt in St Petersburg. = 1914 Name The Year: October Revolution overthrows Russian Provisional Government. = 1917 Name The Year: Goddard demonstrates tube-launched solid propellant rockets. = 1918 Name The Year: Al Hirt, trumpet player = 1922 Name The Year: Richard Nixon's first time quiting politics - "You won't have Nixon to kick around", he tells the press. = 1962 Name The Year: Dana Plato (in Maywood, CA) = 1964 Name The Year: "Gone With the Wind" is shown on TV. = 1976 Name The Year: Bomb explodes in U.S. Capitol, causing heavy damage but no injuries. = 1983 Name The Year: Colombian troops ends 27-hour siege of Bogota's Palace of Justice. = 1985 Name The Year: Sir Edmond Halley, astronomer, first to calculate a comet's orbit = 1656 Name The Year: Bourbon Whiskey is first distilled from corn by Reverend Elijah Craig in Bourbon County, Kentucky. = 1789 Name The Year: The Louvre, in Paris, was opened to the public. = 1793 Name The Year: Gottlob Frege, German mathematician, logician, philosopher = 1848 Name The Year: Abraham Lincoln was elected to his second term as President. = 1864 Name The Year: Wilhelm Rintgen discovers x-rays. = 1895 Name The Year: Margaret Mitchell, author (Gone With the Wind) = 1900 Name The Year: Katherine Hepburn (in Connecticut), actor (African Queen, On Golden Pond) = 1909 Name The Year: Peter Weiss, German dramatist, novelist (Marat/Sade) = 1916 Name The Year: Dr. Christiaan Barnard, performed first human heart transplantation = 1922 Name The Year: Hitler's "Beer Hall Putsch" failed; writes "Mein Kampf" while in jail. = 1923 Name The Year: Edward G. Gibson, astronaut (Skylab 4) = 1936 Name The Year: "Life with Father", longest running Broadway drama, opens (closes in 1947). = 1939 Name The Year: Allied forces invade North Africa. = 1942 Name The Year: Bonnie Raitt (in Los Angeles, CA), guitarist, singer = 1949 Name The Year: Christie Hefner, daughter of Hugh Hefner = 1952 Name The Year: Rickie Lee Jones (in Chicago), singer = 1954 Name The Year: Walt Disney's "Robin Hood" is released. = 1973 Name The Year: Fourteenth Space Shuttle Mission - Discovery 2 is launched. = 1984 Name The Year: Judge overturned Rubin "Hurricane" Carter's conviction. (The case that inspired Bob Dylan's song "The Story Of The Hurricane") = 1985 Name The Year: George Bush becomes the first sitting vice-president of the U.S. in 150 years to win a presidential election. = 1988 Name The Year: Napoleon becomes dictator (1st consul) of France = 1799 Name The Year: Ivan Turgenev, Russia, novelist/poet/playwright (Fathers and Sons), born. = 1818 Name The Year: Edward VII, king of England (1901-10), born. = 1841 Name The Year: Israel Bak created 1st hebrew printing press, dies. = 1874 Name The Year: Hedy Lamarr, actress (Ecstacy, Samson and Delilah), born. = 1913 Name The Year: Bavaria proclaims itself a republic. = 1918 Name The Year: Kaiser Wilhelm II abdicates after German defeat in WW I. = 1918 Name The Year: Carl Sagan, NYC, astronomer/author/professor (Cosmos, Broca's Brain), born. = 1934 Name The Year: "Kristallnacht" (Crystal Night)-Nazi stormtroopers attacked Jews. = 1938 Name The Year: Germany invades Norway and Denmark in WW II. = 1940 Name The Year: Chaim Weizmann 1st pres of Israel, dies at 57. = 1952 Name The Year: Abdul-Aziz ibn Sa'ud founder of Saudi Arabia, dies (born c 1880). = 1953 Name The Year: Cambodia (now Kampuchea) gains independence within French Union. = 1953 Name The Year: Dylan Thomas author-poet, dies in NY at 39. = 1953 Name The Year: Charles DeGaulle French pres, dies at 79. = 1970 Name The Year: Gary Kasparov (USSR) becomes World Chess Champion at age of 22. = 1985 Name The Year: East Berlin opens its borders. = 1989 Name The Year: President Bush announces DOUBLING of US forces in Gulf. = 1990 Name The Year: Yves Montand actor, dies at 70 from a heart attack. = 1991 Name The Year: Martin Luther, Eisleben, Germany, founded Protestantism, born. = 1483 Name The Year: George II king of England (1727-60), born. = 1683 Name The Year: Austrian Archduke Maximilian became emperor of Mexico. = 1864 Name The Year: Claude Rains London, actor (Invisible Man, Casablanca), born. = 1889 Name The Year: 41 suffragists are arrested in front of the White House. = 1917 Name The Year: Independence of Poland proclaimed by Jozef Pilsudski. = 1918 Name The Year: Moise Tshombe, president of Katanga, then premier of the Congo (Zaire), born. = 1919 Name The Year: Richard Burton, Wales, actor (Cleopatra, Virginia Woolf), born. = 1925 Name The Year: Hirohito enthroned as Emperor of Japan. = 1928 Name The Year: Roy Scheider Orange NJ, actor (All That Jazz, Jaws), born. = 1935 Name The Year: Kemal Atarok 1st pres of Turkey, dies. = 1938 Name The Year: 1st long distance telephone call without operator assistance. = 1951 Name The Year: "Sesame Street" premieres on PBS TV. = 1969 Name The Year: Leonid Brezhnev, Soviet 1st sectretary, dies of a heart attack at 75. = 1982 Name The Year: River Rhine (Germany) polluted by chemical spill. = 1986 Name The Year: Berlin Wall opens in East Germany. = 1989 Name The Year: Guerrillas battle with government forces in El Salvador. = 1989 Name The Year: Germans begin punching holes in the Berlin Wall. = 1989 Name The Year: Transit of Earth as seen from Mars. = 2084 Name The Year: Henry IV, Holy Roman emperor (1036-1106) = 1050 Name The Year: 41 Pilgrims signed a compact aboard Mayflower. = 1620 Name The Year: Yen Jo-chu, Chinese scholar of Ch'ing dynasty = 1636 Name The Year: Dutch and French agree to divide St. Maarten, Leeward Islands. = 1648 Name The Year: Charles IV, king of Spain (1788-1808) = 1748 Name The Year: Chrysanthemums are introduced into England from China. = 1790 Name The Year: Fyodor Dostoyevsky (in Russia), author (Crime and Punishment) = 1821 Name The Year: George S. Patton, U.S. military leader = 1885 Name The Year: Armistice Day -- WW I ends (at 11AM on Western Front). = 1918 Name The Year: Kurt Vonnegut, Jr., author (Slaughterhouse Five, Sirens of Titan) = 1922 Name The Year: During WWII Germany completed their occupation of France. = 1942 Name The Year: Daniel Ortega Saavedra, president of Nicaragua (1984- ) = 1945 Name The Year: Jigme Singye Wangchuk, king of Bhutan (1972- ) = 1955 Name The Year: Demi Moore, actor (Ghost) = 1963 Name The Year: Rhodesia proclaimed independence from Britain by PM Ian D. Smith. = 1965 Name The Year: Gemini XII launched on four-day flight (the final Gemini flight). = 1966 Name The Year: John Lennon and Yoko Ono appear nude on cover of "Two Virgins" album. = 1968 Name The Year: US Army turned over Long Bihn base to South Vietnamese army. = 1972 Name The Year: Portugal grants Angola independence (Natl Day). = 1975 Name The Year: The crew of Soyuz 35 returns to Earth aboard Soyuz 37. = 1980 Name The Year: 5th Space Shuttle Mission - Columbia 5 is launched (first commercial flight). = 1982 Name The Year: Rev Martin Luther King Sr, dies in Atlanta at 84. = 1984 Name The Year: Van Gogh's "Irises" sells for record $53.6 million at auction. = 1987 Name The Year: Baha'Ullah (Mirza Husayn Ali), founded Baha'i faith = 1817 Name The Year: Aleksandr Borodin, Russian composer = 1833 Name The Year: Auguste Rodin, sculptor (Kiss) = 1840 Name The Year: Jules Leotard performs the first Flying Trapeze circus act in Paris. He also designed the garment that bears his name. = 1859 Name The Year: Sun Yat-sen, father of modern China (ROC and PRC) = 1866 Name The Year: Harry A. Blackmun, U.S. Supreme Court justice = 1908 Name The Year: Austria becomes a republic. = 1918 Name The Year: Trotsky expelled from Soviet CP; Stalin becomes undisputed dictator. = 1927 Name The Year: Grace Kelly (in Philadelphia, PA), actor, Princess of Monaco = 1929 Name The Year: First Sunday football game in Philadelphia (previously illegal). = 1933 Name The Year: First known photo of Loch Ness monster (or whatever) is taken. = 1933 Name The Year: Richard H. Truly, U.S. astronaut (STS-2, 8) = 1937 Name The Year: Stefanie Powers (in Hollywood, CA), actor (Hart to Hart) = 1942 Name The Year: German battleship Tirpitz was sunk off Norway. = 1944 Name The Year: Neil Young, singer/songwriter = 1945 Name The Year: The first "autobank" (banking by car) was established, in Chicago. = 1946 Name The Year: Walt Disney's "Song Of The South" released. = 1946 Name The Year: Japanese premier Tojo sentenced to death by war crimes tribunal. = 1948 Name The Year: Nadia Comaneci (in Romania), Olympic gymnist = 1961 Name The Year: Venera 2 launched by Soviet Union toward Venus. = 1965 Name The Year: New Orleans elects first black mayor, Ernest `Dutch' Morial. = 1977 Name The Year: US space probe Voyager I approaches 77,000 miles of Saturn. = 1980 Name The Year: First balloon crossing of Pacific completed (Double Eagle V). = 1981 Name The Year: Second Space Shuttle Mission - Columbia 2 is launched. First time a spacecraft is launched twice. = 1981 Name The Year: Yuri V. Andropov succeeds Leonid Brezhnev. = 1982 Name The Year: Space shuttle astronauts snared a satellite first space salvage. = 1984 Name The Year: St. Augustine of Hippo (in Numidia, Algeria) = 354 Name The Year: St Nicholas I (the Great), pope (858-67) dies. = 867 Name The Year: English king Ethelred II launches massacre of Danish settlers. = 1002 Name The Year: Edward III, king of England (1327-77) = 1312 Name The Year: Edward John Trelawney, English traveler, author = 1792 Name The Year: Edwin Booth, American actor = 1833 Name The Year: Robert Louis Stevenson (in Scotland), writer (Treasure Island) = 1850 Name The Year: First shipment of canned pineapple from Hawaii. = 1895 Name The Year: 250 miners die in a fire at St. Paul Mine at Cherry, Ill. = 1909 Name The Year: "The Sheik", starring Rudolph Valentino, is released. = 1921 Name The Year: The Holland Tunnel, first underwater vehicular tunnel, opened between New York and New Jersey. = 1927 Name The Year: NBC forms first full sized symphony orchestra exclusively for radio. = 1937 Name The Year: Walt Disney's "Fantasia" is released. = 1940 Name The Year: British aircraft carrier Ark Royal sank in Mediterranean. = 1941 Name The Year: U.S. Supreme Court struck down segregation of races on public buses. = 1956 Name The Year: Cyclone kills estimated 300,000 in Bangladesh. = 1970 Name The Year: Mariner 9, first orbit of another planet, first orbit of Mars. = 1971 Name The Year: Karen Silkwood killed in a car crash. = 1974 Name The Year: First manned balloon flight across Pacific Ocean ended. = 1981 Name The Year: Korean boxer Duk Koo Kim fatally injured when KOed by Ray Mancini. = 1982 Name The Year: Vietnam War Memorial dedicated in Washington, DC. = 1982 Name The Year: Nevado del Ruiz volcano erupts in Colombia, kills 25,000. = 1985 Name The Year: Soviets launch first unmanned Space Shuttle in two-day test. = 1988 Name The Year: Samuel Pepys reports on first blood transfusion (between dogs). = 1666 Name The Year: William Pitt the Elder (Whig), British PM (1756-61, 1766-68) = 1708 Name The Year: Robert Fulton, built first commercial steamboat = 1765 Name The Year: Henri Dutrochet, discovered & named process of osmosis = 1776 Name The Year: Sir Charles Lyell, geologist = 1797 Name The Year: Claude Monet, impressionist painter = 1840 Name The Year: "Moby Dick", by Herman Melville, is published. = 1851 Name The Year: Jawaharlal Nehru, first Indian PM (1947-64), made fashion statement = 1889 Name The Year: Power plant at Niagara Falls begins operation. = 1896 Name The Year: Louise Brooks, silent screen star (American Venus, Pandora's Box) = 1906 Name The Year: Rosemary DeCamp (in Arizona) = 1910 Name The Year: First airplane flight from the deck of a ship. = 1910 Name The Year: Barbara Hutton, actress = 1912 Name The Year: Veronica Lake, actress = 1919 Name The Year: Brian Keith, actor = 1921 Name The Year: BBC began domestic radio service. = 1922 Name The Year: McLean Stevenson, actor (M = A = S = H) = 1929 Name The Year: Edward H. White II, astronaut (Gemini 4) = 1930 Name The Year: Fred Haise, astronaut (Apollo 13) = 1933 Name The Year: Hussein I, king of Jordan (1953- ) = 1935 Name The Year: During WW II, German planes destroyed most of Coventry, England. = 1940 Name The Year: Charles Phillip Arthur George Windsor, Prince of Wales = 1948 Name The Year: Kilauea Volcano's most spectacular eruption (in Hawaii). = 1959 Name The Year: Apollo 12 is launched. = 1969 Name The Year: Dow Jones closes above 1,000 for first time (1003.16). = 1972 Name The Year: Britain's Princess Anne marries a commoner, Capt. Mark Phillips. = 1973 Name The Year: Second Space Shuttle Mission, Columbia 2, returns to Earth. = 1981 Name The Year: U.S. Securities and Exchange Commission imposes a record $100 million penalty against inside-trader Ivan Boesky. = 1986 Name The Year: Johannes Kepler, planetary law-maker, dies. = 1630 Name The Year: Sir William Herschel, astronomer discovered Uranus = 1738 Name The Year: Gerhart Hauptmann, German writer (Before Dawn) (Nobel 1912) = 1862 Name The Year: Dom Pedro II, Emperor of Brazil, deposed; republic proclaimed. = 1889 Name The Year: Erwin Rommel, German field marshall, brilliant tactician = 1891 Name The Year: League of Nations holds first meeting, in Geneva. = 1920 Name The Year: Ed Asner, actor (Lou Grant) = 1929 Name The Year: Petula Clark (in England), singer = 1932 Name The Year: Walt Disney Art School created. = 1932 Name The Year: Anni-Frid (in Lyngsdtad, Sweden), singer (ABBA) = 1945 Name The Year: Actor Tyrone Power dies of a heart attack at 44. = 1958 Name The Year: Gemini XII returns to Earth. = 1966 Name The Year: Michael Adams in X-15 reaches altitude of 80 km. = 1967 Name The Year: 250,000 protesters peacefully demonstrate against Vietnam War. = 1969 Name The Year: President Jimmy Carter welcomes Shah of Iran. = 1977 Name The Year: Pope John Paul II began 5 day visit to West Germany. = 1980 Name The Year: Funeral services held in Moscow's Red Square for Leonid I. Brezhnev. = 1982 Name The Year: Baby Fae, who received a baboon's, heart died at a California medical center. = 1984 Name The Year: Soviet unmanned Space Shuttle completes 2 day test, lands just 8 miles from launch site in central Asia. = 1988 Name The Year: Pizarro seizes Incan emperor Atahualpa. = 1532 Name The Year: Rodolphe Kreutzer, French composer, virtuoso violinist = 1766 Name The Year: N.E. Guerin of New York receives a patent for his cork filled life preserver. = 1841 Name The Year: William Handy, blues musician, established the popularity of the blues in band music = 1873 Name The Year: 6,000 Armenians massacred by Turks in Kurdistan. = 1894 Name The Year: Paul Hindemith (in Germany), composer = 1895 Name The Year: Burgess Meredith, actor (Batman, Rocky) = 1908 Name The Year: Roosevelt establishes diplomatic relations with the Soviet Union. = 1933 Name The Year: Donna McKechnie (in Pontiac, Michigan) = 1942 Name The Year: The United Nations gets approval from the U.S. government for issuing postage stamps. = 1950 Name The Year: First speed-boat to exceed 200 miles per hour (D.M. Campbell, pilot). = 1955 Name The Year: First public announcement about Walt Disney World. = 1965 Name The Year: Venera 3 launched. (Later, the first man-made object to land on another planet when it crashed into Venus.) = 1965 Name The Year: Dr. Sam Sheppard freed after 9 years in jail, by a jury. = 1966 Name The Year: Lisa Bonet (in San Francisco, CA), actor (Cosby Show) = 1967 Name The Year: Donald Wolf, computer game creator = 1971 Name The Year: Skylab 4 and its crew are launched into earth orbit (The final Skylab mission). = 1973 Name The Year: Fifth Space Shuttle Mission - Columbia 5 returns to Earth. = 1982 Name The Year: Fourteenth Space Shuttle Mission - Discovery 2 returns to Earth. = 1984 Name The Year: Il Bronzino, Florentine painter = 1503 Name The Year: Elizabeth I ascends English throne upon death of Queen Mary. = 1558 Name The Year: Joost van den Vondel, Dutch poet, dramatist = 1587 Name The Year: Jean d'Alembert, French mathematician, scientist, philosopher = 1717 Name The Year: Louis XVIII, first post-revolutionary king of France (1814-24) = 1755 Name The Year: August Ferdinand Mobius, mathematician, inventor of Mobius strip = 1790 Name The Year: The Suez Canal opens. = 1869 Name The Year: Field Marshal Viscount Montgomery of Alamein (in Moville, Ireland) = 1887 Name The Year: Isamu Noguchi, sculptor = 1904 Name The Year: The Panama Canal opens for use. = 1913 Name The Year: Sculptor August Rodin died in Meudon, France. = 1917 Name The Year: Rock Hudson (in Winnetka, Ill), actor (Pillow Talk, A Farewell to Arms, McMillan and Wife) = 1925 Name The Year: Peter Cook, British actor, comedian = 1937 Name The Year: Gordon Lightfoot, folksinger = 1938 Name The Year: Martin Scorsese, director (Mean Streets, NY, NY) = 1942 Name The Year: Lauren Hutton (in South Carolina), model, actor (American Gigolo, Lassiter) = 1943 Name The Year: Danny DeVito, actor (Taxi, Ruthless People, Twins), director (War of the Roses) = 1944 Name The Year: Britain's House of Commons voted to nationalize steel industry. = 1948 Name The Year: Jeff Buckley (singer, son of Tim Buckley) = 1966 Name The Year: Russia lands unmanned remote-controlled vehicle on Moon. = 1970 Name The Year: Pres Nixon told AP "...people have got to know whether or not their pres is a crook. Well, I'm not a crook." = 1973 Name The Year: Egyptian President Sadat formally accepts invitation to visit Israel. = 1977 Name The Year: South Korean boxer Duk Koo Kim was legally declared dead. = 1982 Name The Year: Bartolomeu Dias discovers Cape of Good Hope. = 1497 Name The Year: Louis-Jacques-Mande Daguerre, developed a method of photography = 1789 Name The Year: Battle of Vertieres, in which Haitians defeat French. = 1803 Name The Year: Antarctica discovered by U.S. Navy Captain Nathaniel B. Palmer. = 1820 Name The Year: Sir W.S. Gilbert, playwright (of Gilbert and Sullivan fame) = 1836 Name The Year: U.S. and Canada begin the use of standard time zones (standard time zones established by the railroads in both countries). = 1883 Name The Year: Oahu Railway begins public service in Hawaii. = 1889 Name The Year: George H. Gallop, pollster (what's your opinion?) = 1901 Name The Year: Hay-Bunau-Varilla Treaty, gives US exclusive canal rights through Panama. = 1903 Name The Year: U.S. invades Nicaragua, later overthrows President Zelaya. = 1909 Name The Year: Lincoln Deachey performs first airplane loop-the-loop. = 1913 Name The Year: Latvia declares independence from Russia. = 1918 Name The Year: Alan B. Shepard, Jr., first American in space (Freedom 7, Apollo 14) = 1923 Name The Year: Mickey Mouse, cartoon character, sired by Walt Disney's pen = 1928 Name The Year: Walt Disney's Mickey Mouse debuts in New York in "Steamboat Willie". = 1928 Name The Year: William "Pete" Knight, X-15 pilot, born. = 1929 Name The Year: Brenda Vaccaro (in Brooklyn, NY) actor (Midnight Cowboy) = 1939 Name The Year: "Flowers and Trees" awarded first Academy Award for a cartoon. = 1932 Name The Year: Main span of San Francisco's Golden Gate Bridge joined. = 1936 Name The Year: Linda Evans, actor (Dynasty), born. = 1942 Name The Year: First ambassador to Canada, Ray Atherton, nominated. = 1943 Name The Year: Christopher Columbus discovers Puerto Rico. = 1493 Name The Year: Puerto Rico Discovery Day. = 1582 Name The Year: Charles I, king of England (1625-49); later executed by Parliament = 1600 Name The Year: Pilgrims reach Cape Cod on the Mayflower. = 1620 Name The Year: First protestant ministry society in New England. = 1644 Name The Year: Ferdinand de Lesseps, French diplomat, built Suez Canal = 1805 Name The Year: President Lincoln delivers his famous address at Gettysburg, PA. = 1863 Name The Year: Jose Raul Capablanca of Cuba, world chess champion (1921-27) = 1888 Name The Year: The pencil is invented. = 1895 Name The Year: Tommy Dorsey, band leader = 1905 Name The Year: Indira Gandhi, Indian prime minister (1966-77, 1980-84) = 1917 Name The Year: Treaty of Versailles and League of Nations rejected by U.S. Senate. = 1919 Name The Year: Dan Haggerty, actor (Grizzly Adams) = 1941 Name The Year: Calvin Klein, clothes designer = 1942 Name The Year: Kathleen Quinlan (in Mill Valley, CA), actor (Twilight Zone) = 1954 Name The Year: Ford Motor Company cancels the Edsel. = 1959 Name The Year: Jodie Foster (in Los Angeles, CA), actor (Silence of the Lambs, Nell, The Accused) = 1962 Name The Year: Egyptian President Anwar Sadat arrives in Israel. = 1977 Name The Year: Rev Jim Jones leads 911 people in suicide in Jonestown, Guyana. = 1978 Name The Year: Otto von Guericke, invented the air pump. = 1602 Name The Year: Britain declares war against Holland. = 1780 Name The Year: Andres Bello, Venezuelan diplomat, author, humanist = 1781 Name The Year: William Bundy invents the first timecard clock. = 1888 Name The Year: Edwin Hubble, US astronomer, discoverer of galaxies, red shift = 1889 Name The Year: Chester Gould, cartoonist (gave Dick Tracy a job) = 1900 Name The Year: Revolution broke out in Mexico, led by Francisco I. Madero. = 1910 Name The Year: The U.S. State Department starts requiring photographs for passports. = 1914 Name The Year: Maya Plisetkaya, prima ballerina = 1925 Name The Year: Robert Kennedy (in Brookline, Mass), Democratic Senator from New York, U.S. Attorney General; later assassinated = 1925 Name The Year: Veronica Hamel (in Philadelphia, PA), actor (Hill Street Blues) = 1943 Name The Year: International War Crimes Tribunal opens trial of Nazi leaders in Nuremberg. = 1945 Name The Year: Britain's Princess Elizabeth, marries Duke Philip Mountbatten in London's Westminster Abbey. = 1947 Name The Year: Bo Derek (in Long Beach, CA), actor? (10; Tarzan, Ape Man) = 1956 Name The Year: Spain's Gen. Francisco Franco dies in Madrid at 82. = 1975 Name The Year: Egyptian President Anwar Sadat became first Arab leader to address Israel's Knesset. = 1977 Name The Year: Steve Ptacek in Solar Challenger makes first solar-powered flight. = 1980 Name The Year: Anatoly Karpov, USSR retains world chess championship. = 1981 Name The Year: UN's WHO announces first global effort to combat AIDS. = 1986 Name The Year: Mayflower Compact signed by Pilgrims in Providencetown, MA harbor. = 1620 Name The Year: Jakob Bohme, German philosophical mystic, dies (birth date unknown). = 1624 Name The Year: Voltaire, thinker = 1694 Name The Year: Pilftre de Rozier and Marquis d'Arlandes make first free balloon flight. = 1783 Name The Year: William Beaumont, surgeon, studied digestion = 1785 Name The Year: Sir Samuel Cunard, founded first regular Atlantic steamship line = 1787 Name The Year: Benedict XV, 258th Roman Catholic pope (1914-22) = 1854 Name The Year: Rene Magritte, painter (This is Not a Pipe) = 1898 Name The Year: Coleman Hawkins, virtually created the tenor saxophone for jazz = 1904 Name The Year: Vivian Blaine, actor (Guys and Dolls) = 1923 Name The Year: Henry W. "Hank" Hartsfield, U.S. astronaut (STS-4, 41D, 61A) = 1933 Name The Year: First US ambassador to the USSR (W.C. Bullitt). = 1933 Name The Year: Marlo Thomas (in Detroit, Michigan), actor (That Girl) = 1938 Name The Year: Natalia Maskarova (in Lenningrad), ballerina = 1940 Name The Year: Juliet Mills (in London, England) = 1941 Name The Year: Goldie Hawn (in Washington, DC), actor (Butterflies Are Free, Foul Play, Private Benjamin) = 1945 Name The Year: Jack Benny (Violin) and Richard Nixon (Piano) play their famed duet. = 1959 Name The Year: The Verrazanno Narrows Bridge (Brooklyn to Staten Island) opens, becoming the world's longest suspension bridge. = 1964 Name The Year: The "Who Shot JR" episode of "Dallas" begins the tradition of season-ending cliff-hangers. = 1980 Name The Year: Wilhelm Friedemann Bach, composer, son of J.S. Bach = 1710 Name The Year: George Eliot (in England), novelist (Silas Marner) = 1819 Name The Year: Tarzan of the Apes, according to Edgar Rice Burroughs' novel. = 1888 Name The Year: Charles de Gaulle, pres of France (1958-69), general = 1890 Name The Year: International Radio Telegraphic Convention adopts "SOS" as new call for help. = 1906 Name The Year: Benjamin Britten, English composer = 1913 Name The Year: Geraldine Page (in Kirksville, MO), actor (Trip to Bountiful) = 1924 Name The Year: Owen K. Garriott, astronaut (Skylab 3, STS-9) = 1930 Name The Year: Robert Vaughn, actor (Man from U.N.C.L.E) = 1932 Name The Year: Billie Jean King (in California), tennis pro = 1943 Name The Year: Lebanon gains independence from France (National Day). = 1943 Name The Year: Jamie Lee Curtis (in Los Angeles, CA), actor, (Halloween, Trading Places, True Lies), Tony Curtis and Janet Leigh's daughter = 1958 Name The Year: Mariel Hemingway (in Ketchum, Idaho), actor (Star 80, Manhattan, Personal Best) = 1961 Name The Year: President John Kennedy assasinated in Dallas; Lee Harvey Oswald is assumed to have been the assasin. = 1963 Name The Year: UN Security council passes resolution 242, declaring that Israel must give back land won in the 6 day war. = 1967 Name The Year: Beatles release "The Beatles", their only double album. = 1968 Name The Year: Otto I (the Great), German king, Holy Roman emperor (962-73) = 912 Name The Year: Alfonso X (the Wise), king of Castile and Leon (1252) = 1221 Name The Year: Just past midnight, a sharp jolt causes Lake Merced to drop 30 feet. = 1852 Name The Year: Billy the Kid (William H. Bonney), criminal = 1859 Name The Year: Karl Branting, Swedish statesman, diplomat (Nobel Peace Prize 1921) = 1860 Name The Year: Battle of Chattanooga begins. = 1863 Name The Year: Patent granted for a process of making color photographs. = 1863 Name The Year: Boris Karloff, actor (Frankenstein, Isle of the Dead) = 1887 Name The Year: The first jukebox is installed, at a saloon in San Francisco. = 1889 Name The Year: Post Hospital at Presidio renamed Letterman General Hospital. = 1911 Name The Year: Krzysztof Penderecki, Polish composer (Hiroshima Threnody) = 1933 Name The Year: Vladislav N. Volkov, Soviet cosmonaut (Soyuz 7, 11) = 1935 Name The Year: Lens to provide zoom effects patented (F.G. Back). = 1948 Name The Year: Francie Larrieu, track runner = 1952 Name The Year: Merle Oberon dies at age 68. = 1979 Name The Year: Mae West dies at age 88. = 1980 Name The Year: 25th Anniversary of "Dr. Who" - And still going strong. "Anyone seen a Tardis around here?" = 1988 Name The Year: Destructive eruption of Vesuvius. = 1759 Name The Year: South Carolina passes Ordinance of Nullification. = 1832 Name The Year: Henre de Toulouse-Lautrec (in France), painter = 1864 Name The Year: Dale Carnegie, author (How to Win Friends and Influence People) = 1888 Name The Year: Charles Schultz, Peanuts Creator = 1922 Name The Year: Oscar "Big O" Robertson, basketball player = 1938 Name The Year: Agatha Christie's "The Mousetrap" opens in London. At last report, it is still running. = 1952 Name The Year: First US Presidential airplane christened. = 1954 Name The Year: First and only live murder on TV - Jack Ruby shoots Lee Harvey Oswald. = 1963 Name The Year: Apollo 12 returns to Earth. = 1969 Name The Year: Britain evacuated New York, their last military position in U.S. = 1783 Name The Year: Andrew Carnegie, steel industrialist, library builder = 1835 Name The Year: Carry Nation, scourge of barkeepers and drinkers = 1846 Name The Year: Alfred Nobel invents dynamite. = 1867 Name The Year: John XXIII (born Angelo Roncalli near Bergamo, Italy), 261st pope (1958-63) = 1881 Name The Year: John B. Meyenberg of St Louis patented evaporated milk. = 1884 Name The Year: Ricardo Montalban, actor (Fantasy Island, Star Trek II) = 1920 Name The Year: First Soviet liquid rocket attains altitude of 261 ft. = 1933 Name The Year: John Larroquette, actor (Night Court, Star Trek III) = 1937 Name The Year: Tina Turner (in Brownsville, Texas), singer, actor = 1941 Name The Year: John F. Kennedy, Jr. = 1960 Name The Year: First atomic reactor for research and development, Richland, Wa. = 1960 Name The Year: Greek Pres George Papadopoulos ousted in a bloodless military coup. = 1973 Name The Year: UN Secretary-General U Thant dies in NY of cancer at 65. = 1974 Name The Year: Netherlands grants Surinam independence (Natl Day). = 1975 Name The Year: O.J. Simpson gains 273 yards for Buffalo vs. Detroit. = 1976 Name The Year: Sugar Ray Leonard regains WBC welterweight championship. = 1980 Name The Year: Soyuz T-9 returns to Earth, 149 days after take-off. = 1983 Name The Year: William Schroeder, becomes second to receive Jarvik-7 artificial heart. = 1984 Name The Year: John Harvard, English clergyman, scholar, founder of Harvard U. = 1607 Name The Year: First lion to be seen in America was exhibited in Boston. = 1716 Name The Year: William Cowper, English poet = 1731 Name The Year: Captain Cook discovers Maui (in the Sandwich Islands). = 1778 Name The Year: Sarah Moore Grimke, American antislavery, women's rights advocate = 1792 Name The Year: Republican calendar replaces Gregorian calendar in France. = 1793 Name The Year: Louisa May Alcott, author (Little Women) = 1832 Name The Year: "Alice in Wonderland" is published. = 1865 Name The Year: The first baseball game played in enclosed field in San Francisco, at 25th and Folsom. = 1868 Name The Year: Willis Haviland Carrier, developed air-conditioning equipment = 1876 Name The Year: First Meteor photograph. = 1885 Name The Year: Norbert Wiener, inventor of cybernetics, absentminded-professor = 1894 Name The Year: Eugene Ionesco, French dramatist (Rhinoceros) = 1912 Name The Year: Robert Goulet (in Canada), singer, actor = 1933 Name The Year: Boris Yegorov, Soviet cosmonaut (Voskhod 1) = 1937 Name The Year: Rich Little (in Canada), impressionist = 1938 Name The Year: Nazis force 4.5 mil Warsaw Jews to live in walled ghetto. = 1940 Name The Year: India adopts a constitution as a British Commonwealth Republic. = 1949 Name The Year: China enters Korean conflict. = 1950 Name The Year: The fab fours' first recording session under the name Beatles (they were formerly the Silver Beatles). = 1962 Name The Year: France launches first satellite, a 92-pound A1 capsule. = 1965 Name The Year: First major tidal power plant opened at Rance estuary, France. = 1966 Name The Year: Federal jury found Lynette Fromme guilty of attempted assassination. = 1975 Name The Year: Yasuhiro Nakasone elected PM of Japan succeeding Zenko Suzuki. = 1982 Name The Year: 23rd Space Shuttle Mission - Atlantis 2 is launched. = 1985 Name The Year: Pope Urban II preaches first Crusade. = 1095 Name The Year: Anders Celsius, scientist, invented centigrade temperature scale = 1701 Name The Year: Sir Julius Benedict, composer = 1804 Name The Year: Cracow declared a free republic. = 1815 Name The Year: Chaim Weizmann, Israeli statesman = 1874 Name The Year: Charles A. Beard, American historian = 1874 Name The Year: Alfred Nobel establishes Nobel prize. = 1895 Name The Year: NY's Penn Station opens as world's largest railway terminal. = 1910 Name The Year: Alexander Dubcek, head of Czech Communist Party (1968-69) = 1921 Name The Year: Benigno Aquino, Jr., Philippine opposition leader; assassinated = 1932 Name The Year: Eddie Rabbitt, singer = 1941 Name The Year: Jimi Hendrix, guitarist = 1942 Name The Year: French navy at Toulon scuttled its ships and submarines to prevent them from falling into hands of Nazis. = 1942 Name The Year: Gen George C. Marshall named special U.S. envoy to China. = 1945 Name The Year: First rocket to intercept an airplane, White Sands, NM. = 1951 Name The Year: Playwright Eugene O'Neill dies in Boston at 65. = 1953 Name The Year: Beatles release "Magical Mystery Tour." = 1967 Name The Year: George Harrison releases 3 album set "All Things Must Pass." = 1970 Name The Year: Pope Paul VI wounded in chest during a visit to Philippines by a dagger-wielding Bolivian painter disguised as a priest. = 1970 Name The Year: Mars 2, Mars landing. First crash landing on Mars. = 1971 Name The Year: San Francisco Mayor George Moscone and City Supervisor Harvey Milk shot by Dan White. = 1978 Name The Year: Soyuz T-3 is launched. = 1980 Name The Year: Republic of Ireland gains consultative role in Northern Ireland. = 1985 Name The Year: Space shuttle Atlantis makes second flight, carries 7 (1 Mexican). = 1985 Name The Year: Magellan begins crossing the Pacific Ocean. = 1520 Name The Year: John Bunyan, English cleric, author (Pilgrim's Progress) = 1628 Name The Year: Jean Baptiste Lully, French composer, musician = 1632 Name The Year: William Blake, poet (Songs of Innocence and Experience) = 1757 Name The Year: Washington and his troops cross the Delaware River. = 1776 Name The Year: Friedrich Engels, Marx's collaborator = 1820 Name The Year: America's first auto race starts; 6 cars, 55 miles, winner averages a blazing 7 MPH. = 1895 Name The Year: Claude Levi-Strauss, Belgian social anthropologist = 1908 Name The Year: Admiral R.E. Byrd makes first South Pole flight. = 1929 Name The Year: Roosevelt, Churchill and Stalin met at Tehran. = 1943 Name The Year: Randy Newman, singer (Raindrops), songwriter = 1943 Name The Year: Alexander Godunov, composer = 1949 Name The Year: Paul Schaeffer, musician (Saturday Night Live, David Letterman), actor (This Is Spinal Tap), Thunder Bay, Ont. = 1949 Name The Year: Mauritania gains independence from France (National Day). = 1960 Name The Year: Mariner 4 launched. First spacecraft to fly by Mars. = 1964 Name The Year: Ninth Space Shuttle Mission - Columbia 6 is launched. = 1983 Name The Year: Reagan administration exceeds SALT II arms limitations for first time. = 1986 Name The Year: Cardinal Thomas Wolsey adviser to England's King Henry VIII, died. = 1530 Name The Year: Christian Doppler, discovered Doppler Effect (color shift) = 1803 Name The Year: Sir Ambrose Fleming, inventor of the diode = 1849 Name The Year: Colorado militia kills 150 peaceful Cheyenne Indians. = 1864 Name The Year: Antonio Egas Moniz, Portuguese lobotomist (Nobel 1949) = 1874 Name The Year: U.S. receives rights to Pearl Harbor, on Oahu, Hawaii. = 1887 Name The Year: U.S. declares martial law in Dominican Republic. = 1916 Name The Year: King Tut's Tomb is discovered in Egypt. = 1922 Name The Year: Italian composer Giacomo Puccini died in Brussels. = 1924 Name The Year: Richard Byrd and crew flies over the South Pole. = 1929 Name The Year: Diana Ladd = 1932 Name The Year: John Mayall, blues singer = 1933 Name The Year: Chuck Mangione, jazz musician, composer = 1940 Name The Year: Albania liberated from Nazi control (National Day). = 1944 Name The Year: Yugoslav Republic Day. = 1945 Name The Year: U.N. General Assmebly allows for a Jewish state in Palestine. = 1947 Name The Year: First underground atomic explosion, Frenchman Flat, Nevada. = 1951 Name The Year: President-elect Eisenhower visits Korea to assess the war. = 1952 Name The Year: Howie Mandel, actor (St Elsewhere, Walk Like a Man), comedian = 1955 Name The Year: Mercury 5 launches a chimp (Ham/Enos). = 1961 Name The Year: Beatles release "I Want to Hold Your Hand." = 1963 Name The Year: LBJ sets up Warren comm to investigate assassination of JFK. = 1963 Name The Year: Actor Natalie Wood drowns off Santa Catalina, Calif., at 43. = 1981 Name The Year: Cary Grant, dies in Davenport, Iowa, at 82. = 1986 Name The Year: .C. Cleopatra died = 30 B Name The Year: Andrea Doria, Genovese statesman, admiral = 1466 Name The Year: Jonathan Swift, satirist (Gulliver's Travels, A Modest Proposal) = 1667 Name The Year: Johann Lukas Schonlein, helped establish scientific medicine = 1793 Name The Year: Oliver Fisher Winchester, rifle maker = 1810 Name The Year: Samuel Clemens (a.k.a. Mark Twain) (at Hannibal, MO), author (Tom Sawyer, Huckleberry Finn) = 1835 Name The Year: Sir Winston Churchill (C), Brit. PM (1940-45, 1951-55) (Nobel 1953) = 1874 Name The Year: Virginia Mayo (in St Louis, MO) = 1920 Name The Year: Efrem Zimbalist, Jr., actor (77 Sunset Strip, The Untouchables) = 1923 Name The Year: Richard Crenna, actor = 1927 Name The Year: Dick Clark, America's oldest teenager (American Bandstand) = 1929 Name The Year: G. Gordon Liddy, tough guy, Watergate celebrity = 1930 Name The Year: Abbie Hoffman, inventor of LSD = 1936 Name The Year: The Soviet Union invades Finland over a border dispute. = 1939 Name The Year: Only one day after the U.N. decrees Israel's right to exist, Jewish settlements are attacked. = 1947 Name The Year: First meteorite known to have struck a woman - Sylacauga, Alabama. = 1954 Name The Year: Billy Idol, rocker (White Wedding) = 1955 Name The Year: First guided missile destroyer launched, the Dewey, Bath, Me. = 1958 Name The Year: USSR launches Zond 2 towards Mars. = 1964 Name The Year: Barbados gains independence from Britain (National Day). = 1966 Name The Year: BC Alexander of Macedon defeats Persian army at Gaugamela. = 331 Name The Year: Henry III, king of England (1216-72) = 1207 Name The Year: Charles VI, Holy Roman emperor (1711-40) = 1685 Name The Year: Maria Mitchell discovers a non-naked-eye comet. = 1847 Name The Year: First postcards are issued in Vienna. = 1869 Name The Year: William Edward Boeing, founded aircraft company = 1881 Name The Year: Vladimir Horowitz, pianist = 1904 Name The Year: Walter Matthau, actor (Odd Couple, Bad News Bears, Hopscotch) = 1920 Name The Year: Tom Bosley, actor (Happy Days, Murder She Wrote) = 1927 Name The Year: George Peppard, actor (Breakfast at Tiffany's, Blue Max, A-Team) = 1928 Name The Year: Start of USSR experimental calender. = 1929 Name The Year: Julie Andrews, actor, singer (Sound of Music, Mary Poppins, S.O.B.) = 1935 Name The Year: Germany annexes Sudetenland (1/3 of Czechoslovakia). = 1938 Name The Year: First jet propelled airplane tested. = 1942 Name The Year: People's Rep of China proclaimed (National Day). = 1949 Name The Year: First treaty signed by female ambassador - Eugenie Anderson. = 1951 Name The Year: First ultra high frequency (UHF) television station, Portland, OR. = 1952 Name The Year: Nigeria gains independence from Britain (National Day). = 1960 Name The Year: France, the U.S.S.R., Great Britain, and the U.S. signed away their control over a partitioned Germany. = 1990 Name The Year: BC Aristotle dies of indigestion = 322 Name The Year: Hans Lippershey offers the Dutch government a new invention -- the telescope. = 1608 Name The Year: Darwin returns to England aboard the HMS Beagle. = 1836 Name The Year: Ferdinand Foch, believed to be the leader responsible for Allies winning World War I = 1851 Name The Year: Mahatma K. Ghandi (at Porbandar, Kathiawad, India), pacifist = 1869 Name The Year: Italy annexes Rome and the Papal States; Rome made Italian capital. = 1870 Name The Year: Groucho Marx, comedian = 1895 Name The Year: Graham Greene, prolific English novelist (Brighton Rock) = 1904 Name The Year: Robert Runcie, archbishop of Canterbury = 1921 Name The Year: Spanky McFarland, actor, little rascal = 1928 Name The Year: Yuri N. Glazkov, Soviet cosmonaut (Soyuz 24) = 1939 Name The Year: Don McLean, singer, songwriter (American Pie, Vincent) = 1945 Name The Year: First self-sustaining nuclear chain reaction demonstrated in one of the squash court under Stagg Field, University of Chicago. = 1942 Name The Year: First "Peanuts" Comic Strip featuring Charlie Brown and Snoopy appears, in 9 newspapers = 1950 Name The Year: Sting, musician (Police), actor (Dune) = 1951 Name The Year: "Alfred Hitchcock Presents" premiers. = 1955 Name The Year: First atomic power clock exhibited -- New York City. = 1956 Name The Year: Rod Serling's "Twilight Zone" premieres on CBS. = 1959 Name The Year: Chubby Check (musician) born. = 1941 Name The Year: John Lennon's "Imagine" documentary premieres. = 1988 Name The Year: Louis X (the Stubborn), king of France (1314-16) = 1289 Name The Year: Richard Cromwell, lord protector of England (1658-59) = 1626 Name The Year: Mexico becomes a republic. = 1824 Name The Year: Battle of Corinth ends. = 1862 Name The Year: The 'Orient Express' begins its first run, linking Turkey to Europe by rail. = 1883 Name The Year: Charlton Heston, actor (Ten Commandments, Ben Hur, Planet of the Apes) = 1923 Name The Year: The comic strip "Dick Tracy" debuts. = 1931 Name The Year: Patty LaBelle, singer = 1944 Name The Year: Susan Sarandon (in New York City), actor (Great Waldo Pepper) = 1946 Name The Year: Armand Assante, actor (Q&A, Judge Dredd) = 1949 Name The Year: "Leave It to Beaver" debuts on CBS. = 1957 Name The Year: USSR launches Sputnik I, the first artificial earth satellite. = 1957 Name The Year: USSR Luna 3 sent back first photos of Moon's far side. = 1959 Name The Year: Lesotho (Basutoland) gains independence from Britain (National Day). = 1966 Name The Year: Janis Joplin dies at age 27. = 1970 Name The Year: 21st Space Shuttle Mission - Atlantis 1 is launched. = 1985 Name The Year: Graham Chapman, member of the Monty Python team, dies from cancer. = 1989 Name The Year: Gregorian calendar introduced in Italy, other Catholic countries. = 1582 Name The Year: Louis Lumiere with brother Auguste made 1st motion picture in 1895, born. = 1864 Name The Year: Last day of Julian calendar in Alaska. = 1867 Name The Year: Ray Kroc Illinois, founder of MacDonalds/baseball team owner (San Diego Padres), born. = 1902 Name The Year: Bulgaria declares independence from Turkey, Ferdinand I becomes Tsar. = 1908 Name The Year: Portugal overthrows monarchy, proclaims republic. = 1910 Name The Year: Present constitution of Liechtenstein comes into effect. = 1921 Name The Year: Sir Bob Geldof pop musician (Boomtown Rats, Band Aid), born. = 1951 Name The Year: Clive Barker, novelist (Hell Raiser) = 1952 Name The Year: Michael Andretti Indy-car racer/Auto Hall of Fame (elected 1986), born. = 1962 Name The Year: Lech Walesa wins the Nobel Peace Prize. = 1983 Name The Year: Cincinnati jury acquits art gallery of obsentity (Mappelthorpe photos). = 1990 Name The Year: Li Ta-chao cofounder with Mao Tse-tung of Chinese Communist Party, born. = 1888 Name The Year: Thomas Edison shows his 1st motion picture. = 1889 Name The Year: Austria annexes Bosnia and Herzegovina. = 1908 Name The Year: Thor Heyerdahl Norway, anthropologist/explorer (Kon Tiki, Aku-Aku), born. = 1914 Name The Year: USSR adopts experimental calendar. = 1923 Name The Year: Chiang Kai-Shek becomes president of China. = 1928 Name The Year: Hitler announces he has no intention of war with Britain and France. = 1939 Name The Year: Stephanie Zimbalist NYC, actress (Remington Steele, Centennial), born. = 1956 Name The Year: Soviet Luna 3, 1st successful photographic spacecraft, impacts Moon. = 1959 Name The Year: Yom Kippur War begins as Syria and Egypt attack Israel. = 1973 Name The Year: John Hathaway completes a bicycle tour of every continent in the world and cycling 50,600 miles. = 1976 Name The Year: Pres Ford says there is "no Soviet domination in Eastern Europe". = 1976 Name The Year: Pope John Paul II is 1st Pope to visit the White House. = 1979 Name The Year: Anwar Sadat assassinated. = 1981 Name The Year: Bette Davis dies at 81. = 1989 Name The Year: Elizabeth Taylor weds for the 8th time (Larry Fortensky). = 1991 Name The Year: Turkish fleet defeated by Spanish and Italians in Battle of Lepanto. = 1571 Name The Year: Edgar Allen Poe dies in Baltimore at 40. = 1849 Name The Year: Niels Bohr, physicist, expanded quantum physics (Nobel 1922), born. = 1885 Name The Year: Spain abolishes slavery in Cuba. = 1886 Name The Year: Crete unites with Greece. = 1908 Name The Year: Georgia Tech defeats Cumberland College 222-0 in gridiron (record). = 1915 Name The Year: Al Martino, singer, born. = 1927 Name The Year: First infra-red photograph, Rochester, NY. = 1931 Name The Year: Desmond Tutu, Anglican Archbishop of South Africa (Nobel Peace Prize 1982), born. = 1931 Name The Year: Oliver North, arms dealer, born. = 1943 Name The Year: Dem. Rep of Germany (East) formed (National Day). = 1949 Name The Year: U.S. forces invade Korea by crossing the 38th parallel. = 1950 Name The Year: John Cougar Mellencamp, singer, born. = 1951 Name The Year: Yo-Yo Ma, cellist, born. = 1955 Name The Year: "Route 66" premiers. = 1960 Name The Year: Motion Picture Association of America adopts film rating system. = 1968 Name The Year: 21st Space Shuttle Mission - Atlantis 1 returns to Earth. = 1985 Name The Year: The supernova called "Kepler's nova" is first sighted. = 1604 Name The Year: J. Frank Duryea, with his brother, invented first auto built and operated in the US. = 1869 Name The Year: Ejnar Hertzsprung, Danish astronomer (Hertzsprung-Russell diagram), born. = 1873 Name The Year: Eddie Rickenbacker, aviator, born. = 1890 Name The Year: Juan Peron, president of Argentina (1946-55, 1973-74), born. = 1895 Name The Year: Rouben Mamoulian, movie director, born. = 1897 Name The Year: Karl Nessler demonstrates the first 'permanent wave' for hair, in London. = 1906 Name The Year: Sgt. Alvin York single-handedly kills 25, captures 132 Germans. = 1918 Name The Year: Frank Herbert, sci-fi writer (Dune), born. = 1920 Name The Year: Ozzie and Harriet Nelson married. = 1935 Name The Year: David Carradine, actor (Kung fu, Death Race 2000), born. = 1940 Name The Year: Chevy Chase, comedian, actor (SNL, Vacation, Fletch, Caddyshack), born. = 1943 Name The Year: "Adventures of Ozzie and Harriet" debut on CBS radio. = 1944 Name The Year: President Truman announced atomic bomb secret shared with Britain and Canada. = 1945 Name The Year: Sigourney Weaver (in Los Angeles), actor (Aliens, GhostBusters), born. = 1949 Name The Year: Johnny Ramone, rock guitarist, born. = 1951 Name The Year: N. Korea reports 100% election turnout, 100% vote for Workers' Party. = 1962 Name The Year: Soviet author Alexander I. Solzhenitsyn awarded Nobel Prize for Lit. = 1970 Name The Year: Kenneth Warby sets world speed record on water (514 kph). = 1978 Name The Year: Pres. Reagan greeted predecessors Jimmy Carter, Gerald R. Ford and Richard Nixon before their trip to Egypt for Anwar Sadat's funeral. = 1981 Name The Year: OJ Simpson acquitted for double murder of his Ex-wife Nicole Brown Simpson and Ronald Goldman. = 1995 Name The Year: Husain ibn 'Ali, Shi'i religious leader, enters martyrdom. = 680 Name The Year: Leif Ericson discovers "Vinland" (possibly America). = 1000 Name The Year: Korean Hangual alphabet devised. = 1446 Name The Year: Miguel de Cervantes, novelist (Don Quixote), born. = 1547 Name The Year: Charles X, king of France (1824-30); deposed, born. = 1757 Name The Year: Jacques Tati, director (Traffic, Playtime, My Uncle), born. = 1908 Name The Year: John Lennon, rocker/Beatle, born. = 1940 Name The Year: John Entwistle, rocker (The Who), born. = 1944 Name The Year: Peter Tosh, Jamaica, reggae musician, born. = 1944 Name The Year: 1st electric blanket manufactured; sold for US$39.50. = 1946 Name The Year: Scott Bakula, actor (Quantum Leap, The Invaders), born. = 1954 Name The Year: Tanganyika becomes independent within the British Commonwealth. = 1961 Name The Year: Volcano eruptions on Tristan de Cunha (South Atlantic). = 1961 Name The Year: Uganda becomes a republic within the British Commonwealth. = 1963 Name The Year: Che Guevara executed in Bolivia. = 1967 Name The Year: Khmer Republic (Cambodia) declares independence. = 1970 Name The Year: Elvis and Priscilla Presley divorce after 6 years. = 1973 Name The Year: Saddam threatens to hit Israel with a new missile. = 1990 Name The Year: Henry Cavendish, English physicist, chemist = 1731 Name The Year: Giuseppe Verdi, composer of operas (Rigoletto, Aida, Otello), born. = 1813 Name The Year: Emily Dickinson, poet, born. = 1830 Name The Year: Neptune's moon Triton discovered by William Lassell. = 1846 Name The Year: Fridtjof Nansen, Norwegian Arctic explorer, humanitarian (Nobel 1922), born. = 1861 Name The Year: Griswold Lorillard wears the first dinner jacket to the Autumn Ball in Tuxedo Park. Thus the name 'tuxedo.' = 1886 Name The Year: Ivo Andric, Yugoslav novelist (Bridge on the Drina) (Nobel '61), born. = 1892 Name The Year: Lin Yi-t'ang, Chinese writer (My Country and My People), born. = 1895 Name The Year: China Revolutionaries under Sun Yat-sen overthrew Manchu dynasty. = 1911 Name The Year: Pacific and Atlantic mix as engineers blow Gamboa Dam, opening the Panama Canal. = 1913 Name The Year: James Clavell, author (Shogun, Tai Pan, King Rat, Noble House, Whirlwind), born. = 1924 Name The Year: Harold Pinter, playwright, born. = 1930 Name The Year: First synthetic detergent for home use marketed. = 1933 Name The Year: George Gershwin's "Porgy and Bess" opened in New York City. = 1935 Name The Year: Germany completed annexation of Czechoslovakia's Sudetenland. = 1938 Name The Year: Chiang Kai-shek took the oath of office as president of China. = 1943 Name The Year: Ben Vereen, dancer, actor, singer, born. = 1946 Name The Year: David Lee Roth, rocker, born. = 1955 Name The Year: Treaty banning atmospheric nuclear tests signed by US, UK, USSR. = 1963 Name The Year: 18th Summer Olympic Games opened in Tokyo. = 1964 Name The Year: Fiji gains independence from Britain (National Day). = 1970 Name The Year: Israel formally signs Sinai accord with Egypt. = 1975 Name The Year: 4,500 people die when a pair of earthquakes struck NW Algeria. = 1980 Name The Year: Very Large Array (VLA) radio telescope network dedicated. = 1980 Name The Year: Pope John Paul II proclaimed Rev. M Kolbe, who volunteered to die in place of another inmate at Auschwitz concentration camp, a saint. = 1982 Name The Year: U.S. fighter jets force Egyptian plane carrying hijackers of Italian ship Achille Lauro to land in Italy, gunmen were placed in custody. = 1985 Name The Year: Wilhelm Olbers, discoverer of asteroids Pallas and Vesta, born. = 1758 Name The Year: The Juliana, the first steam-powered ferryboat, begins operation. = 1811 Name The Year: Henry John Heinz, founded a prepared-foods company, born. = 1844 Name The Year: Art Blakey, jazz drummer (Jazz Messengers), born. = 1919 Name The Year: Pioneer 1 launched; first spacecraft launched by NASA. = 1958 Name The Year: Pope John XXIII convenes 21st Roman Catholic ecumenical council, Vatican II, in Rome. = 1962 Name The Year: "Saturday Night Live" premiers. = 1975 Name The Year: Soyuz 25 returns to Earth. = 1977 Name The Year: Cosmonauts Popov and Ryumin set space endurance record of 184 days. = 1980 Name The Year: 200,000 gays march for civil rights in Washington. = 1987 Name The Year: Edward VI king of England (1547-53), born. = 1537 Name The Year: Pedro I 1st emperor of Brazil (1822-31), king of Portugal, born. = 1798 Name The Year: Elmer A Sperry inventor (gyrocompass), born. = 1860 Name The Year: Matsuo Basho greatest Japanese haiku poet, dies. = 1694 Name The Year: Charles Macintosh of Scotland begins selling raincoats (Macs). = 1823 Name The Year: Robert E Lee General of the Confederate Army, dies at 63. = 1870 Name The Year: Luciano Pavarotti Moderna Italy, operatic tenor (Oh Giorgio), born. = 1935 Name The Year: Launch of Voskhod 1, 1st 3 man crew (Komarov, Feokistov, Yegorov). = 1964 Name The Year: 19th modern Olympic games opens in Mexico City. = 1968 Name The Year: Equatorial Guinea gains independence from Spain (National Day). = 1968 Name The Year: Hua Guo-feng succeeds Mao Tse-tung as chairman of Communist Party. = 1976 Name The Year: Representatives of Israel and Egypt open talks in Washington. = 1978 Name The Year: IRA bombs hotel where Margaret Thatcher is staying. = 1984 Name The Year: Ed Sullivan TV host (Ed Sullivan Show), dies at 73. = 1974 Name The Year: Washington lays cornerstone of the Executive Mansion (White House). = 1792 Name The Year: Yves Montand, France, actor/singer (Z, Napoleon), born. = 1921 Name The Year: Lenny Bruce comedian, arrested on obsenity charges, born. = 1925 Name The Year: Margaret Thatcher, (Tory) British PM (1979-90), Iron Lady, born. = 1925 Name The Year: Paul Simon, singer (Kodachrome, Graceland), born. = 1942 Name The Year: Italy declares war on former ally Germany. = 1943 Name The Year: US 1st army begins battle of Aachen. = 1944 Name The Year: "Beatlemania" is coined after the Beatles appear at the Palladium. = 1963 Name The Year: Soyuz 8 is launched. = 1969 Name The Year: 1st military use of trained dolphins (US Navy in Persian Gulf). = 1987 Name The Year: Battle of Hastings, in which William the Conqueror wins England. = 1066 Name The Year: Trial of Mary, Queen of Scots, for conspiracy against Queen Elizabeth I, begins. = 1586 Name The Year: James II, king of England (1685-88), born. = 1633 Name The Year: George Grenville, British prime minister (1763-65), born. = 1712 Name The Year: British arrest Irish nationalist Daniel O'Connell for conspiracy. = 1843 Name The Year: George Eastman patents paper-strip photographic film. = 1884 Name The Year: e e cummings, poet, born. = 1894 Name The Year: Lillian Gish, silent film and stage actor (Birth of a Nation), born. = 1896 Name The Year: Theodore Roosevelt is shot in the chest in Milwaukee while campaigning for President with the Bull Moose Party. = 1912 Name The Year: Roger Moore, actor, (The Saint, numerous James Bond movies), born. = 1926 Name The Year: German Field Marshal Erwin Rommel commits suicide rather than face trial for his part in an attempt to overthrow Hitler. = 1944 Name The Year: Chuck Yeager pilots the world's first supersonic flight (Mach 1.015 at 12,800 m) at Muroc, CA. = 1947 Name The Year: 14 US Communist Party leaders convicted of sedition. = 1949 Name The Year: Joe Engle in X-15 reaches 80 km. = 1965 Name The Year: Soyuz 23 launched to Salyut 6, but return without docking. = 1976 Name The Year: Bing Crosby dies in Madrid, Spain. = 1977 Name The Year: First TV movie from a TV series - "Rescue from Gilligan's Island". = 1978 Name The Year: C Virgil (Publius Vergilius Maro) (Mantua, Italy), poet (Aeneid), born. = 70 B Name The Year: King Henry VIII of England orders bowling lanes to be built at Whitehall, in London. = 1520 Name The Year: Akbar, Indian Mughal emperor (1556-1605), born. = 1542 Name The Year: Catholic countries switch to Gregorian calendar cut 10 prior days. = 1582 Name The Year: Jean Pilftre de Rozier makes captive-balloon ascent. = 1783 Name The Year: Jose Miguel Carrera, president of Chile (1811-14), born. = 1785 Name The Year: Asaph Hall, astronomer, discovered the moons of Mars, Phobos and Deimos, born. = 1829 Name The Year: Friedrich Nietzsche, philosopher, Ubermensch, born. = 1844 Name The Year: John Kenneth Galbraith, economist, born. = 1908 Name The Year: Arthur Schlesinger, Jr., historian, born. = 1917 Name The Year: Mata Hari executed by firing squad outside of Paris. = 1917 Name The Year: Mario Puzo, author (Godfather), born. = 1921 Name The Year: LaGuardia Airport opened in New York City. = 1939 Name The Year: Penny Marshall (in New York City), actor (Laverne and Shirley), born. = 1943 Name The Year: Richard Carpenter, musician (Carpenters), born. = 1946 Name The Year: Nazi Hermann Goering poisons himself in prison. = 1946 Name The Year: "I Love Lucy" premiers. = 1951 Name The Year: Princess Sarah 'Fergie' Ferguson, the Duchess of York, born. = 1959 Name The Year: "The Untouchables" premiers. = 1959 Name The Year: Kosygin and Brezhnev replace Soviet premier Nikita Krushchev. = 1964 Name The Year: Albrecht von Haller, the father of experimental physiology = 1708 Name The Year: Noah Webster, lexicographer = 1758 Name The Year: Marie Antoinette beheaded in France. = 1793 Name The Year: Dentist William T. Morton demonstrated the effectiveness of ether. = 1846 Name The Year: Oscar (Fingal O'Flahertie Wills) Wilde (Dublin, Ireland), author (Picure of Dorian Gray), wit, dandy. = 1854 Name The Year: Sir Austen Chamberlain, British foreign secretary (Nobel 1925) = 1863 Name The Year: A hotel in Boston becomes the first to have indoor plumbing. = 1869 Name The Year: Eugene O'Neill, playwright (Desire Under the Elms) (Nobel 1936) = 1888 Name The Year: Enver Hoxha, post-war leader of Albania = 1908 Name The Year: Margaret Sanger opens the first public birth control clinic (Brooklyn, NY). = 1916 Name The Year: Disney Co. founded. = 1923 Name The Year: Angela Lansbury, actor (Sweeney Todd; Murder, She Wrote) = 1925 Name The Year: Gunter Grass, German novelist, poet (The Tin Drum) = 1927 Name The Year: World Food Day. = 1945 Name The Year: Ten Nazi leaders hanged as war criminals after Nuremberg trials. = 1946 Name The Year: Cuban missile crisis began as JFK becomes aware of missiles in Cuba. = 1962 Name The Year: China becomes world's 5th nuclear power. = 1964 Name The Year: Anwar Sadat elected president of Egypt, succeeding Gamal Abdel Nasser. = 1970 Name The Year: Kissinger and Le Duc Tho jointly awarded Nobel peace prize. = 1973 Name The Year: Soyuz 23 returns to Earth. = 1976 Name The Year: Polish Cardinal Karol Wojtyla elected supreme pontiff-Pope John Paul II. = 1978 Name The Year: Israel's General Moshe Dayan dies at 66. = 1981 Name The Year: Mt Palomar Observatory first to detect Halley's comet on 13th return. = 1982 Name The Year: Shultz warns US will withdraw from UN if they vote to exclude Israel. = 1982 Name The Year: Anglican Bishop Desmond Tutu named Nobel Peace Prize winner. = 1984 Name The Year: Intel introduces 32-bit 80386 microcomputer chip. = 1985 Name The Year: 18-month-old Jessica McClure is rescued 58 hours after she fell 22 feet into a well shaft in Midland, TX. = 1987 Name The Year: 175-kph winds cause blackout in London, much of southern England. = 1987 Name The Year: Jazz drummer Art Blakey dies. = 1990 Name The Year: Columbus sights the isle of San Salvador. = 1492 Name The Year: Marie Antoinette executed. = 1793 Name The Year: Alexandrine-Pieternella-Fran‡oise Tinn, explored the White Nile, born. = 1835 Name The Year: Gustav Kirchoff, discoverer of the laws of spectroscopy, dies. = 1887 Name The Year: Arthur Miller, playwright (Death of a Salesman, The Crucible), born. = 1915 Name The Year: Rita Hayworth (in New York), actor, alzheimer victim, born. = 1919 Name The Year: Tom Poston, comedian, actor (Newhart), born. = 1921 Name The Year: Al Capone convicted of tax evasion, sentenced to 11 years in prison. = 1931 Name The Year: Albert Einstein arrives in the US, a refugee from Nazi Germany. = 1933 Name The Year: William A. Anders, astronaut (Apollo 8), born. = 1933 Name The Year: Robert 'Evel' Knievel, motorcycle daredevil, born. = 1938 Name The Year: Juan Peron becomes dictator of Argentina. = 1945 Name The Year: Margot Kidder (in Yellowknife), actor (Superman), born. = 1948 Name The Year: George Wendt, actor (Cheers), born. = 1949 Name The Year: Britain's Queen Elizabeth and Prince Philip visit the White House. = 1957 Name The Year: Pete Knight in X-15 reaches 85 km about Earth. = 1967 Name The Year: Soyuz 7 returns to Earth. = 1969 Name The Year: The Arab oil embargo begins. It will last until March, 1974. = 1973 Name The Year: Mother Teresa of India was awarded the Nobel Peace Prize. = 1979 Name The Year: Danes defeat Saxons at Battle of Assandun (Ashingdon). = 1016 Name The Year: Louis XIV revokes Edict of Nantes, outlaws Protestantism, hastens onset of French Revolution. = 1685 Name The Year: Treaty of Aix-la-Chapelle, ends War of Austrian Succession. = 1748 Name The Year: In a NY bar decorated with bird tail, customer orders "cock tail". = 1776 Name The Year: US takes formal possession of Alaska from Russia ($7.2 million). = 1867 Name The Year: Belgium annexes Congo Free State. = 1908 Name The Year: Comte de Lambert of France sets airplane altitude record of 300 m. = 1909 Name The Year: British Broadcasting Corporation (BBC) established. = 1922 Name The Year: Chuck Berry, St Louis, USA, rocker (Roll over Beethoven), born. = 1926 Name The Year: Thomas Alva Edison inventor, dies in West Orange, NJ, at 84. = 1931 Name The Year: Lee Harvey Oswald, JFK's assassin, born. = 1939 Name The Year: Soviet troops invade Czechoslovakia during WW II. = 1944 Name The Year: Pam Dawber Detroit, actress (Mindy-Mork and Mindy), born. = 1951 Name The Year: Martina Navratilova, Prague, Czech Republic, tennis champion (Wimbeldon 1989,79,82-87), born. = 1956 Name The Year: Jean-Claude Van Damme, Belgium, actor (Kickboxer, No Retreat) = 1958 Name The Year: Wynton Marsalis, New Orleans La, jazz trumpeter (Grammy 1983), born. = 1961 Name The Year: I.O.C. votes Mexico City to host 1968 Olympics. = 1963 Name The Year: Soviet Venera 4 becomes the 1st probe to send data back from Venus. = 1967 Name The Year: Walt Disney's "Jungle Book" is released. = 1967 Name The Year: Police find 219 grains of cannabis resin in John and Yoko's apartment. = 1968 Name The Year: US Olympic Committee suspends Tommie Smith and John Carlos for giving "black power" salute as a protest during victory ceremony. = 1968 Name The Year: Napoleon begins his retreat from Moscow. = 1812 Name The Year: Wagner's opera Tannhauser performed for 1st time. = 1845 Name The Year: Auguste Lumiere, made 1st movie (Workers Leaving Lumiere Factory), born. = 1862 Name The Year: World's largest gold nugget (215 kg) found in New South Wales. = 1872 Name The Year: Tripoli (Libya) passes from Turkish to Italian control. = 1912 Name The Year: Divine (Harris Glenn Milstead) film actor, born. = 1945 Name The Year: John Lithgow, actor (Harry and the Hendersons, 3rd Rock From The Sun), born. = 1945 Name The Year: UN forces entered Pyongyang, the capital of North Korea. = 1950 Name The Year: Pres Harry S Truman formally ends state of war with Germany. = 1951 Name The Year: France grants Mauritania independence. = 1960 Name The Year: Martin Luther King Jr arrested in Atlanta sit-in. = 1960 Name The Year: The US imposes an embargo on exports to Cuba. = 1960 Name The Year: Evander Hollyfield, 2-time Heavyweight boxing champ, born. = 1962 Name The Year: Beatles record "I Want to Hold Your Hand". = 1963 Name The Year: Mariner 5 makes fly-by of Venus. = 1967 Name The Year: US warships destroy 2 Iranian oil platforms in Persian Gulf. = 1987 Name The Year: Britain bans broadcast interviews with IRA members. = 1988 Name The Year: Battle of Sekigahara sets Tokugawa clan as Japan's rulers (shoguns). = 1600 Name The Year: Sir Christopher Wren, England, astronomer/great architect, born. = 1632 Name The Year: Maria Theresa became ruler of Austria, Hungary and Bohemia. = 1740 Name The Year: German Kingdom of Westphalia abolished. = 1813 Name The Year: Arthur Rimbaud, France, poet/adventurer (Illuminations), born. = 1854 Name The Year: Margaret Dumont, actress-Marx Brothers' foil, born. = 1889 Name The Year: Mickey Mantle, NY Yankee home run slugger, born. = 1931 Name The Year: William Christopher, actor (Father Mulcahy-M = A = S = H), born. = 1932 Name The Year: Martin Landau, actor (Mission Impossible, Space 1999, Tucker), born. = 1934 Name The Year: Revolution by workers and students in Guatemala. = 1944 Name The Year: Tom Petty, Florida, USA, singer, born. = 1953 Name The Year: South Africa begins trial of Nelson Mandela and 8 others on conspiracy. = 1963 Name The Year: Herbert Hoover 31st president of US, dies in NY at 90. = 1964 Name The Year: Jacqueline Kennedy marries Aristotle Onassis. = 1968 Name The Year: Harlow Shapley discoverer of the Sun's position in the galaxy, dies. = 1972 Name The Year: Britain ends suspects right to remain silent in crackdown on IRA. = 1988 Name The Year: 3 members of 2 Live Crew acquitted on obsenity charges in Florida. = 1990 Name The Year: Antiwar protest marches begin in 20 US cities (US-Iraq). = 1990 Name The Year: Samuel Taylor Coleridge (in England), poet, born. = 1772 Name The Year: U.S. Navy frigate USS Constitution, Old Ironsides, launched in Boston. = 1797 Name The Year: Alfred Bernhard Nobel (in Stockholm, Sweden), created dynamite and Peace Prizes, born. = 1833 Name The Year: Severe earthquake at 7:53 a.m., centered in Hayward, Calif. = 1868 Name The Year: Thomas Edison commercially perfects the light bulb. = 1879 Name The Year: Sir Georg Solti, conductor, born. = 1912 Name The Year: Martin Gardner, Scientific American math and puzzles columnist, born. = 1914 Name The Year: Dizzy Gillespie, trumpeter, a creator of modern jazz, born. = 1917 Name The Year: Margaret Owen sets world typing speed record of 170 wpm for 1 min. = 1918 Name The Year: Deutsches Museum, Munich, first Walther Bauersfeld's Zeiss Planetarium. = 1923 Name The Year: Manfred Mann, musician, born. = 1940 Name The Year: US troops captured Aachen, first large German city to fall (WW II). = 1944 Name The Year: Women in France allowed to vote for the first time. = 1945 Name The Year: Facsimile high-speed radio transmission demonstrated, Washington DC. = 1948 Name The Year: Carrie Fisher (in Beverly Hills), actor (Star Wars, Blues Brothers), born. = 1956 Name The Year: Guggenheim Museum, designed by Frank Lloyd Wright, opens in New York. = 1959 Name The Year: JFK and Nixon clashed in 4th and final pres debate. = 1960 Name The Year: Ejnar Hertzsprung, Danish astrophysicist dies at 94. = 1967 Name The Year: Thousands opposing Vietnam War tried to storm the Pentagon. = 1967 Name The Year: Venera 9, first craft to orbit the planet Venus launched. = 1975 Name The Year: US recalls William Bowdler, ambassador to South Africa. = 1977 Name The Year: Steve Jones runs Chicago Marathon in world record 2 h 8 m 5 s. = 1984 Name The Year: B.C. Universe created at 8:00 PM, according to the 1650 pronouncement of Anglican archbishop James Usher. = 4004 Name The Year: Andre-Jacques Garnerin makes the first parachute jump from a balloon (Paris, France). = 1797 Name The Year: Franz Liszt (in Hungary), Romantic composer, virtuoso pianist, born. = 1811 Name The Year: John Reed, journalist who reported on Mexican, Russian revolutions, born. = 1887 Name The Year: Timothy Leary, psychologist, drug testing advocate, born. = 1920 Name The Year: Charles "Pretty Boy" Floyd shot dead by FBI in Ohio. = 1934 Name The Year: Christopher Lloyd, actor (Taxi, Star Trek III, Back to the Future, Addams Family), born. = 1938 Name The Year: Annette Funichello (in Utica, NY), mouseketeer, actor, born. = 1942 Name The Year: Catherine Deneuve (in Paris, France), (Repulsion), born. = 1943 Name The Year: Jeff Goldblum, actor (The Fly, Silverado, Buckaroo Bonzai, ID4), born. = 1952 Name The Year: Laos gains full independence from France. = 1953 Name The Year: West Germany joins North Atlantic Treaty Organization (NATO). = 1954 Name The Year: JFK imposes naval blockade on Cuba, beginning missile crisis. = 1962 Name The Year: Apollo 7 returns to Earth. = 1968 Name The Year: Security Council Resol 338 - a cease fire to the Yom Kippur War. = 1973 Name The Year: Soviet spacecraft Venera 9 lands on Venus. = 1975 Name The Year: Deposed Shah of Iran arrived in NY for medical treatment. = 1979 Name The Year: Walt Disney World's 100-millionth guest. = 1979 Name The Year: Nicolas Appert, inventor of food canning, bouillon tablet, born. = 1752 Name The Year: Slaves revolt in Haiti (later suppressed). = 1790 Name The Year: Robert Bridges, poet laureate of England (The Testament of Beauty), born. = 1844 Name The Year: Felix Bloch, U.S. physicist (Nobel 1952), born. = 1905 Name The Year: Karl Jansky, discoverer of cosmic radio emissions in 1932, born. = 1905 Name The Year: 25,000 women marched in NYC, demanding the right to vote. = 1915 Name The Year: Johnny Carson, pundit (Tonight Show), born. = 1925 Name The Year: Edison Pele, soccer player extraordinaire, born. = 1940 Name The Year: Walt Disney's "Dumbo" is released. = 1941 Name The Year: Michael Crichton, author (Andromeda Strain, Jurrasic Park, Rising Sun), born. = 1942 Name The Year: During WW II, Britain launches major offensive at El Alamein, Egypt. = 1942 Name The Year: United Nations General Assembly convenes in New York for first time in Flushing Meadow. = 1946 Name The Year: First video recording on magnetic tape televised coast-to-coast. = 1956 Name The Year: The ill-fated revolt in Communist Hungary starts -- The Prague Spring. It was later crushed by Soviet tanks. = 1956 Name The Year: Soviet novelist Boris Pasternak, wins Nobel Prize for Literature. = 1958 Name The Year: President Nixon agrees to turn over White House tape recordings. = 1973 Name The Year: By 2/3 majority, Panamanians vote to approve the new Canal treaties. = 1977 Name The Year: Soviet Premier Alexei Kosygin resigns, due to illness. = 1980 Name The Year: 241 US Marines and sailors die in a terrorist suicide attack on their barracks in Beriut. = 1983 Name The Year: 33 years after Soviet tanks crushed the Prague Spring, Hungary declares itself a republic. = 1989 Name The Year: Jane Seymour, third wife of Henry VIII, dies. = 1537 Name The Year: Third partition of Poland, between Austria, Prussia and Russia. = 1795 Name The Year: The match is patented. = 1836 Name The Year: William Lassell discovers Ariel and Umbriel, satellites of Unranus. = 1851 Name The Year: First transcontinental telegram sent. = 1861 Name The Year: Anna Taylor, first to go over Niagara Falls in a barrel and live. = 1901 Name The Year: Moss Hart, playwright, born. = 1904 Name The Year: "Black Thursday", the beginning of the stock market crash. = 1929 Name The Year: F. Murray Abraham, actor (Amadeus, Name of the Rose), born. = 1940 Name The Year: United Nations Charter goes into effect. = 1945 Name The Year: Kevin Kline, actor, born. = 1947 Name The Year: Disaster on USSR launch pad killed missle expert Nedelin and team (unconfirmed); USSR claims he was killed in plane crash. = 1960 Name The Year: Zambia (N. Rhodesia) gains independence from Britain (National Day). = 1964 Name The Year: Salvador Allende Gossens elected president of Chile. = 1970 Name The Year: Yom Kippur War ends - Israel 65 miles from Cairo, 26 from Damascus. = 1973 Name The Year: The first Jewish film and TV festival. = 1976 Name The Year: Author Geoffrey Chaucer dies in London. = 1400 Name The Year: Battle of Agincourt, Welsh longbow defeats the armored knight. = 1415 Name The Year: Giovanni Cassini discovers Iapetus, satellite of Saturn. = 1671 Name The Year: George III ascends the British throne. = 1760 Name The Year: Johann Strauss the Younger, composer, Waltz King = 1825 Name The Year: Georges Bizet, composer = 1838 Name The Year: Henry Norris Russell, astronomer (Hertzsprung-Russell diagram) = 1877 Name The Year: Pablo Picasso, doodler (Guernica) (or 10-05) = 1881 Name The Year: Eduardo Barrios, Chilean novelist (The Love-Crazed Boy) = 1884 Name The Year: Richard E. Byrd, polar explorer = 1888 Name The Year: Marion Ross (in Minnesota), actor (Played Mrs. Cunningham in Happy Days) = 1928 Name The Year: Russell L. (Rusty) Schweickart, astronaut (Apollo 9) = 1935 Name The Year: Nylon stockings go on sale in the U.S. for the first time. = 1939 Name The Year: Helen Reddy (in Melbourne, Australia), singer (I am Woman) = 1941 Name The Year: First electronic wrist watch placed on sale, NY city. = 1960 Name The Year: UN General Assembly admits Mainland China and expels Taiwan. = 1971 Name The Year: USSR Venera 10 made day Venus landing. = 1975 Name The Year: US invades Grenada, a country with 1/2000 its population. = 1983 Name The Year: King Alfred the Great, dies. = 901 Name The Year: Worldwide Red Cross organized in Geneva. = 1863 Name The Year: Football Association forms in England, standardizing soccer. = 1863 Name The Year: Leon Trotsky, Russian revolutionary (pres of 1st Soviet), born. = 1879 Name The Year: Prince Ito of Japan is assassinated by a Korean. = 1909 Name The Year: Felix the Cat, cartoon character, born. = 1917 Name The Year: Mohammad Reza Pahlavi Aryamehr, Shah of Iran (1941-79), born. = 1919 Name The Year: Bob Hoskins, Suffolk, England, actor (Brazil, Who Framed Roger Rabbit?), born. = 1942 Name The Year: Jaclyn Smith, actress (Charlie's Angel, Nightkill), born. = 1947 Name The Year: Ngo Dinh Diem proclaims Vietnam a republic with himself as president. = 1955 Name The Year: USSR fires defense minister, Marshal Georgi Zhukov (Zhukov lead USSR into Berlin during WW2). = 1957 Name The Year: Cary Elwes, actor (Glory, Princess Bride, Robin Hood - Men In Tights), born. = 1962 Name The Year: Shah of Iran crowns himself after 26 years on Peacock Throne. = 1967 Name The Year: Guided tours of Alcatraz (by Park Service) begin. = 1972 Name The Year: Anwar Sadat became 1st Egyptian president to officially visit the US. = 1975 Name The Year: Transkei gains independence, not recognized outside of South Africa. = 1976 Name The Year: Trinidad and Tobago becomes a republic. = 1976 Name The Year: Park Chung-hee South Korean President is assassinated. = 1979 Name The Year: Head of El Salvadoran Human Rights Comm assassinated by death squads. = 1987 Name The Year: US-Soviet effort free 2 grey whales from frozen Arctic. = 1988 Name The Year: James Cook, captain/explorer, discovered Sandwich Islands and led First Fleet to Australia, born. = 1728 Name The Year: Niccolo Paganini, Genoa, Italy, composer/violin virtuoso (Princess Lucca), born. = 1782 Name The Year: Theodore Roosevelt 26th President of US (1901-09) (Nobel 1906), born. = 1858 Name The Year: Emily Post authority on social behavior, writer (Etiquette), born. = 1872 Name The Year: Dylan Thomas, Swansea, Wales, poet (Child's Christmas in Wales), born. = 1914 Name The Year: Roy Lichtenstein, Pop art painter; painted comic book panels, born. = 1923 Name The Year: The Uzbek SSR forms. = 1924 Name The Year: DuPont announces its new synthetic fiber will be called "nylon". = 1938 Name The Year: John Cleese comedian/actor (Monty Python, Fawlty Towers), born. = 1939 Name The Year: "You Bet Your Life", with Groucho Marx, premieres on American ABC radio. = 1947 Name The Year: Simon Le Bon, rocker (Duran Duran), born. = 1958 Name The Year: Outer Mongolia and Mauritania become the 102nd and 103rd members of UN. = 1961 Name The Year: St Vincent and the Grenadines gains associated status with Britain. = 1969 Name The Year: Republic of the Congo becomes Republic of Zaire. = 1971 Name The Year: St Vincent and the Grenadines becomes independent of UK (Nat'l Day). = 1979 Name The Year: Voluntary Euthanasia Society publishes how-to-do-it suicide guide. = 1979 Name The Year: China announces its population at 1 billion people plus. = 1982 Name The Year: South Korean voters overwhelmingly approved a new constitution. = 1987 Name The Year: Larry Flynt paid hitman $1M to kill Hefner, Guccione and Sinatra. = 1988 Name The Year: Henry III, Holy Roman emperor (1046-56) = 1017 Name The Year: Columbus arrives in Cuba. = 1492 Name The Year: Cornelius Otto Jansen of France, Roman Catholic reform leader = 1585 Name The Year: Harvard College, first US college founded. = 1636 Name The Year: Eli Whitney applies for patent for the cotton gin. = 1793 Name The Year: Cornelius Otto Jansen of France, Roman Catholic reform leader = 1585 Name The Year: St. Louis Police try a new investigation method - fingerprints. = 1904 Name The Year: Dr. Jonas Salk, who made polio a fear of the past = 1914 Name The Year: Czechoslovakia declares independence from Austria. = 1918 Name The Year: Walther Bauersfeld, invented first modern projection planetarium = 1919 Name The Year: Volstead Act passed by U.S. Congress, starting Prohibition. = 1919 Name The Year: Benito Mussolini takes control of Italy's government. = 1922 Name The Year: First child born in aircraft, Miami, Fl. = 1929 Name The Year: Gennadi M. Strekalov, Soviet cosmonaut (Soyuz T-3, T-8, T-11) = 1940 Name The Year: German rocket engineers begin work in USSR. = 1946 Name The Year: The flag of Israel is adopted. = 1948 Name The Year: Pope Paul VI proclaims Jews not collectively guilty for crucifixion. = 1965 Name The Year: US/USSR signed an agreement to discuss joint space efforts. = 1970 Name The Year: England becomes 6th nation to have a satellite (Prospero) in orbit. = 1971 Name The Year: BC Babylon falls to Cyrus the Great of Persia. = 539 Name The Year: Sir Walter Raleigh is executed in London. = 1618 Name The Year: Edmund Halley (in London), sky watcher, namesake of Halley's Comet, born. = 1656 Name The Year: Pennsylvania granted to William Penn by King Charles II. = 1682 Name The Year: Severe earthquake in New England. = 1727 Name The Year: James Boswell (in Scotland), Samuel Johnson's biographer, born. = 1740 Name The Year: International Committee of the Red Cross is founded (Nobel 1917, 1944, 1963). = 1863 Name The Year: Guillermo Valencia, Colombian poet, translator, statesman, born. = 1873 Name The Year: Marie, queen consort of Ferdinand I of Rumania, born. = 1875 Name The Year: Bela Lugosi, horror actor (Dracula, Body Snatcher), born. = 1884 Name The Year: Paul Joseph Goebbels, Nazi propagandist, born. = 1897 Name The Year: Turkey is proclaimed to have a republican government. = 1923 Name The Year: "Black Tuesday", the Stock Market crash. = 1929 Name The Year: Golden Gate International Exposition closes (first closure). = 1939 Name The Year: Israeli paratroopers drop into the Sinai to open Straits of Tiran. = 1956 Name The Year: Richard Dreyfuss, actor (Jaws, Close Encounters of the Third Kind), born. = 1947 Name The Year: Kate Jackson (in Alabama), actor (Charley's Angels, Making Love), born. = 1948 Name The Year: Star of India and other jewels are stolen in NY. = 1964 Name The Year: 8th and last crusade is launched. = 1270 Name The Year: Richard Brinsley Sheridan, playwright (Rivals, School for Scandal) = 1632 Name The Year: US Navy is created. = 1775 Name The Year: Francisco Madero, Mexican revolutionary, president (1911-13) = 1873 Name The Year: Ezra Pound (in Hailey, Idaho) = 1885 Name The Year: Ruth Gordon (in Mass.), actor (Rosemary's Baby, Harold and Maude) = 1896 Name The Year: Tsar Nicholas II grants Russia a constitution. = 1905 Name The Year: Fyodor Dostoevsky, author = 1918 Name The Year: Mussolini forms cabinet in Italy. = 1922 Name The Year: Orson Welles panics a nation with his broadcast of H. G. Wells' "War of the Worlds". = 1938 Name The Year: Grace Slick (in Chicago, IL), singer (Jefferson Airplane/Starship) = 1939 Name The Year: Talia Shire, actor (Rocky (as Adrian)) = 1943 Name The Year: Henry Winkler, actor (Happy Days as the Fonz) = 1945 Name The Year: Dr. Albert Schweitzer received Nobel Peace Prize for 1952. = 1953 Name The Year: Soviet Party Congress unanimously approves a resolution removing Josef Stalin's body from Lenin's tomb in Red Square. = 1961 Name The Year: USSR Kosmos 186 and 188 make first automatic docking. Also, Venera 13 is launched. = 1967 Name The Year: Muhammad Ali KOs George Foreman in 8th round in Kinshasa, Zaire. = 1974 Name The Year: 22nd Space Shuttle Mission, Challenger 9, is launched carrying eight crewmen (2 Germans, 1 Dutch). = 1985 Name The Year: Martin Luther posts his 95 Theses, begins Protestant Reformation. = 1517 Name The Year: Jan Vermeer (in Holland), painter (Procuress, The Astronomer) = 1632 Name The Year: John Keats, Romantic poet = 1795 Name The Year: Sir Humphrey Davy of London patents the miner's safety lamp. = 1815 Name The Year: William Parson, third Earl of Rosse and maker of large telescopes dies. = 1865 Name The Year: Benito Mussolini (Il Duce) becomes premier of Italy. = 1922 Name The Year: Erich Weiss, better known as Magician Harry Houdini, dies. = 1926 Name The Year: Michael Collins, astronaut (Gemini 10, Apollo 11) = 1930 Name The Year: Michael Landon, actor = 1936 Name The Year: Jane Pauley (in Indianapolis, IN), newcaster for CNN = 1950 Name The Year: John Candy, actor (Second City TV, Splash, Blues Brothers) = 1950 Name The Year: First thermonuclear bomb detonated - Marshall Islands. = 1952 Name The Year: Lee Harvey Oswald announces in Moscow he will never return to U.S. = 1959 Name The Year: U.S. President Johnson orders a halt to all bombing of North Vietnam. = 1968 Name The Year: Adrian IV only English pope (1154-59), dies (birth date unknown). = 1159 Name The Year: Jacques Cartier French explorer, dies (birth date unknown). = 1557 Name The Year: Great London Fire begins in Pudding Lane. 80% of London is destroyed. = 1666 Name The Year: Louis XIV the great, king of France (1643-1715), dies at 76. = 1715 Name The Year: RC Carrington and R Hodgson make 1st observation of solar flare. = 1859 Name The Year: Napoleon III captured at Sedan. = 1870 Name The Year: Sydney General Post Office opens. = 1874 Name The Year: Edgar Rice Burroughs, novelist (Tarzan, John Carter Of Mars Saga), born. = 1875 Name The Year: St Petersburg, Russia changes name to Petrograd. = 1914 Name The Year: Yvonne De Carlo, Vancouver BC, actress (Lily Munster in the Munsters), born. = 1922 Name The Year: Rocky Marciano, heavyweight champion boxer (1952-56), born. = 1923 Name The Year: Albania becomes a kingdom, with Zogu I as king. = 1928 Name The Year: Physical Review publishes 1st paper to deal with black holes. = 1939 Name The Year: WW II starts, Germany invades Poland, takes Danzig. = 1939 Name The Year: Japan surrenders ending WW II (US date, 9/2 in Japan). = 1945 Name The Year: UN's World Health Organization forms. = 1948 Name The Year: US, Australia and New Zealand sign ANZUS treaty. = 1951 Name The Year: Gloria Estefan, Cuba, singer, born. = 1957 Name The Year: 10,000 die in an earthquake in western Iran. = 1962 Name The Year: UN announces Earth population has hit 3 billion. = 1962 Name The Year: Libyan revolution, Col Moammar Gadhafi deposes King Idris. = 1969 Name The Year: Qatar declares independence from Britain. = 1971 Name The Year: Pioneer 11 makes 1st fly-by of Saturn, discovers new moon, rings. = 1979 Name The Year: Palestinian Liberation Organization leaves Lebanon. = 1982 Name The Year: Korean Boeing 747 strays into Siberia and is shot down by a Soviet jet. = 1983 Name The Year: BC Phidippides runs first marathon, to announce the victory of Sparta over Persia. = 490 Name The Year: C Battle of Actium; Octavian defeats Mark Antony and becomes Emperor Augustus. = 31 B Name The Year: The Mayflower sets sail from Plymouth with 102 Pilgrims. = 1620 Name The Year: Great Fire of London starts; destroys St. Paul's Church. = 1666 Name The Year: Last day of Julian calendar in Britain, British colonies. = 1752 Name The Year: K. L. Harding discovers Juno, third known asteroid. = 1804 Name The Year: Queen Liliuokalani, last queen of Hawaii (1891-93) = 1838 Name The Year: Wilhelm Ostwald, German physical chemist (Nobel 1909) = 1853 Name The Year: Lord Kitchener retakes Sudan for Britain. = 1898 Name The Year: First transatlantic round-trip air flight. = 1936 Name The Year: Vietnam declares independence from France (National Day). = 1945 Name The Year: V-J Day; formal surrender of Japan aboard USS Missouri. = 1945 Name The Year: Jimmy Connors, tennis player = 1952 Name The Year: St Gregory I begins his reign as Catholic Pope. = 590 Name The Year: England's King Richard I (the Lion-Hearted) crowned in Westminster. = 1189 Name The Year: Nicolo Amati, Italy, violin maker (Stradivari and Guarneri), born. = 1596 Name The Year: James I king of England (1603-25), dies at 92. = 1658 Name The Year: Lord Oliver Cromwell British king (1653-58), dies at 59. = 1658 Name The Year: Charles X, Versailles, France, Duke of Prussia, born. = 1757 Name The Year: British annex Natal (South Africa). = 1900 Name The Year: Cardinal Giacome della Chiesa becomes Pope Benedict XV. = 1914 Name The Year: Allies turned back Germans in WW I's Battle of Verdun. = 1916 Name The Year: Britain declares war on Germany. France follows 6 hours later quickly joined by Australia, New Zealand, South Africa and Canada. = 1939 Name The Year: Pauline Collins, London England, actress (Shirley Valentine), born. = 1940 Name The Year: US gives Britain 50 destroyers in exchange for Newfoundland base lease. = 1940 Name The Year: Allies invade Italy. = 1943 Name The Year: Japanese forces in the Philippines surrender to Allies. = 1945 Name The Year: Pope Pius X canonized a saint. = 1954 Name The Year: e. e. cummings poet, dies at 67. = 1962 Name The Year: Charlie Sheen, actor (Wall St, Platoon), born. = 1965 Name The Year: Ho Chi Minh North Vietnamese president, dies. = 1969 Name The Year: Qatar regains complete independence from Britain. = 1971 Name The Year: Viking 2 soft lands on Mars (Utopia), returns photos. = 1976 Name The Year: Crew of Soyuz 31 returns to Earth aboard Soyuz 29. = 1978 Name The Year: Frank Capra director (It's a Wonderful Life), dies at 94. = 1991 Name The Year: Romulus Augustulus, last Roman emperor in west, is deposed. = 476 Name The Year: Navigator Henry Hudson discovered the island of Manhattan. = 1609 Name The Year: Francois Ren, de Chateaubriand, French poet, novelist, statesman = 1768 Name The Year: Los Angeles founded in the Valley of Smokes (Indian Name). = 1781 Name The Year: Marcus Whitman, missionary = 1802 Name The Year: Anton Bruckner (in Austria), Wagner disciple = 1824 Name The Year: The first newsboy in the US hired (Barney Flaherty), by the NY Sun. = 1833 Name The Year: Daniel Burnham, American architect, built skyscrapers = 1846 Name The Year: French republic proclaimed. = 1870 Name The Year: Darius Milhaud, composer = 1872 Name The Year: Geronimo, Apache indian, finally surrenders ending last major US-Indian war. = 1886 Name The Year: George Eastman patents first rollfilm camera and registers Kodak. = 1888 Name The Year: Paul Osborn, playwright = 1901 Name The Year: Richard Wright, American author (Native Son, Uncle Tom's Children) = 1908 Name The Year: Garros sets world altitude record of 4,250 m (13,944 ft). = 1911 Name The Year: Henry Ford II, businessman = 1917 Name The Year: Last day of Julian civil calendar (in parts of Bulgaria). = 1920 Name The Year: Tom Watson, Golfer = 1949 Name The Year: Ford Motor Co. introduced the Edsel! (Oh boy !) = 1957 Name The Year: NASA launches its first Orbital Geophysical Observatory (OGO-1) = 1964 Name The Year: George Harrison releases "My Sweet Lord" single. = 1970 Name The Year: American swimmer Mark Spitz becomes the first athlete to win seven olympic gold medals. = 1972 Name The Year: Iraqi troops seized Iranian territory in a border dispute. = 1980 Name The Year: Louis VIII [Coeur-de-Lion] king of France (1223-26), born. = 1187 Name The Year: Louis XIV the great, king of France (1643-1715), born. = 1638 Name The Year: Jesse James Missouri, outlaw, born. = 1847 Name The Year: Sam Houston elected president of the Republic of Texas. = 1836 Name The Year: France proclaims a protectorate over Chad. = 1900 Name The Year: Raquel Welch Chic (Myra Breckenridge, 1,000,000 BC, 100 Rifles), born. = 1940 Name The Year: Freddie Mercury, singer, born. = 1946 Name The Year: 1st color video recording on magnetic tape presented, Charlotte North Carolina, USA. = 1958 Name The Year: Cassius Clay captures the olympic light heavyweight gold medal. = 1960 Name The Year: 11 Israeli athletes are slain at Munich Olympics. = 1972 Name The Year: Iraqi Pres Saddam Hussein urges Arabs to rise against the West. = 1990 Name The Year: US trial of former Panamanian leader Manuel Noriega begins. = 1991 Name The Year: Pilgrims set sail from Plymouth England to the New World. = 1620 Name The Year: James II king of England (1685-88), dies at 68. = 1701 Name The Year: Pres William McKinley assassinated by Leon Czologosz in Buffalo, New York. = 1901 Name The Year: Roger Waters, singer (Pink Floyd), born. = 1947 Name The Year: Juliana becomes queen of the Netherlands. = 1948 Name The Year: Michael Winslow, actor/comedian (Police Academy), born. = 1960 Name The Year: "Star Trek" premiers on NBC TV. = 1966 Name The Year: Dr Verwoerd South African PM, assassinated in assembly. = 1966 Name The Year: Swaziland gains independence from Britain (National Day). = 1968 Name The Year: USSR recognizes independence of the 3 Baltic republics (Estonia, Lithuania and Latvia). = 1991 Name The Year: Queen Elizabeth I England, (1558-1603) daughter of Henry VIII, born. = 1533 Name The Year: Treaty of Baden-French retain Alsace, Austria gets right bank of Rhine. = 1714 Name The Year: Francois-Andre Philidor, France, chess champion/musician, born. = 1726 Name The Year: Brazil declares independence from Portugal (National Day). = 1822 Name The Year: Geo Ligowsky patents device to throw clay pigeons for trapshooters. = 1880 Name The Year: Anthony Quayle England, actor (Anne of 1000 Days, Lawrence of Arabia), born. = 1913 Name The Year: James Van Allen, discovered Van Allen radiation belts, born. = 1914 Name The Year: Buddy Holly singer (Peggy Sue, That'll Be the Day), born. = 1936 Name The Year: Gloria Gaynor Newark NJ, disco singer (I Will Survive), born. = 1949 Name The Year: Chrissie Hynde, rocker (Pretenders), born. = 1950 Name The Year: Corbin Bernsen North Hollywood Calif, actor (Arnie Becker-LA Law), born. = 1954 Name The Year: Desmond Tutu installed to lead south African Anglican Church. = 1986 Name The Year: Monica Seles wins the US Open. = 1991 Name The Year: Clement III 1st antipope (1084-1100), dies (birth date unknown). = 1100 Name The Year: Richard I [Richard the Lion Hearted], King of England (1189-99), born. = 1157 Name The Year: Spanish navigator Juan de Elcano returns to Spain, completes 1st circumnavigation of globe, expedition begins under Ferdinand Magellan. = 1522 Name The Year: City of Lichfield, England established. = 1553 Name The Year: 1st permanent settlement in US forms (St Augustine, Florida). = 1565 Name The Year: Turkish siege of Malta broken by Maltese and Knights of St John. = 1565 Name The Year: Antonin Dvorak, Nelahozeves, Czechoslovakia, composer (New World Symphony), born. = 1841 Name The Year: Peter Sellers England, actor (Pink Panther, Being There, Goon Show), born. = 1925 Name The Year: 1st Miss America crowned (Margaret Gorman of Washington DC). = 1921 Name The Year: Alexandra Kollontai of Russia becomes 1st woman ambassador. = 1924 Name The Year: 1st appearance of the comic strip "Blondie". = 1930 Name The Year: Patsy Cline Va, country singer (Walkin' After Midnight), born. = 1932 Name The Year: Franklin D. Roooseveldt declares "limited national emergency" due to war in Europe. = 1939 Name The Year: Italy surrenders to the allies in WW II. = 1943 Name The Year: Ernest Hemmingway's "Old Man and the Sea" published. = 1952 Name The Year: NASA's Marshall Space Flight Center in Huntsville, Ala., dedicated by President Dwight D. Eisenhower. = 1960 Name The Year: "Star Trek" premiers on NBC-TV. = 1966 Name The Year: Uganda abolishes traditional tribal kingdoms, becomes a republic. = 1967 Name The Year: 1st all Australian women's US Open final, (Margaret Court beats Yvonne Goolagong). = 1973 Name The Year: Ellis Island Historical Site opens on Eliis Island, NYC. = 1990 Name The Year: St Sergius I ends his reign as Catholic Pope. = 701 Name The Year: William I The Conqueror, King of England and Duke of Normandy, dies. = 1087 Name The Year: Battle of Flodden Fields; English defeat James IV of Scotland. = 1513 Name The Year: Cardinal A Jean de Plessicide de Richelieu, King Louis XIII of France's chief minister, born. = 1585 Name The Year: William Bligh nasty ship's captain and New South Wales governor (HMS Bounty), born. = 1754 Name The Year: Continental Congress renames "United Colonies", "United States". = 1776 Name The Year: Leo Tolstoy Russia, novelist (War and Peace, Anna Karenina), born. = 1828 Name The Year: Frederick R Spofforth Australia, cricketer (Demon), born. = 1853 Name The Year: Luxembourg gains independence. = 1867 Name The Year: Harland Sanders, Kentucky Fried Chicken founder/colonel, born. = 1890 Name The Year: EE Barnard at Lick discovers Amalthea, 5th Jupiter moon. = 1892 Name The Year: James Hilton, hotel magnate (Hilton Hotels), born. = 1900 Name The Year: Otis Redding, Georgia, rocker (Sitting on the Dock of the Bay), born. = 1941 Name The Year: Roger Waters, (Pink Floyd-The Wall), born. = 1943 Name The Year: Allied forces liberate Luxembourg from Nazis. = 1944 Name The Year: Bulgaria liberated from Nazi control (National Day). = 1944 Name The Year: Japanese in S Korea, Taiwan, China, Indochina surrender to Allies. = 1945 Name The Year: People's Democratic Republic of Korea proclaimed. = 1948 Name The Year: Michael Keaton, actor (Pacific Heights, Batman, Multiplicity), born. = 1951 Name The Year: Dave Stewart, rocker (Eurythmics-Here Comes the Rain Again), born. = 1952 Name The Year: Elvis Presley appears on national TV for 1st time (Ed Sullivan). = 1956 Name The Year: Tibet is made an autonomous region of China. = 1965 Name The Year: 1st successful test flight of a Saturn V. = 1967 Name The Year: John Lennon releases the "Imagine" album. = 1971 Name The Year: Viking 2 launched toward orbit around Mars, soft landing. = 1975 Name The Year: Mao Tse-Tung, Chinese communist party chairman (1949-76), dies at 82. = 1976 Name The Year: 1st TRS-80 computer sold. = 1977 Name The Year: Radio Shack announces their color computer 2 (the Coco2). = 1983 Name The Year: Bush and Gorbachev meet in Helsinki and urge Iraq to leave Kuwait. = 1990 Name The Year: Julius III Counter-Reformation pope (1550-55), born. = 1487 Name The Year: Simon Bolivar named president of Peru. = 1823 Name The Year: Isaac Kauffman Funk US, publisher (Funk and Wagnalls), born. = 1839 Name The Year: Elias Howe patents the sewing machine. = 1846 Name The Year: Ranjitsinhji Vibhaji India, cricketer/politician, born. = 1872 Name The Year: Fay Wray Alberta Canada, actress-King Kong's main squeeze, born. = 1907 Name The Year: NYC welcomes home Gen John J Pershing and 25,000 WW I soldiers. = 1919 Name The Year: Arnold Palmer golfer (PGA Golfer of the Year 1960, 1962), born. = 1929 Name The Year: Yevgeny V Khrunov USSR, cosmonaut (Soyuz 5), born. = 1933 Name The Year: Jose Feliciano, singer/songwriter (Light my Fire), born. = 1945 Name The Year: Siobhan Fahey rocker (Bananarama), born. = 1957 Name The Year: Gibraltar votes 12,138 to 44 to remain British. = 1967 Name The Year: Guinea-Bissau gains independence from Portugal. = 1974 Name The Year: East Germans begin their flight to the west (via Hungary and Czech). = 1989 Name The Year: 19 year old Pete Sampras beats Andre Agassi to win the US Open. = 1990 Name The Year: Hard Rock Cafe opens in Las Vegas Nevada. = 1990 Name The Year: Samuel Kanyon Doe president of Liberia, assassinated. = 1990 Name The Year: Iran agrees to resume dimplomatic ties with Iraq. = 1990 Name The Year: Yves Montand actor (Lets Make Love, Z), dies at 70. = 1991 Name The Year: Henry Hudson discovers Manhattan island. = 1609 Name The Year: English, Dutch and Austrians defeat French in Battle of Malplaquet. = 1709 Name The Year: 1st newspaper cartoon strip. = 1875 Name The Year: David Herbert "DH" Lawrence England, writer (Lady Chatterly's Lover), born. = 1885 Name The Year: Ferdinand Marcos Philippines Pres (1965-86), born. = 1917 Name The Year: Valentino, Milan Italy, fashion designer (Jacquline Kennedy Onasis), born. = 1932 Name The Year: Gherman Titov USSR, 1st man to spend a day in space (Vostok 2), born. = 1935 Name The Year: FDR orders any Axis ship found in American waters be shot on sight. = 1941 Name The Year: The 17th Olympic games close in Rome. = 1960 Name The Year: Virginia Madsen Chicago Ill, actress (Dune, Highlander, Class), born. = 1961 Name The Year: Bob Dylan's 1st NY performance. = 1961 Name The Year: Beatles' "Help!", album goes #1 and stays #1 for 9 weeks. = 1965 Name The Year: Nikita Khrushchev dies of a heart attack at 77. = 1971 Name The Year: Chile's President, Salvador Allende, deposed in a military coup. = 1973 Name The Year: Lorne Greene actor (Bonanza, Battlestar Galactica), dies at 72. = 1987 Name The Year: Peter Tosh reggae singer shot dead at 43 in Jamacia. = 1988 Name The Year: B.C. Athenians defeat second Persian invasion of Greece at Marathon. = 490 Name The Year: English explorer Henry Hudson enters the Hudson River = 1609 Name The Year: Drogheda, Ireland falls to Puritan troops; inhabitants massacred. = 1649 Name The Year: Charles Messier observes the Crab Nebula and begins catalog. = 1758 Name The Year: Richard Jordan Gatling, US inventor of hand-cranked machine gun, born. = 1818 Name The Year: Charles Dudley Warner, US newspaperman, author, born. = 1829 Name The Year: H.H. Asquith (Liberal), British prime minister (1908-16), born. = 1852 Name The Year: Henry L. Mencken, newspaperman, critic, Baltimore's son, born. = 1880 Name The Year: Maurice Chevalier, actor, singer (Gigi), ultimate Frenchman, born. = 1888 Name The Year: Margaret Hamilton, actor (Wizard of Oz), played Wicked Witch of the West, born. = 1902 Name The Year: Jesse Owens, athlete, spoiled Hitler's Olympic plans in 1936, born. = 1913 Name The Year: Amilcar Cabral, worked for independence of Portuguese Africa, born. = 1921 Name The Year: Stanislaw Lem, Polish science-fiction writer, born. = 1921 Name The Year: Katharine Hepburn makes her New York stage debut in "Night Hostess." = 1928 Name The Year: First German ship in WW2 captured by US ship (Busko). = 1941 Name The Year: Barry White, singer, born. = 1944 Name The Year: Khrushchev becomes First Secretary of the Communist Party. = 1953 Name The Year: "Bonanza" premieres -- in Color. = 1959 Name The Year: Luna 1 launched by USSR; first spacecraft to impact on the moon. = 1959 Name The Year: "The Monkees" premieres. = 1966 Name The Year: Gemini XI is launched. = 1966 Name The Year: USSR launches Luna 16; returns samples from lunar Sea of Fertility. = 1970 Name The Year: Coup overthrows Emperor Haile Selassie in Ethiopia (National Day). = 1974 Name The Year: Death of Dante Alighieri, author of the Divine Comedy. = 1321 Name The Year: Dante Alighieri Day. = 1583 Name The Year: Oliver Evans, pioneered the high-pressure steam engine, born. = 1755 Name The Year: Wolfe defeats Montcalm on Plains of Abraham; Canada becomes English. = 1759 Name The Year: New York City becomes the capitol of the United States. = 1788 Name The Year: Clara Wieck Schumann, German pianist, composer, born. = 1819 Name The Year: Walter Reed, who proved mosquitoes transmit yellow fever, born. = 1851 Name The Year: Milton S. Hershey, US chocolate manufacturer, philanthropist, born. = 1857 Name The Year: Arnold Schoenberg, composer, born. = 1874 Name The Year: Britain invades Egypt. = 1882 Name The Year: First airplane flight in Europe. = 1906 Name The Year: Barbara Bain (in Chicago, IL), actor (Mission Impossible, Space: 1999), born. = 1934 Name The Year: Judith Martin, etiquet authority, "Miss Manners", born. = 1938 Name The Year: Jaqueline Bisset (in England), actor (Deep), born. = 1941 Name The Year: Oscar Arias Sanchez, president of Costa Rica (1986- ) (Nobel 1987), born. = 1941 Name The Year: Soviet Lunik 2 becomes first human-made object to crash on moon. = 1959 Name The Year: "Car 54 Where are You?" premieres. = 1961 Name The Year: "The Outer Limits" premieres. = 1963 Name The Year: Beatles release "Yesterday." = 1965 Name The Year: IBM announces System 370 computer. = 1970 Name The Year: 9 hostages and 28 prisoners die in take over a Attica State Prison. = 1971 Name The Year: First TV viewer discretion warning - "Soap". = 1977 Name The Year: Second test of the Space Shuttle Enterprise. = 1977 Name The Year: According to South Africa, Venda gains independence. Not recognized as an independent country outside of South Africa. = 1979 Name The Year: Princess Grace of Monaco dies at 52 in a car crash. = 1982 Name The Year: England and colonies adopt Gregorian calendar, 11 days disappear, rioting ensues. = 1752 Name The Year: Napoleon occupies Moscow. = 1812 Name The Year: Ivan Pavlov, Russia, physiologist/pioneer in psychology, born. = 1849 Name The Year: Arthur Wellesley General/Duke of Wellington, dies at 83. = 1852 Name The Year: Lord Cecil of Chelwood, UK, helped form League of Nations (early version of the UN), born. = 1864 Name The Year: Hal Wallis movie producer (Maltese Falcon, Barefoot in the Park), born. = 1899 Name The Year: Henry Bliss becomes 1st automobile fatality. = 1899 Name The Year: Provisional government of Russia established, Republic proclaimed. = 1917 Name The Year: Graf Zeppelin II, world's largest airship, makes maiden flight. = 1938 Name The Year: Walter Koenig Chicago Ill, actor (Chekov-Star Trek), born. = 1938 Name The Year: Israel shoots down 13 Syrian MIG-21s. = 1973 Name The Year: Charles Kowal discovers Leda, 13th satellite of Jupiter. = 1974 Name The Year: Grace Kelly princess of Monaco, dies at 52 in a car crash. = 1982 Name The Year: Calagary Flames become 1st NHL team to play in USSR, win 4-2. = 1989 Name The Year: rajan, 13th Roman emperor (98-117), conqueror of Ctesiphon, born. = 53 T Name The Year: Francoise duc de la Rochefoucald Paris France, writer (Memoires), born. = 1613 Name The Year: Mayflower departs from Plymouth, England with 102 pilgrims. = 1620 Name The Year: Costa Rica, El Salvador, Guatemala, Honduras and Nicaragua gain independence. = 1821 Name The Year: Dame Agatha Christie mystery writer (Murder on the Orient Express), born. = 1890 Name The Year: Japan defeats China in Battle of Ping Yang. = 1894 Name The Year: Umberto II king of Italy (1946), born. = 1904 Name The Year: Fay Wray, actress (King Kong), born. = 1907 Name The Year: Battle of Aisne begins between Germans and French during WW I. = 1914 Name The Year: Margaret Lockwood actress (Lady Vanishes), born. = 1916 Name The Year: Russia proclaimed a republic by Alexander Kerensky. = 1917 Name The Year: Nuremberg Laws deprives German Jews of citizenship and makes the swastika the official symbol of Nazi Germany. = 1935 Name The Year: British PM Chamberlain visits Hitler at Berchtesgarden. = 1938 Name The Year: Tide turns in Battle of Britain in WW II, RAF beats Luftwaffe. = 1940 Name The Year: Oliver Stone NYC, director (Wall St, Good Morning Vietnam, Platoon), born. = 1946 Name The Year: Tommy Lee Jones actor (Volcano, Batman Forever, Under Siege), born. = 1946 Name The Year: UN turns over Eritrea to Ethiopia. = 1952 Name The Year: Soviet Premier Khrushchev arrives in US to begin a 13-day visit. = 1959 Name The Year: Dan Marino NFL quarterback (Miami Dolphins), born. = 1961 Name The Year: "Lost in Space" premiers. = 1965 Name The Year: Gemini XI returns to Earth. = 1966 Name The Year: Decca awards Bing Crosby a 2nd platinum disc for selling 300 million. = 1970 Name The Year: Muhammad Ali beats WBA heavyweight champion Leon Spinks. = 1978 Name The Year: Israeli forces began pouring into west Beirut. = 1982 Name The Year: Prince Henry Charles Albert David of Wales, 3rd in British sucession, born. = 1984 Name The Year: Lillehammer, Norway upsets Anchorage to host 1994 Winter olympics. = 1988 Name The Year: France announce it will send 4,000 troops to the Persian Gulf. = 1990 Name The Year: Henry V, king of England (1413-22), born. = 1387 Name The Year: Death of Tomas de Torquemada, inquisitor who burned 10,000 people. = 1498 Name The Year: The Mayflower departs from Plymouth, England with 102 pilgrims headed for the 'New World'. = 1620 Name The Year: Flamsteed sees solar eclipse, first known astronomical observation. = 1662 Name The Year: Mexico first declares independence from Spain (National Day). = 1810 Name The Year: Fire of Moscow. = 1812 Name The Year: A. Bonar Law (C), British prime minister (1922-23), born. = 1858 Name The Year: James J. Jeans, cosmologist and astrophysicist, born. = 1877 Name The Year: William Crapo Durant incorporates General Motors. = 1908 Name The Year: Wilfred Burchett, Australian Communist, journalist, writer, born. = 1911 Name The Year: Allen Funt, voyuer, 'Candid Camera' creator, born. = 1914 Name The Year: Lauren Bacall (in Staten Island, NY), actor, whistler (Dark Passage, Key Largo, Always), born. = 1924 Name The Year: B. B. King, singer, musician, born. = 1925 Name The Year: Charlie Byrd, guitarist, born. = 1925 Name The Year: Peter Falk, actor (Colombo, Princess Bride, In-Laws), born. = 1927 Name The Year: Barometric pressure at 856 mb (25.55") off Okinawa (record low). = 1945 Name The Year: James J. Jeans, astrophysicist, dies on his 69th birthday. = 1946 Name The Year: First automobile to exceed 400 mph, John Cobb, Bonneville Salt Flats. = 1947 Name The Year: Ed Begley, Jr., actor (St. Elsewhere, Real Genius, Spinal Tap), born. = 1949 Name The Year: Richard Nixon appears on "Laugh-in". = 1968 Name The Year: Papua New Guinea gains independence from Australia (National Day). = 1975 Name The Year: Episcopal Church approves ordination of women as priests and bishop. = 1976 Name The Year: Maria Callas American-born prima donna dies in Paris at 53. = 1977 Name The Year: "Miami Vice" premieres. = 1984 Name The Year: Charles III [The Simple], king of France (893-923), born. = 879 Name The Year: Wenceslas II king of Bohemia and Poland (1278-1305), born. = 1271 Name The Year: Joseph Caspar Mezzofanti, Cardinal/linguist (understood 70 languages), born. = 1774 Name The Year: William Herschel discovers Mimas, satellite of Saturn. = 1789 Name The Year: Konstantin Tsiolkovsky, pioneer in rocket and space research, born. = 1857 Name The Year: Thomas Selfridge becomes 1st fatality of powered flight. = 1908 Name The Year: Anne Bancroft AKA Mrs Mel Brooks, Bronx, actress (Graduate), born. = 1931 Name The Year: 1st 33 1/3 rpm recording released (Beethoven's 5th). = 1934 Name The Year: Soviet Union invades Poland during WW II. = 1939 Name The Year: 1st successful separation of Siamese twins. = 1953 Name The Year: Rita Rudner comedienne, born. = 1955 Name The Year: "The Fugitive" premiers on TV in the USA. = 1963 Name The Year: "Bewitched" premiers on TV in the USA. = 1964 Name The Year: Supremes release "Baby Love". = 1964 Name The Year: "Mission Impossible" premieres on TV in the USA. = 1967 Name The Year: "M = A = S = H" premiers on TV. = 1972 Name The Year: Soviet Union and Saudi Arabia restore diplomatic ties. = 1990 Name The Year: North and South Korea joins the UN. = 1991 Name The Year: Chile declares independence from Spain (National Day). = 1810 Name The Year: "The New York Times" goes on sale at 2 cents a copy. = 1851 Name The Year: Pacific Stock Exchange opens (as the Local Security Board). = 1882 Name The Year: D.D. Palmer of Davenport, Iowa, becomes the first Chiropractor. = 1895 Name The Year: Greta Garbo (in Stockholm, Sweden), actor (Ninotchka, Grand Hotel), born. = 1905 Name The Year: Jack Warden, actor (Verdict, Brian's Song), born. = 1920 Name The Year: Nikolai N. Rukavishnikov, Soviet cosmonaut (Soyuz 10, 16, 33), born. = 1932 Name The Year: Robert Blake, actor (Little Rascals, Baretta), born. = 1933 Name The Year: Frankie Avalon, singer (Four Seasons), born. = 1940 Name The Year: "Get Smart" premiers. = 1965 Name The Year: U.S. Voyager I takes the first space photograph of the earth and moon together. = 1977 Name The Year: Soyuz 38 carries 2 cosmonauts (1 Cuban) to Salyut 6 space station. = 1980 Name The Year: Joe Kittinger completes first solo balloon crossing of Atlantic. = 1984 Name The Year: English defeat French at Battle of Poitiers. = 1356 Name The Year: Jan Luyts, Dutch scholar, physicist, mathematician, astronomer, born. = 1655 Name The Year: Napoleon's retreat from Russia begins. = 1812 Name The Year: Bond (U.S.) and Lassell (England) independently discover Hyperion. = 1848 Name The Year: William Golding, English novelist (Lord of the Flies) (Nobel 1983), born. = 1911 Name The Year: Mickey Mouse makes his screen debut in "Steamboat Willie." = 1928 Name The Year: David McCallum, actor (Ilya Kuryakin in Man from U.N.C.L.E.), born. = 1933 Name The Year: Al Oerter, US discus thrower, born. = 1936 Name The Year: Paul Williams, singer, composer, actor, born. = 1940 Name The Year: Jeremy Irons, English actor (French Lieutenant's Woman), born. = 1948 Name The Year: Twiggy (Leslie Hornby) (in England), model, actor (Blues Brothers), born. = 1949 Name The Year: "Mary Tyler Moore Show" premiers. = 1970 Name The Year: St Christopher-Nevis gains independence from Britain (Nat'l Day). = 1983 Name The Year: Antoninus Pius, Roman Emperor = 0086 Name The Year: Alexander III the Great, king of Macedonia, emperor = 0357 Name The Year: Magellan starts first successful circumnavigation of the world. = 1519 Name The Year: Ernesto Teodoro Moneta, Italian journalist (Nobel Peace Prize 1907) = 1833 Name The Year: Slave trade abolished in DC, but slavery allowed to continue. = 1850 Name The Year: British and French defeat Russians at Alma, in the Crimea. = 1854 Name The Year: Patent granted on the electric range. = 1859 Name The Year: The Prince of Wales, later to become King Edward VII, becomes the first British royalty to visit the United States. = 1860 Name The Year: Civil War Battle of Chickamauga, near Chattanooga, Tenn, ended. = 1863 Name The Year: Ferdinand "Jelly Roll" Morton, jazz pianist, composer, singer = 1885 Name The Year: Sophia Loren (in Rome, Italy), actor (Desire Under the Elms, Black Orchid) = 1934 Name The Year: German rocket engineers begin work in U.S. = 1945 Name The Year: First North Pole jet crossing = 1951 Name The Year: First FORTRAN computer program run. = 1954 Name The Year: Luna 16 lands on Moon's Mare Fecunditatis, drills core sample. = 1970 Name The Year: Billy Jean King beats Bobby Riggs in battle-of-sexes tennis match. = 1973 Name The Year: Walt Disney World's 200-millonth guest. = 1985 Name The Year: Edward II king of England (1307-1327), dies at 43. = 1327 Name The Year: Frederick III Innsbruck Austria, German Emperor (1440-1493), born. = 1415 Name The Year: John Loudon McAdam created macadam road surface (asphalt), born. = 1756 Name The Year: 1st French Republic declared. = 1792 Name The Year: H(erbert) G(eorge) Wells Bromley, England (War of the Worlds), born. = 1866 Name The Year: Teiichi Igarashi Japan, climed Mt Fuji at age 99, born. = 1886 Name The Year: NY Sun runs famous "Yes, Virginia there is a Santa Claus," editorial. = 1897 Name The Year: Chuck Jones animator (Bugs Bunny, Daffy Duck), born. = 1912 Name The Year: Larry Hagman Fort Worth Tx, actor (I Dream of Jeannie, JR-Dallas), born. = 1931 Name The Year: Stones at Stonehenge, England, sold at auction for £6,600. = 1915 Name The Year: Leonard Cohen, Montreal, singer/songwriter (Death of Ladies Man), born. = 1934 Name The Year: J.R.R. Tolkein, publishes "The Hobbit". = 1937 Name The Year: Stephen King suspense writer (Shining, Kujo), born. = 1947 Name The Year: Federal Republic of [West] Germany created under 3-power occupation. = 1949 Name The Year: Peoples Rebulic of China proclaimed. = 1949 Name The Year: Bill Murray Evanston Ill, comedian (SNL, What About Bob, Stripes), born. = 1950 Name The Year: Nuclear submarine "Nautilus" is commissioned. = 1954 Name The Year: Haakon VII king of Norway, dies, Olaf succeeds him. = 1957 Name The Year: Malta gains independence from Britain. = 1964 Name The Year: Ricki Lake actress (Hairspray, Ricki Lake Show), born. = 1968 Name The Year: Luna 16 leaves the Moon. = 1970 Name The Year: Belize gains independence from Britain (National Day). = 1981 Name The Year: Armenia votes on whether to remain in the Soviet Union. = 1991 Name The Year: Bilbo Baggins (in Shire Reconning), born. = 1290 Name The Year: Lord Chesterfield letter writer; introduced Gregorian calendar (1752), born. = 1694 Name The Year: Michael Faraday discovered principle of electric motor, born. = 1791 Name The Year: Shigeru Yoshida Japanese PM (most of 1946-54), born. = 1878 Name The Year: Italo Marchiony granted patent for the ice cream cone. = 1903 Name The Year: Junko Tabei Japan, 1st woman to climb Mount Everest, born. = 1939 Name The Year: USSR detonates its 1st atomic bomb. = 1949 Name The Year: Commercial TV begins in England. = 1955 Name The Year: Mali (without Senegal) gains independence from France (National Day). = 1960 Name The Year: Iraqi troops seize part of Iran in a border dispute; war begins. = 1980 Name The Year: California University makes the Dead Sea Scrolls public. = 1991 Name The Year: BC Euripides, ancient Greek playwright (Trojan Women) (or 480 BC), born. = 484 Name The Year: C Octavian (Augustus Caesar), first Roman emperor, born. = 63 B Name The Year: Ferdinand VI, king of Spain (1746-59), born. = 1713 Name The Year: Johann Galle and Heinrich d'Arrest find the planet Neptune. = 1846 Name The Year: Urbain J.J. Leverrier, codiscoverer of Neptune, dies. = 1877 Name The Year: First Mack Sennett "Keystone Comedy" movie is released. = 1912 Name The Year: Mickey Rooney, actor (too many credits to mention), born. = 1920 Name The Year: Gene Tunney defeats Jack Dempsey for world heavyweight boxing title. = 1926 Name The Year: John Coltrane, saxophonist, born. = 1926 Name The Year: Ray Charles, entertainer extraordinaire, born. = 1930 Name The Year: Kingdom of Saudi Arabia formed (National Day). = 1932 Name The Year: Bruce "The Boss" Springsteen, rock musician (Born in the USA), born. = 1949 Name The Year: Jason Alexander (George in Seinfeld), born. = 1959 Name The Year: First movie to become a TV series -- "How to Marry a Millionaire". = 1961 Name The Year: ABC's first color TV series -- "The Jetsons". = 1962 Name The Year: Soyuz 22 returns to Earth. = 1976 Name The Year: Cheryl Ladd replaces Farrah Fawcett on "Charlie's Angels". = 1977 Name The Year: Third test of the Space Shuttle Enterprise. = 1977 Name The Year: First round-the-world trip by yacht (Cornelius Vanderbilt). = 1853 Name The Year: Black Friday -- Wall Street panics after Gould and Fisk attempt to corner gold. = 1869 Name The Year: Georges Claude, inventor of the neon light, born. = 1870 Name The Year: First round-the-world trip by a woman on a bicycle (took 15 months). = 1895 Name The Year: F. Scott Fitzgerald (in St. Paul, MN), writer (Great Gatsby), born. = 1896 Name The Year: John W. Young, astronaut (Gemini 3, 10, Apollo 10, 16, STS-1, 9), born. = 1930 Name The Year: John Brunner, British sci-fi author (Sheep Look Up), born. = 1934 Name The Year: Jim Henson, creater of the Muppets, "our era's Charlie Chaplin, Mae West, W. C. Fields, and Marx Brothers", born. = 1936 Name The Year: Linda Eastman McCartney, singer, born. = 1942 Name The Year: Pres Eisenhower suffered a heart attack on vacation in Denver. = 1955 Name The Year: Eisenhower orders US troops to desegregate Little Rock schools. = 1957 Name The Year: First atomic aircraft carrier, the "USS Enterprise", is launched. = 1960 Name The Year: "The Munsters" premier. = 1964 Name The Year: "60 Minutes" premiers. = 1968 Name The Year: First Automated return of lunar sample by Luna 16. = 1970 Name The Year: Guinea-Bissau gains independence from Portugal. = 1974 Name The Year: Vasco Nuez de Balboa is the first European to see the Pacific Ocean. = 1513 Name The Year: First printing press in America. = 1639 Name The Year: Olaus Roemer, first to accurately measured speed of light, born. = 1644 Name The Year: Jean Phillippe Rameau, French composer (baptized), born. = 1683 Name The Year: Nicolas-Joseph Cugnot, designed and built first automobile, born. = 1725 Name The Year: Mark Rothko, painter, born. = 1903 Name The Year: Dmitri Shostakovich, Russian composer, born. = 1906 Name The Year: Henry Ford announces the five day work week. = 1926 Name The Year: Michael Douglas, actor, producer, born. = 1944 Name The Year: Christopher Reeve, actor (Superman, Somewhere in Time), born. = 1952 Name The Year: Mark Hamill, actor (Star Wars), born. = 1952 Name The Year: First transatlantic telephone cable goes into operation. = 1956 Name The Year: Heather Locklear, actor, born.. = 1961 Name The Year: Three-man crew of Skylab II make safe splashdown in Pacific after 59 days. = 1973 Name The Year: Pacific SW Airlines Boeing 727 and a Cessna private plane collide. = 1978 Name The Year: Parthenon destroyed in war between Turks and Venetians. = 1687 Name The Year: Moses Mendelssohn, philosopher, critic, Bible translator, born. = 1729 Name The Year: John Chapman, alias Johnny Appleseed, born. = 1774 Name The Year: Frontiersman Daniel Boone died in Missouri at the age of 85. = 1820 Name The Year: Kapiolani defies Pele (Hawaiian volcano goddess) and lives. = 1824 Name The Year: T.S. Eliot, Anglican, playwright (Waste Land) (Nobel 1948), born. = 1888 Name The Year: Paul VI, 262nd Roman Catholic pope (1963-78), born. = 1897 Name The Year: George Gershwin, (in Brooklyn, NY), composer (Rhapsody in Blue), born. = 1898 Name The Year: Meuse-Argonne offensive against the Germans began during WWI. = 1918 Name The Year: Julio Iglesias, singer, born. = 1941 Name The Year: Olivia Newton-John (in Cambridge, England), singer, born. = 1948 Name The Year: Vladimir Remek, first Czechoslovakian space traveler (Soyuz 28), born. = 1948 Name The Year: UN troops in Korean War recaptured South Korean capital of Seoul. = 1950 Name The Year: Musical "West Side Story", opened on Broadway. = 1957 Name The Year: TV comedy "The Beverly Hillbillies" premiered on CBS. = 1962 Name The Year: Yemen Arab Republic proclaimed (National Day). = 1962 Name The Year: Japan launches its first satellite in to space. = 1966 Name The Year: Beatles release "Abbey Road" album. = 1969 Name The Year: Soyuz 38 returns to Earth. = 1980 Name The Year: Australia II won the America's Cup yacht race. = 1983 Name The Year: Cosmonauts Titov and Strekalov are saved from exploding Soyuz T-10. = 1983 Name The Year: Society of Jesus (Jesuits) founded by Ignatius Loyola. = 1540 Name The Year: Louis XIII, king of France (1610-43), born. = 1601 Name The Year: Sophia, regent of Russia (1682-89), born. = 1657 Name The Year: Agustin I de Iturbide, emperor of Mexico (1822-23), born. = 1783 Name The Year: Railroad transportation is born with first track in England. = 1825 Name The Year: Sir Martin Ryle, British radio astronomer, astronomer royal 1972-82, born. = 1918 Name The Year: Arthur Penn, stage and film director, born. = 1922 Name The Year: The ocean liner Queen Elizabeth was launched at Glasgow. = 1938 Name The Year: Actors Hume Cronyn and Jessica Tandy are married. = 1942 Name The Year: Dumitru Prunariu, first Rumanian space traveler (on board Soyuz 40), born. = 1952 Name The Year: Cheryl Tiegs (in Minnesota), model and actor, born. = 1957 Name The Year: Shaun Cassidy, singer, born. = 1958 Name The Year: US sells Hawk anti-aircraft missiles to Israel. = 1962 Name The Year: Warren Commission finds that Lee Harvey Oswald acted alone. = 1964 Name The Year: Soyuz 12 is launched. = 1973 Name The Year: B.C. Pompey (Gnaeus Pompeius Magnus) Rome, warrior, born. = 106 Name The Year: B.C. Confucius (as celebrated in Taiwan), born. = 551 Name The Year: William the Conqueror lands in England. = 1066 Name The Year: Caravaggio Italy, painter (or 1573), born. = 1573 Name The Year: Edward Thompson US archeologist who explored Mayan ruins, born. = 1856 Name The Year: Donati's comet becomes the 1st to be photographed. = 1858 Name The Year: Battle of Alcolea, causes Queen Isabella II of Spain to flee to France. = 1868 Name The Year: The city of Sydney inaugurates steam motor tram route. = 1879 Name The Year: Marcello Mastroianni actor (La Dolce Vita), born. = 1924 Name The Year: Seymour Cray inventor (Cray I computer), born. = 1925 Name The Year: Soviet-German treaty agree on 4th partition of Poland (WW II) and gives Lithuania to the USSR. = 1939 Name The Year: Phil Hartman comedian (SNL, Newsradio, Simpsons (Voice of Troy McLure)), born. = 1948 Name The Year: Albert Ascoli Italian developed anti-tuberculosis vaccine, dies. = 1957 Name The Year: Guinea votes for independence from France. = 1958 Name The Year: Explorer VI reveals an intense radation belt around the Earth. = 1959 Name The Year: Harpo Marx comedian (Marx Bros), dies at 75. = 1964 Name The Year: Beatles' "Hey Jude", single goes #1 and stays #1 for 9 weeks = 1968 Name The Year: Pope John Paul I dies after 33 days as pope. = 1978 Name The Year: Record 23,000 start in a marathon (Mexico City). = 1986 Name The Year: Ferdinand Marcos deposed president of Phillipines, dies. = 1989 Name The Year: Marvin Gaye gets a star on Hollywood's walk of fame. = 1990 Name The Year: Miles Davis jazz musician, dies at 65 from pneumonia. = 1991 Name The Year: Battle of Auray, English forces defeat French at Brittany. = 1364 Name The Year: Robert Lord Clive, founded British empire in India, born. = 1755 Name The Year: Horatio Nelson Burnham Thorpe, Britain, naval hero at Trafalgar, born. = 1758 Name The Year: Tennis is 1st mentioned in an English sporting magazine. = 1793 Name The Year: Scotland Yard formed in London. = 1829 Name The Year: Louis Pasteur dies. = 1895 Name The Year: Enrico Fermi, Italy, nuclear physicist, born. = 1901 Name The Year: Gene Autry, singer/actor/Calif Angels owner, born. = 1907 Name The Year: Bob Newhart Oak Park Ill, actor/comedian (Bob Newhart Show), born. = 1929 Name The Year: Richard Bonynge, Sydney Australia, conductor (Australian Orchestra Sydney-1976), born. = 1930 Name The Year: Anita Ekberg, Sweden, actress (La Dolce Vita, War and Peace), born. = 1931 Name The Year: Jerry Lee Lewis, singer (Great Balls of Fire, Breathless), born. = 1935 Name The Year: Lech Walesa, Popowo Poland, leads Polish Solidarity (Nobel 1983), born. = 1943 Name The Year: Eisenhower and Italian Marshal Pietro Badoglio sign an armistice. = 1943 Name The Year: Soviet troops invade Yugoslavia. = 1944 Name The Year: Sultan of Brunei promulgates a constitution. = 1959 Name The Year: Rolling Stones 1st tour (opening act for Bo Diddley and Everly Bros). = 1963 Name The Year: Bechuanaland gains independence from England, becomes Botswana. = 1966 Name The Year: Erika Eleniak, actress (Under Siege, Beverly Hillbillies), born. = 1969 Name The Year: Soviet space station Salyut 6 launched into Earth orbit. = 1977 Name The Year: Pope John Paul II becomes 1st pope to visit Ireland. = 1979 Name The Year: USSR releases US journalist Nicholas Daniloff confined on spy charges. = 1986 Name The Year: Charles Addams cartoonist (Addams Family), dies at 76 of heart attack. = 1988 Name The Year: 26th Space Shuttle mission, Discovery 7 launched. = 1988 Name The Year: First book published, Johann Guttenberg's Bible. = 1452 Name The Year: Robinson Crusoe, according to Daniel Defoe = 1627 Name The Year: Robinson Crusoe is shipwrecked (according to Defoe). = 1659 Name The Year: William Morris first uses ether in the first tooth extraction under anesthesia at Charlestown, Mass. = 1846 Name The Year: Jean Perrin, French physicist, studied Brownian motion (Nobel 1926) = 1870 Name The Year: Deborah Kerr (in Helensburg, Scotland), actor (King and I, Night of the Iguana) = 1921 Name The Year: Truman Capote, a short short story writer (In Cold Blood) = 1924 Name The Year: Elie Wiesel, author (Souls on Fire), Nazi hunter (Nobel 1986) = 1928 Name The Year: Angie Dickinson (in Kulm, ND), actor = 1931 Name The Year: Johnny Mathis, singer = 1935 Name The Year: Munich Agreement-forces Czechoslovakia to give territory to Germany. = 1938 Name The Year: First manned rocket flight (by auto maker Fritz von Opel). = 1939 Name The Year: Calais reoccupied by Allies. = 1944 Name The Year: James Dean, actor, died in a car crash (born Feb 08, 1931) = 1955 Name The Year: "The Flintstones" premieres. Yabba Dabba Doo! = 1960 Name The Year: Botswana gains independence from Britain (National Day). = 1966 Name The Year: USSR's Kosmos 186 and 188 complete the 1st automatic docking. = 1967pvpgn-1.99.7.2.1+dfsg/lua/quiz/questions/warcraft.txt000066400000000000000000000101541462640461400223560ustar00rootroot00000000000000************************************************** Category: Warcraft Author: Blizzard ************************************************** This Horde ship was crafted by goblins. Originally intended to bring Thrall and Aggra to the Maelstrom, the ship was destroyed in a surprise attack by the Alliance. = Draka's Fury What are undead murlocs called? = Murghouls What is the name of Tirion Fordring's gray stallion? = Mirador Which of these is the correct name for King Varian Wrynn's first wife? = Tiffin Ellerlan Wrynn Who was the first satyr to be created? = Xavius Before Ripsnarl became a worgen, he had a family. What was his wife's name? = Calissa Harrington This structure, located in Zangarmarsh was controlled by naga who sought to drain a precious and limited resource: the water of Outland. = Coilfang Reservoir One name for this loa is "Night's Friend" = Mueh'zala This defender of the Scarlet Crusade was killed while slaying the dreadlord Beltheris = Holia Sunshield Brown-skinned orcs first began showing up on Azeroth several years after the Third War, when the Dark Portal was reactivated. What are these orcs called? = Mag'har Succubus demons revel in causing anguish, and they serve the Legion by conducting nightmarish interrogations. What species is the succubus? = Sayaad While working as a tutor, Stalvan Mistmantle became obsessed with one of his students, a young woman named Tilloa. What was the name of her younger brother? = Giles Who was the mighty proto-dragon captured by Loken and tranformed into Razorscale? = Veranus This emissary of the Horde felt that Silvermoon City was a little too bright and clean. = Tatal This queen oversaw the evacuation of her people after the Cataclysm struck and the Forsaken attacked her nation. = Mia Greymane Not long ago, this frail Zandalari troll sought to tame a direhorn. Although he journeyed to the Isle of Giants, he was slain in his quest. What was his name? = Talak Arthas's death knights were trained in a floating citadel that was taken by force when many of them rebelled against the Lich King. What was the fortress's name? = Acherus White wolves were once the favored mounts of which orc clan? = Frostwolf Clan In Taur-ahe, the language of the tauren, what does lar'korwi mean? = Sharp claw Name the homeworld of the ethereals. = K'aresh Who was the first death knight to be created on Azeroth? = Teron Gorefiend In the assault on Icecrown, Horde forces dishonorably attacked Alliance soldiers who were busy fighting Scourge and trying to capture this gate. = Mord'rethar What evidence drove Prince Arthas to slaughter the people in Stratholme during the Third War? = Grain Who is the current leader of the gnomish people? = Gelbin Mekkatorque Malfurion Stormrage helped found this group which is the primary druidic organization of Azeroth = Cenarion Circle The draenei like to joke that in the language of the naaru, the word Exodar has this meaning = Defective elekk turd Thane Kurdran Wildhammer recently suffered a tragic loss when his valiant gryphon was killed in a fire. What was this gryphon's name? = Sky'ree Before she was raised from the dead by Arthas to serve the Scourge, Sindragosa was a part of what dragonflight? = Blue The Ironforge library features a replica of an unusually large ram's skeleton. What was the name of this legendary ram? = Toothgnasher Name the titan lore-keeper who was a member of the elite Pantheon = Norgannon What did the dragon aspects give the night elves after the War of the Ancients? = Nordrassil Formerly a healthy paladin, this draenei fell ill after fighting the Burning Legion and becoming one of the Broken. He later became a powerful shaman. = Nobundo Who were the three young twilight drakes guarding the twilight dragon eggs in the Obsidian Sanctum? = Shadron Tenebron Vesperon What phrase means "Thank you" in Draconic, the language of the dragons? = Belan Shi Before the original Horde formed, a highly contagious sickness began spreading rapidly among the orcs. What did the orcs call it? = Red pox What is the highest rank bestowed on a druid? = Archdruid Whose tomb includes the inscription "May the bloodied crown stay lost and forgotten"? = Terenas Menethil 2 pvpgn-1.99.7.2.1+dfsg/lua/quiz/quiz.lua000066400000000000000000000173031462640461400174500ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- quiz = {} -- object q_dictionary = {} -- dictionary word=question q_records_current = { } -- table with current player records q_records_total = {} -- table with total player records q_records_diff = {} -- table with current diffs -- counter of questions local _q_question_counter = 0 -- count of current question's hints local _q_hint_counter = 0 -- count of current question's hints local _q_current_index = 0 -- current hint local _q_hint = nil -- time when question was shown local _q_time_start = 0 -- last user streaks local _q_streak = { username = nil, count = 0 } -- delay flag for next_question timer local _q_next_question_skip_first = false -- is question unswered? local q_unswered = true function quiz:start(channelname, quizname) local filename = q_directory() .. "/questions/" .. string.lower(quizname) .. ".txt" if not file_exists(filename) then channel_send_message(config.quiz_channel, filename, message_type_error) return false end config.quiz_channel = channelname -- reset _q_question_counter = 0 _q_streak.username = nil _q_streak.count = 0 -- clear tables table.clear(q_dictionary) table.clear(q_records_current) table.clear(q_records_diff) -- table.clear(q_records_total) -- do not clear it, it should be cached -- load records (if it was not loaded yet) q_load_records() -- fill dictionary from file if file_load(filename, file_load_dictionary_callback, q_read_dictionary_callback) then channel_send_message(config.quiz_channel, string.format("Quiz \"%s\" started!", quizname), message_type_error) q_next_question() end end function q_read_dictionary_callback(a, b) table.insert(q_dictionary, { question = a, word = b }) end function quiz:stop(username) timer_del("q_hint") timer_del("q_question") local output = nil if username then output = "Quiz stopped by "..username else output = "Quiz finished!" end channel_send_message(config.quiz_channel, output, message_type_error) -- display current records if (q_records_current) and next(q_records_current) then table.sort(q_records_current, q_compare_desc) channel_send_message(config.quiz_channel, "Records of this game:", message_type_info) for i,t in pairs(q_records_current) do channel_send_message(config.quiz_channel, string.format(" %d. %s [%d points]", i, t.username, t.points), message_type_info) end end -- merge current with total records if (q_records_total) and next(q_records_total) then for i,t in pairs(q_records_current) do -- steal points from players in total table accordingly with half points of current table players if (config.quiz_competitive_mode) then if q_records_total[i] and not (q_records_total[i].username == t.username) then -- remove half points local points = math.floor(t.points / 2) q_records_diff[q_records_total[i].username] = points * -1 q_records_total[i].points = q_records_total[i].points - points -- avoid negative value if q_records_total[i].points <= 0 then q_records_total[i].points = 0 end end end -- add non exist users from current to total table -- and increase total users points local idx = q_records_total_find(t.username) -- set diff as winned points local tusername = q_records_total[idx].username q_records_diff[tusername] = t.points -- if user not found (new user with 0 points) if q_records_total[idx].points == 0 then q_records_total[idx].points = t.points else -- update diff if it was changed to negative if tonumber(q_records_diff[tusername]) < 0 then q_records_diff[tusername] = q_records_diff[tusername] + t.points end q_records_total[idx].points = q_records_total[idx].points + t.points end end -- sort by points table.sort(q_records_total, q_compare_desc) quiz_display_top_players() else -- if quiz_records.txt is empty then save first records q_records_total = q_records_current end q_save_records() config.quiz_channel = nil end -- handle channel message function quiz_handle_message(username, text) if q_unswered then return end local q = q_dictionary[_q_current_index] if string.upper(q.word) == string.upper(text) then -- time from question to unswer local time_diff = os.clock() - _q_time_start local points, total = 0,0 -- calc points points = 1+(#q.word - _q_hint_counter) - math.floor(time_diff / config.quiz_hint_delay) -- avoid negative value if (points <= 0) then points = 1 end -- remember previous streak local prev_streak = _q_streak -- increase streaks if (_q_streak.username == username) then _q_streak.count = _q_streak.count + 1 else _q_streak.username = username _q_streak.count = 0 end local bonus = "" -- add bonus points for streaks if (_q_streak.count > 0) then points = points + _q_streak.count bonus = string.format(" +%s streak bonus", _q_streak.count) end local idx = q_records_current_find(username) total = q_records_current[idx].points + points q_records_current[idx].points = total -- lose half points for previous user if config.quiz_competitive_mode and not (prev_streak.username == username) then local lose_points = math.floor(total / 2) local idx = q_records_current_find(prev_streak.username) q_records_current[idx].points = q_records_current[idx].points - lose_points -- avoid negative value if q_records_current[idx].points <= 0 then q_records_current[idx].points = 0 end end channel_send_message(config.quiz_channel, string.format("%s is correct! The unswer is: %s (+%d points%s, %d total) [%d sec]", username, q.word, (points-_q_streak.count), bonus, total, time_diff), message_type_info) q_next_question() end end -- go to next question function q_next_question() q_unswered = true _q_question_counter = _q_question_counter + 1 if (_q_question_counter > config.quiz_max_questions) then quiz:stop() return 0 end timer_del("q_hint") _q_next_question_skip_first = false timer_add("q_question", config.quiz_question_delay, q_tick_next_question) end -- write random question function q_tick_next_question(options) -- skip first tick if not _q_next_question_skip_first then _q_next_question_skip_first = true return 0 end _q_time_start = os.clock() _q_current_index = math.random(#q_dictionary) local q = q_dictionary[_q_current_index] -- send question channel_send_message(config.quiz_channel, "--------------------------------------------------------", message_type_info) channel_send_message(config.quiz_channel, string.format(" %s (%d letters)", q.question, #q.word), message_type_info) -- hide hint _q_hint = q_hide_unswer(q.word) -- stop this timer timer_del(options.id) _q_hint_counter = 0 -- start timer to hint unswer timer_add("q_hint", config.quiz_hint_delay, q_tick_hint_unswer) q_unswered = false end -- write hint for unswer function q_tick_hint_unswer(options) -- skip first tick if _q_hint_counter == 0 then _q_hint_counter = _q_hint_counter + 1 return 0 end _q_hint_counter = _q_hint_counter + 1 local q = q_dictionary[_q_current_index] -- update hint _q_hint = q_show_next_symbol(_q_hint, q.word) if not (_q_hint == q.word) then -- show hint channel_send_message(config.quiz_channel, "Hint: ".._q_hint, message_type_info) else -- reset counter _q_hint_counter = 0 -- show unswer q_nounswer(nil) end end -- nobody unswered function q_nounswer(username) channel_send_message(config.quiz_channel, 'Nobody unswered. The unswer was: '.._q_hint, message_type_info) -- decrease streaks if (_q_streak.count > 0) then _q_streak.count = _q_streak.count - 1 end q_next_question() end pvpgn-1.99.7.2.1+dfsg/lua/quiz/records.lua000066400000000000000000000053071462640461400201220ustar00rootroot00000000000000--[[ Copyright (C) 2014 HarpyWar (harpywar@gmail.com) This file is a part of the PvPGN Project http://pvpgn.pro Licensed under the same terms as Lua itself. ]]-- -- Load total records from a text file into the table function q_load_records() local filename = config.vardir() .. "quiz_records.txt" if not file_exists(filename) then DEBUG("Could not open file with Quiz records: " .. filename) return false end if not q_records_total or not next(q_records_total) then -- fill records table return file_load(filename, file_load_dictionary_callback, q_read_records_callback) end return true end function q_read_records_callback(a, b) table.insert(q_records_total, { username = a, points = b }) end -- Save total records from the table into a text file function q_save_records() local filename = config.vardir() .. "quiz_records.txt" file_save2(filename, q_save_records_callback) end function q_save_records_callback(file) if (q_records_total) and next(q_records_total) then for i,t in pairs(q_records_total) do file:write(t.username .. " = " .. t.points) file:write("\n") end end end -- Print Top X players records function quiz_display_top_players() local output = "Top " .. config.quiz_users_in_top .. " Quiz players:" channel_send_message(config.quiz_channel, output, message_type_info) -- display TOP of total records for i,t in pairs(q_records_total) do if (i > config.quiz_users_in_top) then break end local diff = "" if q_records_diff[t.username] then if q_records_diff[t.username] < 0 then diff = "(" .. q_records_diff[t.username] .. ")" -- minus points elseif q_records_diff[t.username] > 0 then diff = "(+" .. q_records_diff[t.username] .. ")" -- plus points end end local output = string.format(" %d. %s [%d points] %s", i, t.username, t.points, diff) channel_send_message(config.quiz_channel, output, message_type_info) end end -- find username and return table index -- preventnew - do not add user in table if it is not found function q_records_current_find(username, preventnew) -- find username for i,t in pairs(q_records_current) do if (t.username == username) then return i end end if preventnew then return nil end -- if not found then insert table.insert(q_records_current, {username = username, points = 0}) return #q_records_current end -- find username and return table index -- prevent_new = do not add user in table if it is not found function q_records_total_find(username, prevent_new) for i,t in pairs(q_records_total) do if (t.username == username) then return i end end if prevent_new then return nil end -- if not found then insert table.insert(q_records_total, {username = username, points = 0}) return #q_records_total end pvpgn-1.99.7.2.1+dfsg/man/000077500000000000000000000000001462640461400147535ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/man/CMakeLists.txt000066400000000000000000000003201462640461400175060ustar00rootroot00000000000000install(FILES bnbot.1 bnchat.1 bnetd.1 bnetd.conf.5 bnftp.1 bni2tga.1 bnibuild.1 bniextract.1 bnilist.1 bnpass.1 bnpcap.1 bnproxy.1 bnstat.1 bntext.5 bntrackd.1 tgainfo.1 DESTINATION ${MANDIR}) pvpgn-1.99.7.2.1+dfsg/man/bnbot.1000066400000000000000000000053101462640461400161400ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNBOT 1 "11 June, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnbot \- Battle.net text-based chat bot client .SH SYNOPSIS .B bnbot [\fIoptions\fP] \fIservername\fP [\fIserverport\fP] .SH DESCRIPTION .B bnbot is a simple program which will allow communication through the bot protocol of a Battle.net server from the command line. Input from stdin is sent to the server and output from the server is send to stdout. The interface is so simple that .BR telnet (1) could be used. The only complicating factor is that a control-C character must be sent to the server before any other input. .LP It is hoped that this program can be used with a scripting language like .BR expect (1) to make creation of chat bots for Unix easier. To date, I don't know of anyone doing this. This program may also be used to test bot login permissions for accounts. .LP If no \fIservername\fP is specified, localhost is assumed. If no \fIserverport\fP is specified, 6112 is assumed. .SH OPTIONS .TP .B \-h --help --usage Print usage information and exit. .TP .B \-v --version Print its version number and exit. .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS The terminal settings are not consulted for keybindings. The following are assumed: .TP ^H delete the character to the left of the cursor .TP ^J accept current line .TP ^M (return) accept current line .TP ^T transpose the last two characters .TP ^W delete word to the left of the cursor .TP ^U delete the whole input line .TP ^[ (escape) cancel the current input line .TP ^? (delete) delete the character to the left of the cursor .SH "SEE ALSO" .BR bnetd (1), .BR bnchat (1) .SH AUTHOR .TP Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnchat.1000066400000000000000000000102101462640461400162660ustar00rootroot00000000000000.\" .\" Copyright (C) 1998,1999,2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNCHAT 1 "09 April, 1999" "BNETD" "BNETD User's Manual" .SH NAME bnchat \- Battle.net text-based chat client .SH SYNOPSIS .B bnchat [\fIoptions\fP] [\fIservername\fP [\fIserverport\fP]] .SH DESCRIPTION .B bnchat is a simple Battle.net client emulator that can be used as a minimal chat client with .BR bnetd (1) in order to test certain aspects of the server configuration or to do server administration from the Unix command line. It is also compatible with FSGS servers, but has difficulty with real Battle.net servers. .LP If no \fIservername\fP is specified, localhost is assumed. If no \fIserverport\fP is specified, 6112 is assumed. .SH OPTIONS .TP .B \-a --ansi-color Allows .B bnchat to use ANSI terminal color escapes to make things more readable. You must be using a terminal compatible with VT100 positioning and ANSI color codes. The program will not attempt to determine your terminal type. .TP .B \-b Causes .B bnchat to emulate a Brood Wars client. .TP .B \-c --change-password Causes .B bnchat to prompt for a new login password on the .BR bnetd (1) server. .TP .B \--client Causes .B bnchat to emulate a specific client. The following client tags are recognized: DRTL DSHR STAR SSHR SEXP W2BN. There are short options which may be used instead for the common clients. .TP .B \-d Causes .B bnchat to emulate a Diablo client. .TP .B \-h --help --usage Print usage information and exit. .TP .B \-k --cdkey Causes .B bnchat to report an alternate key to the .BR bnetd (1) server. .TP .B \-o --owner Causes .B bnchat to report an alternate owner to the .BR bnetd (1) server. .TP .B \-n --new-account Causes .B bnchat to prompt to create a new player account on the .BR bnetd (1) server. .TP .B \-s Causes .B bnchat to emulate a Starcraft client. .TP .B \-v --version Print its version number and exit. .TP .B \-w Causes .B bnchat to emulate a Warcraft II BNE client. .SH NOTES The terminal width is attempted to be determined at runtime but if it does not work you may need to try running .BR resize (1) . As a final act of desperation you can try setting the COLUMNS environmental variable. .LP If SIGWINCH and its associated .BR ioctl (2) are supported on your system, resizing the window while the program is running should immediately update width of the entry line. .LP The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS This program assumes that the terminal will move to the beginning of the current line when the carriage return character (^M) is printed. It also assumes the backspace character (^H) will move one column to the left that character printed after that will replace those already on the screen. .LP The terminal settings are not consulted for keybindings. The following are assumed: .TP ^H delete the character to the left of the cursor .TP ^J accept current line .TP ^M (return) accept current line .TP ^T transpose the last two characters .TP ^W delete word to the left of the cursor .TP ^U delete the whole input line .TP ^[ (escape) cancel the current input line .TP ^? (delete) delete the character to the left of the cursor .SH "SEE ALSO" .BR bnetd (1) .SH AUTHOR Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnetd.1000066400000000000000000000076361462640461400161450ustar00rootroot00000000000000.\" .\" Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) .\" Copyright (C) 1998,1999 Ross Combs (ross@bnetd.org) .\" Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNETD 1 "12 June, 2000" "BNETD" "The bnetd Project" .SH NAME bnetd \- Unix Battle.net daemon .SH SYNOPSIS .B bnetd [\fB-f\fP] [\fB-c\fP \fIconfigfile\fP] [\fB-d\fP \fIdumpfile\fP] .SH DESCRIPTION .B bnetd is a program that will eventually completely emulate a Blizzard Battle.net server. Currently Starcraft, Brood War, Diablo, and Warcraft II BNE are supported as clients. .LP It is known to work under most versions of Unix and can also be used under Windows as a win32 console application. .SH OPTIONS .TP .B \-c --config Use this configuration file instead of the compiled-in default location. .TP .B \-d --hexdump Put a hex dump of packets sent and received into the specified file. .TP .B \-f --foreground The server normally acts as a daemon and goes into the background. This forces the server to run in the foreground. .TP .B \-h --help --usage Print server usage information and exit. .TP .B \-v --version Print its version number and exit. .SH FILES .TP .I bnetd.conf The main configuration file. Among other things, it specifies the locations for the other files used by the daemon. .TP .I bnmotd.txt The "message of the day" file. This is displayed when the user enters the first chat channel. .TP .I bnetd_default_user This is the default account file. The contains all default account attributes which are used when an attribute is not set on an actual account. .I channel.list This is the permanent channel list. Short names, target clients, and other attributes may be specified here. .TP .I news.txt The contensts of this file are printed when the /news command is used. .TP .I ad.list This is the ad banner list which determines which banners to display at which times. The order of display for the banners may be specified as well as the URL which they should follow when clicked. .TP .I bnban The IP ban file can be used to block connections from certain addresses. .TP .I bnhelp This file contains the the chat command help. .TP .I gametrans The game address translation file can be used to allow players from behind a NAT firewall to play with those outside of the firewall. .TP .I autoupdate This is the autoupdate MPQ archive list. It chooses which versions of which clients to update. .TP .I tos.txt This is the Terms Of Service file that is displayed before a user can create a new account. .TP .I icons.bni The icons which the client displays for players in the chat rooms. .TP .I ad000001.pcx This is the ad Banner. Currently, it says "Welcome to... BNETD". .SH NOTES The current version is available at: .LP .RS .I http://www.bnetd.org/ .RE .SH "SEE ALSO" .BR bntrackd (1), .BR bnproxy (1), .BR bnchat (1), .BR bnbot (1), .BR bnpass (1), .BR bnftp (1), .BR bnetd.conf (5), .BR bnmotd.txt (5) .SH AUTHORS Ross Combs (\fIross@bnetd.org\fP), Rob Crittenden (\fIrob@bnetd.org\fP), and Mark Baysinger (\fImark@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnetd.conf.5000066400000000000000000000254341462640461400170710ustar00rootroot00000000000000.\" .\" Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) .\" Copyright (C) 1998,1999,2000 Ross Combs (ross@bnetd.org) .\" Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNETD.CONF 5 "12 June, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnetd.conf \- configuration for the Unix Battle.net daemon .SH DESCRIPTION The file .I bnetd.conf contains information used by the Unix Battle.net daemon .BR bnetd (1) to obtain system-specific information such as file locations and policies. .LP The file consists of a list of configuration entries and comments. A comment starts with a pound (#) sign and continues to the end of the line. Blank lines and whitespace are ignored between entries. A configuration entry is composed of two fields separated by an equals (=) sign: .IP \f2directive\f1 = \f2value\f1 .LP The .I directive field contains the name of the configuration item you want to set to the value of \f2value\f1. If the value contains spaces it should be surrounded by quotation marks ("). The value may not contain quotation marks. Multiple entries are not allowed on a single line. .LP If the field is Boolean, any of \fBtrue\fP, \fBfalse\fP, \fByes\fP, \fBno\fP, \fB1\fP, or \fB0\fP may be used. If the field is numeric, it must be in base ten. If the field is an address list, it must be delimited by commas. Each element may contain a hostname (or IP) and a port number. If a port number is given, it must be preceded by a colon (:). A colon by itself may be used to represent the wildcard address. If the field is a file or directory path, use of an absolute pathname is recommended. The values for \fBiconfile\fP, \fBtosfile\fP, and \fBmpqauthfile\fP are not pathnames but just the names of the files. They must be in the \fBfiledir\fP directory. .LP Recognized values for \f2directive\f1 include: .RS .TP 10 .B filedir Specifies the path where downloadable files are stored. These files are include the TOS (terms of service), icons, and ad banners. (Normally "files/") .TP .B userdir Specify the path where user accounts are stored. (Normally "users/") .TP .B reportdir Specify the path where game reports are stored. (Normally "reports/") .TP .B chanlogdir Specify the path where channel logs are stored. (Normally "chanlogs/") .TP .B defacct Specify the path of the default account file. This file contains all default account attributes which are used when an attribute is not set on an actual account. (Normally "bnetd_default_user") .TP .B motdfile Specify the path of the "message of the day" text file. This is displayed when a user enters the first chat channel. (Normally "bnmotd.txt") .TP .B channelfile Specify the path of the permanent channel list. (Normally "channel.list") .TP .B newsfile Specify the path of the file that is printed when the /news command is used. If no file is specified or the file doesn't exist then the server prints "No news today" to the user. (Normally "news.txt") .TP .B adfile Specify the path of the ad banner list which determines which banners to display at which times. (Normally "ad.list") .TP .B ipbanfile Specify the path of the IP ban file which can be used to block connections from certain addresses. (Normally "bnban") .TP .B helpfile Specify the path of the chat command help file. (Normally "bnhelp") .TP .B transfile Specify the path of the game address translation file which can be used to allow players from behind a NAT firewall to play with those outside of the firewall. (Normally "gametrans") .TP .B mpqfile Specify the path of the autoupdate MPQ archive list. It chooses which versions of which clients to update. (Normally "autoupdate") .TP .B logfile Specifies the file where the server appends the event messages selected with the \f3loglevels\f1 field. (Normally "bnetd.log") .TP .B pidfile Specify the path of the file where the server records its process ID. (Normally "bnetd.pid") .TP .B fortunecmd The path for the program to be executed when /fortune is used. See .BR fortune (6). (Normally "/usr/games/fortune") .TP .B loglevels Specify A comma-delimited list of message levels to log. The avaliable levels are: .RS .TP .B trace flow tracing statements .TP .B debug debugging information .TP .B info potentially useful informational .TP .B warn potentially serious events .TP .B error error messages and unexpected events .TP .B fatal fatal errors .RE .TP .B iconfile Specify the name of the BNI chat icons file which must be inside of the .B filedir directory. (Normally "icons.bni") .TP .B tosfile Specify the name of the TOS file which must be inside of the .B filedir directory. (Normally "tos.txt") .TP .B allow_autoupdate Enable or disable to automatic client update facility. If you want to allow different client versions to connect, set this to false. If you want to update all clients to the same version, be sure to also download the MPQ auth file, and the update MPQ. .TP .B mpqauthfile Specify the name of the MPQ authorization file which must be inside of the .B filedir directory. This file is used to determine the current client version. This is only needed if .B allow_autoupdate is turned on. (Normally "IX86ver1.mpq") .TP .B usersync Specify the time in seconds between account file saves. A value of 0 means to wait until shutdown to save account files and not do do periodic updates. .TP .B userflush Specify the number of seconds of inactivity before a user is unloaded from memory (only checked during file account updates, see usersync. After the account has been unloaded, it must be read from disk when accessed again. .TP .B latency How often to send user latency tests to the user, in seconds. These are used to decide how many "lag bars" to print next to users in channels. .TP .B shutdown_delay Specify the amount of time in seconds to delay shutting down the server when it receives a SIGTERM or SIGINT .BR signal (7). .TP .B shutdown_decr You can decrease the amount of time to wait by this value by sending another SIGTERM or SIGINT .BR signal (7). .TP .B newaccounts If this value is true then new accounts may be created by users on the system. Administrators can always use the /addacct chat command. .TP .B kick_oldlogin If this value is true then a second login by the same user will kick off the first, otherwise the second login will get message saying that they are already logged in. .TP .B ask_new_channel If this value is true, a user using the "Channels" button to join a non-existing channel will be asked if they wish to create it just like Battle.net. Otherwise, the channel will be created silently. .TP .B report_all_games If this value is true, all games with more than one player lasting for more than 5 minutes will be logged. Otherwise, only ladder games are logged. .TP .B hide_pass_games If this value is true, private (passworded) games will not be shown in the game list just like Battle.net. Otherwise all games will be listed. .TP .B hide_started_games If this value is true, games which have already stated will not be sent to the clients. The clients filter these games out for Starcraft and Brood War already. .TP .B hide_temp_channels If this value is true, private channels will not be shown in the channel list just like Battle.net. Otherwise all channels will be listed. .TP .B extra_commands If this value is true then all the extended chat commands are enabled, otherwise only those which exist on Battle.net are enabled. .TP .B disc_is_loss If this value is true then any disconnect will be treated as a loss (regardless of the setting in ladder games). .TP .B enable_conn_all If this value is true then all users may use the /con and /connections commands. Otherwise only administrators may use them. .TP .B hide_addr If this value is true then IP addresses will be hidden from non-admins in the output of the /con, /games, /gameinfo, and /netinfo commands. .TP .B chanlog If this value is true then private channels will be logged to files in the .B chanlogdir direcotry. The logging of public (permanent) channels is controlled in the .B channelfile file. .TP .B track Set this value to 0 to disable sending updates to the tracking server. Any other number will set the number of seconds between tracking packets. Try to use a number above 60 seconds unless you know the tracking server administrator doesn't mind being spammed. .TP .B trackaddrs Specify a comma-delimited list of addresses of tracking servers to send updates to. The address for the master server is "track.bnetd.org". A port may be specified after a colon numerically or by name from .BR /etc/services (5). If a port number is not specified, it defaults to 6114. .TP .B location Give a description of the physical location of the server such as "San Diego, CA, USA". This is reported to the tracking server. .TP .B description Give a description of title or role of the server such as "Joe's Spiffy Server". This is reported to the tracking server. .TP .B url Give a URL for the homepage or contact for the server such as "http://www.foo.com/joeserv.html". This is reported to the tracking server. .TP .B contact_name Give the name of the contact for the server such as "Joe Blockhead". This is reported to the tracking server. .TP .B contact_email Give the email address of the contact for the server such as "joe@foo.com". This is reported to the tracking server. .TP .B use_keepalive If this value is set to true, the server will enable the TCP keepalive option to allow the system to detect stale connections. .TP .B servaddrs Specify the comma-delimited list of addresses the server should listen on. A port may be specified after a colon numerically or by name from .BR /etc/services (5). If a port number is not specified, it defaults to 6112. .TP .B udptest_port The UDP port number which is assumed for clients if they don't send SESSIONADDR packets. If it is set to zero, the server will use the remote TCP port number as the default UDP port number. .SH SEE ALSO .BR bnetd (1) .SH AUTHOR Rob Crittenden (\fIrob@bnetd.org\fP), Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnftp.1000066400000000000000000000062271462640461400161550ustar00rootroot00000000000000.\" .\" Copyright (C) 1998,1999 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNFTP 1 "11 June, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnftp \- Battle.net file transfer client .SH SYNOPSIS .B bnftp [\fIoptions\fP] [\fIservername\fP [\fIserverport\fP]] .SH DESCRIPTION .B bnftp is a simple program for downloading data files from a bnetd server. These files include \fItos.txt\fP, upgrade MPQ archives, and ad banners. .LP If no \fIservername\fP is specified, localhost is assumed. If no \fIserverport\fP is specified, 6112 is assumed. .LP After the program is started it will connect to the server and then prompt for the filename to download. No directory components may be used in the filename. The file is copied to a file with the same name in the current directory. .SH OPTIONS .TP .B \-b Causes .B bnftp to emulate a Brood Wars client. .TP .B \--client Causes .B bnftp to emulate a specific client. The following client tags are recognized: DRTL DSHR STAR SSHR SEXP W2BN. There are short options which may be used instead for the common clients. .TP .B \-d Causes .B bnftp to emulate a Diablo client. .TP .B \-h --help --usage Print usage information and exit. .TP .B \-s Causes .B bnftp to emulate a Starcraft client. .TP .B \-v --version Print its version number and exit. .TP .B \-w Causes .B bnftp to emulate a Warcraft II BNE client. .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS This program assumes that the terminal will move to the beginning of the current line when the carriage return character (^M) is printed. It also assumes the backspace character (^H) will move one column to the left that character printed after that will replace those already on the screen. .LP The terminal settings are not consulted for keybindings. The following are assumed: .TP ^H delete the character to the left of the cursor .TP ^J accept current line .TP ^M (return) accept current line .TP ^T transpose the last two characters .TP ^W delete word to the left of the cursor .TP ^U delete the whole input line .TP ^[ (escape) cancel the current input line .TP ^? (delete) delete the character to the left of the cursor .SH "SEE ALSO" .BR bnetd (1) .SH AUTHOR Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bni2tga.1000066400000000000000000000045461462640461400163740ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNI2TGA 1 "6 November, 2000" "BNETD" "BNETD User's Manual" .SH NAME bni2tga \- convert a Battle.net icon file to a Targa file .SH SYNOPSIS .B bni2tga [\fIoptions\fP] [--] [\fIbnifile\fP [\fItgafile\fP]] .SH DESCRIPTION .B bni2tga is a simple program which will convert the icons in a Battle.net icon file into a TGA file with the icons arranged vertically. .LP The specific variant of TGA that is used will depend on the input file, but typically it will be a 28 pixel wide, true-color, 24bit, run-length encoded image with no attributes. .LP If .I bnifile is not specified or is specified as "-", the standard input will be used. If .I tgafile is not specified or is specified as "-", the standard output will be used. .SH OPTIONS .TP .B \-h --help --usage Print usage information and exit. .TP .B \-v --version Print its version number and exit. .TP .B -- Signal end of options. Any further arguments will be treated as filenames even if they begin with a "-". .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS The purpose of some of the fields in the BNI format are not known. Only true-color formats are currently supported. .SH "SEE ALSO" .BR bnetd (1), .BR bnibuild (1), .BR bniextract (1), .BR bnilist (1), .BR tgainfo (1) .SH AUTHORS .TP Marco Ziech (\fImmz@gmx.net\fP) and Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnibuild.1000066400000000000000000000042661462640461400166350ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNIBUILD 1 "6 November, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnibuild \- build a Battle.net icon file from a directory of Targa icon files .SH SYNOPSIS .B bnibuild [\fIoptions\fP] [--] \fIinputdir\fP [\fIbnifile\fP] .SH DESCRIPTION .B bnibuild is a simple program which will convert a directory with a text file named .I bniindex.lst and associated Targa image file into a single Battle.net icon file. .LP If .I bnifile is not specified or is specified as "-", the standard output will be used. .LP FIXME: specify format of bniindex.lst .SH OPTIONS .TP .B \-h --help --usage Print usage information and exit. .TP .B \-v --version Print its version number and exit. .TP .B -- Signal end of options. Any further arguments will be treated as filenames even if they begin with a "-". .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS The purpose of some of the fields in the BNI format are not known. Only true-color formats are currently supported. .SH "SEE ALSO" .BR bnetd (1), .BR bni2tga (1), .BR bniextract (1), .BR bnilist (1), .BR tgainfo (1) .SH AUTHORS .TP Marco Ziech (\fImmz@gmx.net\fP) and Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bniextract.1000066400000000000000000000041731462640461400172050ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNIEXTRACT 1 "6 November, 2000" "BNETD" "BNETD User's Manual" .SH NAME bniextract \- extract a directory of Targa icon files from a Battle.net icon file .SH SYNOPSIS .B bniextract [\fIoptions\fP] [--] \fIbnifile\fP \fIoutputdir\fP .SH DESCRIPTION .B bniextract is a simple program which will convert a single Battle.net icon file into a directory with a text file named .I bniindex.lst and associated Targa image file. If .I bnifile is specified as "-", the standard input will be used. .SH OPTIONS .TP .B \-h --help --usage Print usage information and exit. .TP .B \-v --version Print its version number and exit. .TP .B -- Signal end of options. Any further arguments will be treated as filenames even if they begin with a "-". .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS The purpose of some of the fields in the BNI format are not known. Only true-color formats are currently supported. .SH "SEE ALSO" .BR bnetd (1), .BR bni2tga (1), .BR bnibuild (1), .BR bnilist (1), .BR tgainfo (1) .SH AUTHORS .TP Marco Ziech (\fImmz@gmx.net\fP) and Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnilist.1000066400000000000000000000044171462640461400165070ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNILIST 1 "6 November, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnilist \- list contents of a Battle.net icon file .SH SYNOPSIS .B bnilist [\fIoptions\fP] [--] [\fIbnifile\fP] .SH DESCRIPTION .B bnilist is a simple program which will list all the icons stored in a BNI file along with their attributes. Additionally, information is displayed from the TGA header in the same format as .BR tgainfo (1) would use. Some simple sanity checks are also done to verify the file is using the same format as expected by the other bni utilities. .LP If .I bnifile is not specified or is specified as "-", the standard input will be used. .SH OPTIONS .TP .B \-h --help --usage Print usage information and exit. .TP .B \-v --version Print its version number and exit. .TP .B -- Signal end of options. Any further arguments will be treated as filenames even if they begin with a "-". .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS The purpose of some of the fields in the BNI format are not known. Only true-color formats are currently supported. .SH "SEE ALSO" .BR bnetd (1), .BR bni2tga (1), .BR bnibuild (1), .BR bniextract (1), .BR tgainfo (1) .SH AUTHORS .TP Marco Ziech (\fImmz@gmx.net\fP) and Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnpass.1000066400000000000000000000037651462640461400163360ustar00rootroot00000000000000.\" .\" Copyright (C) 1998,1999,2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNPASS 1 "09 April, 1999" "BNETD" "BNETD User's Manual" .SH NAME bnpass \- bnetd password utility .SH SYNOPSIS .B bnpass [\fIoptions\fP] [--] [\fIcleartextpassword\fP] .SH DESCRIPTION .B bnpass is a small utility which will output the ASCII hashed version of the clear text password in the same format as used in bnetd account files. This may be useful for writing a script to generate accounts or to reset a password in the bnetd user database. .LP If .I cleartextpassword is not specified you will be prompted to enter it into the standard input. .SH OPTIONS .TP .B \-h --help --usage Print usage information and exit. .TP .B \-v --version Print its version number and exit. .TP .B -- Signal end of options. Any further arguments will be treated as passwords even if they begin with a "-". .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH "SEE ALSO" .BR bnetd (1), .BR bnchat (1), .BR bnftp (1) .SH AUTHOR Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnpcap.1000066400000000000000000000032741462640461400163060ustar00rootroot00000000000000.\" .\" Copyright (C) 2001 Hakan Tandogan (hakan@gurkensalat.com) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNPCAP 1 "07 July, 2001" "BNETD" "BNETD Developers's Manual" .SH NAME bnpcap .SH SYNOPSIS .B bnpcap [\fB-d\fP] [\fB-v\fP] <\fIpcap-filename\fP> .SH DESCRIPTION .B bnpcap is a utility to decode packet dumps from .B tcpdump into a developer-readable format .SH OPTTIONS .TP .B \-h Print usage information and exit .TP .B \-d Print out debugging information .TP .B \-v Be more verbose .LP You have to generate the dumps with .B tcpdump \-i <\fIyour_interface\fP> \-s 2000 \-w <\fIpcap-filename\fP> .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH "SEE ALSO" .BR bnetd (1), .BR tcpdump (1) .SH AUTHOR Marco Ziech (\fmmz@gmx.net\fP) pvpgn-1.99.7.2.1+dfsg/man/bnproxy.1000066400000000000000000000050721462640461400165420ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNPROXY 1 "12 June, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnproxy \- Battle.net proxy daemon .SH SYNOPSIS .B bnproxy [\fB-f\fP] [\fB-d\fP \fIdumpfile\fP] [\fB-l\fP \fIlogfile\fP] [\fB-p\fP \fIport\fP] [\fIservername\fP [\fIserverport\fP]] .BR .SH DESCRIPTION .B bnproxy is a simple daemon which will relay client connections to a Battle.net server like .BR bnetd (1). It has the capability of saving a hexadecimal/ASCII dump of the network records (packets) into a log file. Someday this command might be useful for people playing from behind a NAT firewall. .LP If no \fIservername\fP is specified, localhost is assumed. If no \fIserverport\fP is specified, 6112 is assumed. .SH OPTIONS .TP .B \-d --hexdump Put a hex dump of packets sent and received into the specified file. .TP .B \-f --foreground The server normally acts as a daemon and goes into the background. This forces the server to run in the foreground. .TP .B \-h --help --usage Print server usage information and exit. .TP .B \-l --logfile Put the event log messages into the specified file. .TP .B \-p --port Listen on the specified TCP and UDP port number. .TP .B \-v --version Causes .B bnproxy to print its version number and exit. .SH NOTES .TP The current version is available at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS No support for dynamic UDP address determination (SESSIONADDR[12] packets) has been added. Support for multiple clients is broken because the server becomes confused as to which client to relay UDP server traffic to. .SH "SEE ALSO" .BR bnetd (1) .SH AUTHOR Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bnstat.1000066400000000000000000000072571462640461400163430ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNSTAT 1 "11 June, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnstat \- Battle.net statisics client .SH SYNOPSIS .B bnstat [\fIoptions\fP] [\fIservername\fP [\fIserverport\fP]] .SH DESCRIPTION .B bnstat is a simple Battle.net client emulator that can be used to gather player statistics. It is also compatible with FSGS servers, but has difficulty with real Battle.net servers. .LP If no \fIservername\fP is specified, localhost is assumed. If no \fIserverport\fP is specified, 6112 is assumed. If no player is specified on the command line, .BR bnstat (1) will connect to the server and prompt for one from stdin. All avaliable statistics for the emulated client type are formatted and displayed on stdout. .SH OPTIONS .TP .B \-b Causes \fBbnstat\fP to emulate a Brood Wars client. .TP .B \--client Causes \fBbnstat\fP to emulate a specific client. The following client tags are recognized: DRTL DSHR STAR SSHR SEXP W2BN. There are short options which may be used instead for the common clients. .TP .B \--bnetd Also attempt to gather .BR bnetd (1) specific information. .TP .B \-d Causes \fBbnstat\fP to emulate a Diablo client. .TP .B \--fsgs Also attempt to gather FSGS specific information. .TP .B \-h --help --usage Print usage information and exit. .TP .B \-k --cdkey Causes \fBbnstat\fP to report an alternate key to the .BR bnetd (1) server. .TP .B \-o --owner Causes \fBbnstat\fP to report an alternate owner to the .BR bnetd (1) server. .TP .B \-p --player Gather statistics only for the specified player and then exit. This also avoids using login and terminal functions allowing it to be used in shell scripts. .TP .B \-s Causes \fBbnstat\fP to emulate a Starcraft client. .TP .B \-v --version Print its version number and exit. .TP .B \-w Causes \fBbnstat\fP to emulate a Warcraft II BNE client. .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS This program assumes that the terminal will move to the beginning of the current line when the carriage return character (^M) is printed. It also assumes the backspace character (^H) will move one column to the left that character printed after that will replace those already on the screen. .LP The terminal settings are not consulted for keybindings. The following are assumed: .TP ^H delete the character to the left of the cursor .TP ^J accept current line .TP ^M (return) accept current line .TP ^T transpose the last two characters .TP ^W delete word to the left of the cursor .TP ^U delete the whole input line .TP ^[ (escape) cancel the current input line .TP ^? (delete) delete the character to the left of the cursor .SH "SEE ALSO" .BR bnetd (1) .SH AUTHOR Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bntext.5000066400000000000000000000060141462640461400163460ustar00rootroot00000000000000.\" .\" Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) .\" Copyright (C) 1998,1999,2000 Ross Combs (ross@bnetd.org) .\" Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH bntext 5 "2 August, 2001" .SH NAME bnmotd.txt, bnnews.txt bnissue.txt \- messages for the Unix Battle.net daemon .SH DESCRIPTION The file .I bnmotd.txt contains text displayed by .BR bnetd (1), when users first log into the server. .LP The file .I bnnews.txt contains text displayed when the user uses the /news chat command. .LP The files consist of raw text with printf-style formatting escapes. Each line of a file can contain a type formatter from the following list: .RS .TP .B %B Use the broadcast attribute (???). .TP .B %C Execute the line as if the user entered it as a command. .TP .B %E Use the error attribute (red). .TP .B %I Use the info attribute (yellow). This is the same was .B %W. .TP .B %M Normal chat message (white). This will appear as if the user said it. .TP .B %T Emote chat message (???). This will appear as if the user said it. .TP .B %W Use the warning attribute (yellow). This is the same was .B %I. .RE .LP Within a line, any of the following format formatters may be used: .RS .TP .B %% Expand to a literal percent sign (%). .TP .B %a Expand to the number of accounts on the server. .TP .B %c Expand to the number of channels on the server. This includes all permanent and current temporary channels. .TP .B %g Expand to the number of games on the server. This includes both public and private (passworded) games. .TP .B %h Expand to the hostname of the server (as returned by .BR gethostname (2)). .TP .B %i Expand to this user's account ID number, formatted with a leading pound (#) sign and leading zeros. .TP .B %l Expand to this user's current chat name which is usually the same .TP .B %r Expand to the IP of the remote machine (the client). .TP .B %t Expand to four character client tag. .TP .B %u Expand to the number of users logged into the server. .TP .B %v Expand to the version number of the server. .RE .SH "SEE ALSO" .TP .BR bnetd (1) .SH AUTHOR .TP Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/bntrackd.1000066400000000000000000000067051462640461400166350ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH BNPROXY 1 "12 September, 2000" "BNETD" "BNETD User's Manual" .SH NAME bnproxy \- bnetd tracking daemon .SH SYNOPSIS .B bntrackd [\fIoptions\fP] .BR .SH DESCRIPTION .B bntrackd is a simple daemon which will listen for UDP announcement packets sent by .BR bnetd (1) servers. The list of servers and their statistics is written to a text file an optionally processed by an external program. A .BR perl (1) script is provided which processes this information to produce an HTML document suitable for display on the web. .LP .BR bnetd (1) servers must be configured to report to tracking servers. The .BR bnetd.conf (5) file allows for multiple tracking servers to be listed separated by commas. The master server is track.bnetd.org. Please consider including that server in your configuration. .SH OPTIONS .TP .B \-c --command Run the specified command after writing server information to \fIoutfile\fP. If this option is not specified, the command "scripts/process.pl" is executed from the directory that .B bntrackd was started. If an empty string is provided for \fIcommand\fP, no command will be executed. .TP .B \-e --expire Set the period of time in seconds to keep server information without receiving new updates. If this option is not specified, 600 seconds is used. .TP .B \-d --debug Place additional informational messages in the event log. .TP .B \-h --help --usage Print usage information and exit. .TP .B \-l --logfile Put the event log messages into the specified file. If this option is not specified, the file "logs/bntrackd.log" will be written from the directory that .B bntrackd was started. If an empty string is provided for \fIlogfile\fP, no event messages will be written. .TP .TP .B \-o --outfile Put the server information into the specified file \fIoutfile\fP.. .TP .B \-p --port Listen for announcements on the specified UDP port number. If this option is not specified, the port number defaults to 6114. .TP .B \-P --pidfile Record the process ID of the tracking daemon in the specified file. If this option is not specified, this information is not written to any file. .TP .B \-u --update Set the period of time in seconds between updates of the \fIoutfile\fP. If this option is not specified, 150 seconds is used. .TP .B \-v --version Causes .B bntrackd to print its version number and exit. .SH NOTES .TP The current version is available at: .LP .RS .I http://www.bnetd.org/ .RE .SH "SEE ALSO" .BR bnetd (1), .BR perl (1) .SH AUTHOR Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/man/tgainfo.1000066400000000000000000000040621462640461400164660ustar00rootroot00000000000000.\" .\" Copyright (C) 2000 Ross Combs (ross@bnetd.org) .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, .\" USA. .\" .TH TGAINFO 1 "6 November, 2000" "BNETD" "BNETD User's Manual" .SH NAME tgainfo \- display information about a Targa image .SH SYNOPSIS .B tgainfo [\fIoptions\fP] [--] [\fItgafile\fP] .SH DESCRIPTION .B tgainfo is a simple program which will display information from the TGA header. This information may be useful for determining if a file is in the correct format for .BR bnibuild (1) or other programs. .LP If .I tgafile is not specified or is specified as "-", the standard input will be used. .SH OPTIONS .TP .B \-h --help --usage Print usage information and exit. .TP .B \-v --version Print its version number and exit. .TP .B -- Signal end of options. Any further arguments will be treated as filenames even if they begin with a "-". .SH NOTES The current version is available with the bnetd distribution at: .LP .RS .I http://www.bnetd.org/ .RE .SH BUGS Only a subset of all the possible Targa formats are recognized properly. .SH "SEE ALSO" .BR bnetd (1), .BR bni2tga (1), .BR bnibuild (1), .BR bniextract (1), .BR bnilist (1) .SH AUTHOR .TP Ross Combs (\fIross@bnetd.org\fP) pvpgn-1.99.7.2.1+dfsg/scripts/000077500000000000000000000000001462640461400156675ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/scripts/S98bnetd000066400000000000000000000025241462640461400172150ustar00rootroot00000000000000#!/bin/sh # A semi-RedHat style init script for staring bnetd . /etc/rc.d/init.d/functions bnetduser="bnetd" bnetdhome="/home/bnetd" bnetdpid="${bnetdhome}/bnetd.pid" bnetdexe="${bnetdhome}/sbin/bnetd" case "$1" in start) echo -n "Starting bnetd: " if [ -r "${bnetdpid}" ] && kill -0 `cat "${bnetdpid}"`; then echo "${bnetdpid} exists and the pid is still valid." else if [ -d "${bnetdhome}" ]; then if [ -x "${bnetdexe}" ]; then # su -c "cd ${bnetdhome}; ${bnetdexe} -f &" - "${bnetduser}" 2>&1 > /dev/null su -c "${bnetdexe}" - "${bnetduser}" 2>&1 > /dev/null # "${bnetdexe}" echo bnetd else echo "${bnetdexe} is not an executable." fi else echo "${bnetdhome} is not a directory." fi fi ;; restart) echo -n "Restarting bnetd: " if [ -f "${bnetdpid}" ] && kill -0 `cat "${bnetdpid}"`; then kill -HUP `cat "${bnetdpid}"` echo "bnetd" else echo "bnetd is not running." fi ;; stop) echo -n "Stopping bnetd: " if [ -f "${bnetdpid}" ]; then kill -TERM `cat "${bnetdpid}"` sleep 1 if [ -f "${bnetdpid}" ] && kill -0 `cat "${bnetdpid}"` 2>&1 > /dev/null; then echo "waiting for users to log out." else rm -f "${bnetdpid}" echo "bnetd" fi else echo "bnetd is not running." fi ;; status) status bnetd ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 esac exit 0 pvpgn-1.99.7.2.1+dfsg/scripts/announce.sh000066400000000000000000000034071462640461400200350ustar00rootroot00000000000000#!/bin/sh # This script is to announce a message on a server automatically # and repeatedly. It is intended for server admin use. # Here is an example: # # announce.sh localhost account password 30 "Attention: Here is an # announcement\nAnd here is another announcement" # # The bnchat program can be obtained from the bnetd package. BNCHAT=bnchat PIPE="/tmp/pipe-bnannounce-$$" cleanup () { kill -9 "${pid}" 2> /dev/null rm -f "${PIPE}" 2> /dev/null exit 0 } if [ -z "$4" ]; then echo -e "Usage: $0 server account password interval [msgs] ..." echo -e " server server ip or hostname" echo -e " account your server account" echo -e " password password for your account" echo -e " interval time intervals between announce in seconds" echo -e " [msgs] messages you want to announce" echo echo -e "Notes: Your account should have announce or admin permissions" echo -e " If interval is zero then bnannounce will only print" echo -e " one copy of the announcement." echo exit fi rm -f "${PIPE}" mknod "${PIPE}" p > /dev/null if [ $? -ne 0 ] ; then echo "$0: failed to make pipe file ${PIPE}, check your permissions." >&2 exit 1 fi server="$1" user="$2" pass="$3" interval="$4" shift 4 msg="`echo -e "$*" | sed -e 's/^/\/announce /g'`" "${BNCHAT}" < "${PIPE}" > /dev/null 2>&1 & pid="$!" trap "eval cleanup" SIGINT SIGQUIT SIGTERM EXIT echo -e "${user}" > "${PIPE}" echo -e "${pass}" > "${PIPE}" echo "/join Support" > "${PIPE}" while kill -0 "${pid}" 2> /dev/null; do echo "/announce ${msg}" > "${PIPE}" if [ "${interval}" -lt "1" ]; then exit fi sleep "${interval}" done exit pvpgn-1.99.7.2.1+dfsg/scripts/bnetd.init000066400000000000000000000014601462640461400176510ustar00rootroot00000000000000# -*- sh -*- # # bnetd Script to control the Unix Battle.net Daemon # # Author: Hakan Tandogan # # chkconfig: 2345 21 79 # description: bnetd is a Unix clone of the Battle.net Daemon # # Source function library. . /etc/rc.d/init.d/functions # See how we were called. case "$1" in start) # Start daemons. echo -n "Starting bnetd: " daemon bnetd echo touch /var/lock/subsys/bnetd ;; stop) # Stop daemons. echo -n "Shutting down bnetd: " killproc bnetd echo rm -f /var/lock/subsys/bnetd ;; reload) $0 stop $0 start ;; restart) $0 stop $0 start ;; status) status bnetd ;; *) echo "Usage: bnetd {start|stop|restart|reload|status}" exit 1 esac exit 0 pvpgn-1.99.7.2.1+dfsg/scripts/bnetd.logrotate000066400000000000000000000002011462640461400206760ustar00rootroot00000000000000/var/log/bnetd/bnetd.log { rotate 9 missingok notifempty postrotate /usr/bin/killall -HUP bnetd endscript } pvpgn-1.99.7.2.1+dfsg/scripts/bnetmasq.sh000066400000000000000000000076511462640461400200460ustar00rootroot00000000000000#!/bin/sh # Author: Marco Ziech (mmz@gmx.net) # Date: Jan-24-2000 # # Bnetd UDP masquerade for Linux # # NOTE: # This script is not needed if you have "loose UDP" turned # on. It was a patch around the 2.2 series. It automates # all of this for any "well behaved" peer-to-peer games, # not just for Battle.net clients. # # Kernel: # This script should work fine with 2.2 kernels but I don't # know if it works with older kernels too. # # Installation: # Copy this file into your local init.d directory. # The position of this directory may vary with diffrent # linux distributions. # On Debian/GNU Linux it's /etc/init.d # On S.u.S.E. Linux it's /sbin/init.d (I guess) # To start the script on system startup you have to # make a symlink to this file from a filename like # "S99bnetmasq.sh" in the rc2.d directory. To stop it # on system shutdown do the same with "K01bnetmasq.sh" # in the rc0.d, rc1.d and rc6.d directories. These # directories are usually in the same as the init.d # directory. # If you have a dial-up connection and want to start the # script on dial-in you have to put a script which starts # or stops this script in /etc/ppp/ip-up.d and # /etc/ppp/ip-down.d . # # Using on original Bnet: # This script should also work with the original Battle.net. # # Using on servers not running on your masquerading gateway: # Outside servers like the real Battle.net will not # know your correct address, only your internal IP:port. # to fix it, either bnproxy needs to be improved to # be used instead of ipmasq or this need to do some # content altering like with the ftp script. # --- Thanks to Ross Combs for these corrections. # This is the destination port on the clients. It is usually 6112. BNET_PORT=6112 # This is the list of the destination hosts with their ports on # the firewall. The list should look like this: # client1IP:GW1port client2IP:GW2port ... clientNIP:GWNport # Remember that each port can only be allocated by one client. # Some ports may already be reserved by some programs. # I recomment using ports between 5000 and 7000. See # /etc/services on information about assigned ports. REDIR_LIST="192.168.1.1:5000 192.168.1.2:5001" # This is the external interface i.e. the interface which is # connected to the Internet. On dial-up links this is usually # ppp0 or ippp0 (for ISDN). EXTERNAL_IF=eth0 # ------------------- END OF CONFIG SECTION ----------------- # This determines the ip of the external interface EXTERNAL_IP=`LANGUAGE="C" LC_ALL="C" ifconfig $EXTERNAL_IF | grep 'inet addr:' | sed 's/.*inet addr:\([0-9.]*\).*/\1/g'` case "$1" in start) echo -n "Starting bnet masquerading: " for i in $REDIR_LIST; do echo -n "$i " D_HOST="`echo $i | sed -e 's/:/ /g' | awk '{ print $1 }'`" D_PORT="`echo $i | sed -e 's/:/ /g' | awk '{ print $2 }'`" ipmasqadm portfw -a -P udp -L $EXTERNAL_IP $D_PORT -R $D_HOST $BNET_PORT # FIXME: according to: http://www.mail-archive.com/masq@tori.indyramp.com/msg01538.html #> setting up a single machine would look like: #> # ipmasqadm portfw -a -P tcp -L $1 6112 -R $2 6112 #> # ipmasqadm portfw -a -P tcp -L $3 6112 -R $2 6112 #> # ipmasqadm portfw -a -P udp -L $1 6112 -R $2 6112 #> # ipmasqadm portfw -a -P udp -L $3 6112 -R $2 6112 #> Replace $1 with the internal IP address of your gateway machine, $2 #> with the address (internal again, but it should be the only) of the #> machine you're playing starcraft on and $3 with the external IP ad- #> dress of the gateway machine. # This implies that # inside_gw --> client # outside_gw --> inside_gw # this script doesn't do the gw --> gw setup.... does it need to?! done echo "." ;; stop) echo -n "Stopping bnet masquerading: " for i in $REDIR_LIST; do echo -n "$i " D_PORT="`echo $i | sed -e 's/:/ /g' | awk '{ print $2 }'`" ipmasqadm portfw -d -P udp -L $EXTERNAL_IP $D_PORT done echo "." ;; *) echo "Usage: $0 {start|stop}" exit 1 ;; esac pvpgn-1.99.7.2.1+dfsg/scripts/convert_w3.pl000066400000000000000000000045621462640461400203240ustar00rootroot00000000000000#!/usr/bin/perl if (scalar(@ARGV) != 2) { &usage; exit; } &header; # remove a trailing / $dirpath = $ARGV[0]; $dirpath =~ s!(.*)/$!$1!; $dirpath2 = $ARGV[1]; $dirpath2 =~ s!(.*)/$!$1!; $max_uid = -1; opendir FILEDIR, $dirpath or die "Error opening filedir!\n"; while ($filename = readdir FILEDIR) { if ($filename =~ m/^\./) { next; } #ignore . and .. if ( ! (($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat $dirpath ."/". $filename )) { print "Error stat-ing the file $pathdir/$filename!\n" ; next; } $type = ($mode & 070000) >> 12; if ($type != 0) { print "File ". $dirpath ."/". $filename ." its not regular\n"; next; } convertfile($filename); } closedir FILEDIR; sub convertfile { my $filen = shift; my ($tab, $col, $val); open FILE, $dirpath ."/". $filen or die "Error opening file ". $dirpath ."/". $filen ."\n"; if (open FILEOUT, $dirpath2 ."/". $filen) { die "Found existent file : ". $dirpath2 ."/". $filen ."!! Plese provide an empty output directory !!"; } open FILEOUT, ">". $dirpath2 ."/". $filen or die "Error opening output file ". $dirpath2 ."/". $filen ."\n"; print "Converting file ". $filen ."... "; while($line = ) { if ($line =~ m/".*"=".*"/) { $line =~ m/^"([A-Za-z0-9]+)\\\\(.*)"="(.*)"\n$/; $tab = $1; $col = $2; $val = $3; if ($tab =~ m/^team$/i) { print FILEOUT "\"". $tab . "\\\\WAR3\\\\" . $col . "\"=\"" . $val ."\"\n"; next; } if ($tab =~ m/^record$/i) { if ($col =~ m/^solo/i or $col =~ m/^team/i or $col =~ m/^ffa/i or $col =~ m/^orcs/i or $col =~ m/^humans/i or $col =~ m/^nightelves/i or $col =~ m/^undead/i or $col =~ m/^random/i) { print FILEOUT "\"". $tab . "\\\\WAR3\\\\" . $col . "\"=\"" . $val ."\"\n"; next; } } print FILEOUT $line; } } close FILEOUT; close FILE; print "done\n"; } sub header { print "Account files WAR3 converting tool.\n"; print " Copyright (C) 2003 Dizzy (dizzy\@roedu.net)\n"; print " People Versus People Gaming Network (www.pvpgn.org)\n\n"; } sub usage { &header; print "Usage:\n\n\tconvert_w3.pl \n\n"; print "\t \t: directory with the input account files\n"; print "\t \t: directory where the output account files will reside\n"; } pvpgn-1.99.7.2.1+dfsg/scripts/cvs2cl.pl000066400000000000000000001621211462640461400174230ustar00rootroot00000000000000#!/bin/sh exec perl -w -x $0 ${1+"$@"} # -*- mode: perl; perl-indent-level: 2; -*- #!perl -w ############################################################## ### ### ### cvs2cl.pl: produce ChangeLog(s) from `cvs log` output. ### ### ### ############################################################## ## ## (C) 1999 Karl Fogel , under the GNU GPL. ## ## (Extensively hacked on by Melissa O'Neill .) ## ## cvs2cl.pl is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2, or (at your option) ## any later version. ## ## cvs2cl.pl is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You may have received a copy of the GNU General Public License ## along with cvs2cl.pl; see the file COPYING. If not, write to the ## Free Software Foundation, Inc., 59 Temple Place - Suite 330, ## Boston, MA 02111-1307, USA. use strict; use Text::Wrap; use Time::Local; use File::Basename; # The Plan: # # Read in the logs for multiple files, spit out a nice ChangeLog that # mirrors the information entered during `cvs commit'. # # The problem presents some challenges. In an ideal world, we could # detect files with the same author, log message, and checkin time -- # each would be a changelog entry. # We'd sort them; and spit them out. Unfortunately, CVS is *not atomic* # so checkins can span a range of times. Also, the directory structure # could be hierarchical. # # Another question is whether we really want to have the ChangeLog # exactly reflect commits. An author could issue two related commits, # with different log entries, reflecting a single logical change to the # source. GNU style ChangeLogs group these under a single author/date. # We try to do the same. # # So, we parse the output of `cvs log', storing log messages in a # multilevel hash that stores the mapping: # directory => author => time => message => filelist # As we go, we notice "nearby" commit times and store them together # (i.e., under the same timestamp), so they appear in the same log # entry. # # When we've read all the logs, we twist this mapping into # a time => author => message => filelist mapping for each directory. # # If we're not using the `--distributed' flag, the directory is always # considered to be `./', even as descend into subdirectories. ############### Globals ################ # What we run to generate it: my $Log_Source_Command = "cvs log"; # In case we have to print it out: my $VERSION = '$Revision: 1.1.1.1 $'; $VERSION =~ s/\S+\s+(\S+)\s+\S+/$1/; ## Vars set by options: # Print debugging messages? my $Debug = 0; # Just show version and exit? my $Print_Version = 0; # Just print usage message and exit? my $Print_Usage = 0; # Single top-level ChangeLog, or one per subdirectory? my $Distributed = 0; # What file should we generate (defaults to "ChangeLog")? my $Log_File_Name = "ChangeLog"; # Grab most recent entry date from existing ChangeLog file, just add # to that ChangeLog. my $Cumulative = 0; # Expand usernames to email addresses based on a map file? my $User_Map_File = ""; # Output to a file or to stdout? my $Output_To_Stdout = 0; # Eliminate empty log messages? my $Prune_Empty_Msgs = 0; # Don't call Text::Wrap on the body of the message my $No_Wrap = 0; # Separates header from log message. Code assumes it is either " " or # "\n\n", so if there's ever an option to set it to something else, # make sure to go through all conditionals that use this var. my $After_Header = " "; # Format more for programs than for humans. my $XML_Output = 0; # Do some special tweaks for log data that was written in FSF # ChangeLog style. my $FSF_Style = 0; # Show times in UTC instead of local time my $UTC_Times = 0; # Show day of week in output? my $Show_Day_Of_Week = 0; # Show revision numbers in output? my $Show_Revisions = 0; # Show tags (symbolic names) in output? my $Show_Tags = 0; # Show branches by symbolic name in output? my $Show_Branches = 0; # Show only revisions on these branches or their ancestors. my @Follow_Branches; # Don't bother with files matching this regexp. my @Ignore_Files; # How exactly we match entries. We definitely want "o", # and user might add "i" by using --case-insensitive option. my $Case_Insensitive = 0; # Maybe only show log messages matching a certain regular expression. my $Regexp_Gate = ""; # Pass this global option string along to cvs, to the left of `log': my $Global_Opts = ""; # Pass this option string along to the cvs log subcommand: my $Command_Opts = ""; # Read log output from stdin instead of invoking cvs log? my $Input_From_Stdin = 0; # Don't show filenames in output. my $Hide_Filenames = 0; # Max checkin duration. CVS checkin is not atomic, so we may have checkin # times that span a range of time. We assume that checkins will last no # longer than $Max_Checkin_Duration seconds, and that similarly, no # checkins will happen from the same users with the same message less # than $Max_Checkin_Duration seconds apart. my $Max_Checkin_Duration = 180; # What to put at the front of [each] ChangeLog. my $ChangeLog_Header = ""; ## end vars set by options. # In 'cvs log' output, one long unbroken line of equal signs separates # files: my $file_separator = "=======================================" . "======================================"; # In 'cvs log' output, a shorter line of dashes separates log messages # within a file: my $logmsg_separator = "----------------------------"; ############### End globals ############ &parse_options (); &derive_change_log (); ### Everything below is subroutine definitions. ### # If accumulating, grab the boundary date from pre-existing ChangeLog. sub maybe_grab_accumulation_date () { if (! $Cumulative) { return ""; } # else open (LOG, "$Log_File_Name") or die ("trouble opening $Log_File_Name for reading ($!)"); my $boundary_date; while () { if (/^(\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d)/) { $boundary_date = "$1"; last; } } close (LOG); return $boundary_date; } # Fills up a ChangeLog structure in the current directory. sub derive_change_log () { # See "The Plan" above for a full explanation. my %grand_poobah; my $file_full_path; my $time; my $revision; my $author; my $msg_txt; my $detected_file_separator; # Might be adding to an existing ChangeLog my $accumulation_date = &maybe_grab_accumulation_date (); if ($accumulation_date) { $Log_Source_Command .= " -d\'>${accumulation_date}\'"; } # We might be expanding usernames my %usermap; # In general, it's probably not very maintainable to use state # variables like this to tell the loop what it's doing at any given # moment, but this is only the first one, and if we never have more # than a few of these, it's okay. my $collecting_symbolic_names = 0; my %symbolic_names; # Where tag names get stored. my %branch_names; # We'll grab branch names while we're at it. my %branch_numbers; # Save some revisions for @Follow_Branches my @branch_roots; # For showing which files are branch ancestors. # Bleargh. Compensate for a deficiency of custom wrapping. if (($After_Header ne " ") and $FSF_Style) { $After_Header .= "\t"; } if (! $Input_From_Stdin) { open (LOG_SOURCE, "$Log_Source_Command |") or die "unable to run \"${Log_Source_Command}\""; } else { open (LOG_SOURCE, "-") or die "unable to open stdin for reading"; } %usermap = &maybe_read_user_map_file (); while () { # If on a new file and don't see filename, skip until we find it, and # when we find it, grab it. if ((! (defined $file_full_path)) and /^Working file: (.*)/) { $file_full_path = $1; if (@Ignore_Files) { my $base; ($base, undef, undef) = fileparse ($file_full_path); # Ouch, I wish trailing operators in regexps could be # evaluated on the fly! if ($Case_Insensitive) { if (grep ($file_full_path =~ m|$_|i, @Ignore_Files)) { undef $file_full_path; } } elsif (grep ($file_full_path =~ m|$_|, @Ignore_Files)) { undef $file_full_path; } } next; } # Just spin wheels if no file defined yet. next if (! $file_full_path); # Collect tag names in case we're asked to print them in the output. if (/^symbolic names:$/) { $collecting_symbolic_names = 1; next; # There's no more info on this line, so skip to next } if ($collecting_symbolic_names) { # All tag names are listed with whitespace in front in cvs log # output; so if see non-whitespace, then we're done collecting. if (/^\S/) { $collecting_symbolic_names = 0; } else # we're looking at a tag name, so parse & store it { # According to the Cederqvist manual, in node "Tags", tag # names must start with an uppercase or lowercase letter and # can contain uppercase and lowercase letters, digits, `-', # and `_'. However, it's not our place to enforce that, so # we'll allow anything CVS hands us to be a tag: /^\s+([^:]+): ([\d.]+)$/; my $tag_name = $1; my $tag_rev = $2; # A branch number either has an odd number of digit sections # (and hence an even number of dots), or has ".0." as the # second-to-last digit section. Test for these conditions. my $real_branch_rev = ""; if (($tag_rev =~ /^(\d+\.\d+\.)+\d+$/) # Even number of dots... and (! ($tag_rev =~ /^(1\.)+1$/))) # ...but not "1.[1.]1" { $real_branch_rev = $tag_rev; } elsif ($tag_rev =~ /(\d+\.(\d+\.)+)0.(\d+)/) # Has ".0." { $real_branch_rev = $1 . $3; } # If we got a branch, record its number. if ($real_branch_rev) { $branch_names{$real_branch_rev} = $tag_name; if (@Follow_Branches) { if (grep ($_ eq $tag_name, @Follow_Branches)) { $branch_numbers{$tag_name} = $real_branch_rev; } } } else { # Else it's just a regular (non-branch) tag. push (@{$symbolic_names{$tag_rev}}, $tag_name); } } } # End of code for collecting tag names. # If have file name, but not revision, and see revision, then grab # it. (We collect unconditionally, even though we may or may not # ever use it.) if ((! (defined $revision)) and (/^revision (\d+\.[\d.]+)/)) { $revision = $1; if (@Follow_Branches) { foreach my $branch (@Follow_Branches) { # Special case for following trunk revisions if (($branch =~ /^trunk$/i) and ($revision =~ /^[0-9]+\.[0-9]+$/)) { goto dengo; } my $branch_number = $branch_numbers{$branch}; if ($branch_number) { # Are we on one of the follow branches or an ancestor of # same? # # If this revision is a prefix of the branch number, or # possibly is less in the minormost number, OR if this # branch number is a prefix of the revision, then yes. # Otherwise, no. # # So below, we determine if any of those conditions are # met. # Trivial case: is this revision on the branch? # (Compare this way to avoid regexps that screw up Emacs # indentation, argh.) if ((substr ($revision, 0, ((length ($branch_number)) + 1))) eq ($branch_number . ".")) { goto dengo; } # Non-trivial case: check if rev is ancestral to branch elsif ((length ($branch_number)) > (length ($revision))) { $revision =~ /^((?:\d+\.)+)(\d+)$/; my $r_left = $1; # still has the trailing "." my $r_end = $2; $branch_number =~ /^((?:\d+\.)+)(\d+)\.\d+$/; my $b_left = $1; # still has trailing "." my $b_mid = $2; # has no trailing "." if (($r_left eq $b_left) && ($r_end <= $b_mid)) { goto dengo; } } } } } else # (! @Follow_Branches) { next; } # Else we are following branches, but this revision isn't on the # path. So skip it. undef $revision; dengo: next; } # If we don't have a revision right now, we couldn't possibly # be looking at anything useful. if (! (defined ($revision))) { $detected_file_separator = /^$file_separator$/o; if ($detected_file_separator) { # No revisions for this file; can happen, e.g. "cvs log -d DATE" goto CLEAR; } else { next; } } # If have file name but not date and author, and see date or # author, then grab them: unless (defined $time) { if (/^date: .*/) { ($time, $author) = &parse_date_and_author ($_); if (defined ($usermap{$author}) and $usermap{$author}) { $author = $usermap{$author}; } } else { $detected_file_separator = /^$file_separator$/o; if ($detected_file_separator) { # No revisions for this file; can happen, e.g. "cvs log -d DATE" goto CLEAR; } } # If the date/time/author hasn't been found yet, we couldn't # possibly care about anything we see. So skip: next; } # A "branches: ..." line here indicates that one or more branches # are rooted at this revision. If we're showing branches, then we # want to show that fact as well, so we collect all the branches # that this is the latest ancestor of and store them in # @branch_roots. Just for reference, the format of the line we're # seeing at this point is: # # branches: 1.5.2; 1.5.4; ...; # # Okay, here goes: if (/^branches:\s+(.*);$/) { if ($Show_Branches) { my $lst = $1; $lst =~ s/(1\.)+1;|(1\.)+1$//; # ignore the trivial branch 1.1.1 if ($lst) { @branch_roots = split (/;\s+/, $lst); } else { undef @branch_roots; } next; } else { # Ugh. This really bothers me. Suppose we see a log entry # like this: # # ---------------------------- # revision 1.1 # date: 1999/10/17 03:07:38; author: jrandom; state: Exp; # branches: 1.1.2; # Intended first line of log message begins here. # ---------------------------- # # The question is, how we can tell the difference between that # log message and a *two*-line log message whose first line is # # "branches: 1.1.2;" # # See the problem? The output of "cvs log" is inherently # ambiguous. # # For now, we punt: we liberally assume that people don't # write log messages like that, and just toss a "branches:" # line if we see it but are not showing branches. I hope no # one ever loses real log data because of this. next; } } # If have file name, time, and author, then we're just grabbing # log message texts: $detected_file_separator = /^$file_separator$/o; if ($detected_file_separator && ! (defined $revision)) { # No revisions for this file; can happen, e.g. "cvs log -d DATE" goto CLEAR; } unless ($detected_file_separator || /^$logmsg_separator$/o) { $msg_txt .= $_; # Normally, just accumulate the message... next; } # ... until a msg separator is encountered: # Ensure the message contains something: if ((! $msg_txt) || ($msg_txt =~ /^\s*\.\s*$|^\s*$/) || ($msg_txt =~ /\*\*\* empty log message \*\*\*/)) { if ($Prune_Empty_Msgs) { goto CLEAR; } # else $msg_txt = "[no log message]\n"; } ### Store it all in the Grand Poobah: { my $dir_key; # key into %grand_poobah my %qunk; # complicated little jobbie, see below # Each revision of a file has a little data structure (a `qunk') # associated with it. That data structure holds not only the # file's name, but any additional information about the file # that might be needed in the output, such as the revision # number, tags, branches, etc. The reason to have these things # arranged in a data structure, instead of just appending them # textually to the file's name, is that we may want to do a # little rearranging later as we write the output. For example, # all the files on a given tag/branch will go together, followed # by the tag in parentheses (so trunk or otherwise non-tagged # files would go at the end of the file list for a given log # message). This rearrangement is a lot easier to do if we # don't have to reparse the text. # # A qunk looks like this: # # { # filename => "hello.c", # revision => "1.4.3.2", # time => a timegm() return value (moment of commit) # tags => [ "tag1", "tag2", ... ], # branch => "branchname" # There should be only one, right? # branchroots => [ "branchtag1", "branchtag2", ... ] # } if ($Distributed) { # Just the basename, don't include the path. ($qunk{'filename'}, $dir_key, undef) = fileparse ($file_full_path); } else { $dir_key = "./"; $qunk{'filename'} = $file_full_path; } # This may someday be used in a more sophisticated calculation # of what other files are involved in this commit. For now, we # don't use it, because the common-commit-detection algorithm is # hypothesized to be "good enough" as it stands. $qunk{'time'} = $time; # We might be including revision numbers and/or tags and/or # branch names in the output. Most of the code from here to # loop-end deals with organizing these in qunk. $qunk{'revision'} = $revision; # Grab the branch, even though we may or may not need it: $qunk{'revision'} =~ /((?:\d+\.)+)\d+/; my $branch_prefix = $1; $branch_prefix =~ s/\.$//; # strip off final dot if ($branch_names{$branch_prefix}) { $qunk{'branch'} = $branch_names{$branch_prefix}; } # If there's anything in the @branch_roots array, then this # revision is the root of at least one branch. We'll display # them as branch names instead of revision numbers, the # substitution for which is done directly in the array: if (@branch_roots) { my @roots = map { $branch_names{$_} } @branch_roots; $qunk{'branchroots'} = \@roots; } # Save tags too. if (defined ($symbolic_names{$revision})) { $qunk{'tags'} = $symbolic_names{$revision}; delete $symbolic_names{$revision}; } # Add this file to the list # (We use many spoonfuls of autovivication magic. Hashes and arrays # will spring into existence if they aren't there already.) &debug ("(pushing log msg for ${dir_key}$qunk{'filename'})\n"); # Store with the files in this commit. Later we'll loop through # again, making sure that revisions with the same log message # and nearby commit times are grouped together as one commit. push (@{$grand_poobah{$dir_key}{$author}{$time}{$msg_txt}}, \%qunk); } CLEAR: # Make way for the next message undef $msg_txt; undef $time; undef $revision; undef $author; undef @branch_roots; # Maybe even make way for the next file: if ($detected_file_separator) { undef $file_full_path; undef %branch_names; undef %branch_numbers; undef %symbolic_names; } } close (LOG_SOURCE); ### Process each ChangeLog while (my ($dir,$authorhash) = each %grand_poobah) { &debug ("DOING DIR: $dir\n"); # Here we twist our hash around, from being # author => time => message => filelist # in %$authorhash to # time => author => message => filelist # in %changelog. # # This is also where we merge entries. The algorithm proceeds # through the timeline of the changelog with a sliding window of # $Max_Checkin_Duration seconds; within that window, entries that # have the same log message are merged. # # (To save space, we zap %$authorhash after we've copied # everything out of it.) my %changelog; while (my ($author,$timehash) = each %$authorhash) { my $lasttime; my %stamptime; foreach my $time (sort {$main::a <=> $main::b} (keys %$timehash)) { my $msghash = $timehash->{$time}; while (my ($msg,$qunklist) = each %$msghash) { my $stamptime = $stamptime{$msg}; if ((defined $stamptime) and (($time - $stamptime) < $Max_Checkin_Duration) and (defined $changelog{$stamptime}{$author}{$msg})) { push(@{$changelog{$stamptime}{$author}{$msg}}, @$qunklist); } else { $changelog{$time}{$author}{$msg} = $qunklist; $stamptime{$msg} = $time; } } } } undef (%$authorhash); ### Now we can write out the ChangeLog! my ($logfile_here, $logfile_bak, $tmpfile); if (! $Output_To_Stdout) { $logfile_here = $dir . $Log_File_Name; $logfile_here =~ s/^\.\/\//\//; # fix any leading ".//" problem $tmpfile = "${logfile_here}.cvs2cl$$.tmp"; $logfile_bak = "${logfile_here}.bak"; open (LOG_OUT, ">$tmpfile") or die "Unable to open \"$tmpfile\""; } else { open (LOG_OUT, ">-") or die "Unable to open stdout for writing"; } print LOG_OUT $ChangeLog_Header; if ($XML_Output) { print LOG_OUT "\n\n" . "\n\n"; } foreach my $time (sort {$main::b <=> $main::a} (keys %changelog)) { my $authorhash = $changelog{$time}; while (my ($author,$mesghash) = each %$authorhash) { # If XML, escape in outer loop to avoid compound quoting: if ($XML_Output) { $author = &xml_escape ($author); } while (my ($msg,$qunklist) = each %$mesghash) { my $files = &pretty_file_list ($qunklist); my $header_line; # date and author my $body; # see below my $wholething; # $header_line + $body # Set up the date/author line. # kff todo: do some more XML munging here, on the header # part of the entry: my ($ignore,$min,$hour,$mday,$mon,$year,$wday) = $UTC_Times ? gmtime($time) : localtime($time); # XML output includes everything else, we might as well make # it always include Day Of Week too, for consistency. if ($Show_Day_Of_Week or $XML_Output) { $wday = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")[$wday]; $wday = ($XML_Output) ? "${wday}\n" : " $wday"; } else { $wday = ""; } if ($XML_Output) { $header_line = sprintf ("%4u-%02u-%02u\n" . "${wday}" . "\n" . "%s\n", $year+1900, $mon+1, $mday, $hour, $min, $author); } else { $header_line = sprintf ("%4u-%02u-%02u${wday} %02u:%02u %s\n\n", $year+1900, $mon+1, $mday, $hour, $min, $author); } # Reshape the body according to user preferences. if ($XML_Output) { $msg = &preprocess_msg_text ($msg); $body = $files . $msg; } elsif ($No_Wrap) { $msg = &preprocess_msg_text ($msg); $files = wrap ("\t", " ", "$files"); $msg =~ s/\n(.*)/\n\t$1/g; unless ($After_Header eq " ") { $msg =~ s/^(.*)/\t$1/g; } $body = $files . $After_Header . $msg; } else # do wrapping, either FSF-style or regular { if ($FSF_Style) { $files = wrap ("\t", " ", "$files"); my $files_last_line_len = 0; if ($After_Header eq " ") { $files_last_line_len = &last_line_len ($files); $files_last_line_len += 1; # for $After_Header } $msg = &wrap_log_entry ($msg, "\t", 69 - $files_last_line_len, 69); $body = $files . $After_Header . $msg; } else # not FSF-style { $msg = &preprocess_msg_text ($msg); $body = $files . $After_Header . $msg; $body = wrap ("\t", " ", "$body"); } } $wholething = $header_line . $body; if ($XML_Output) { $wholething = "\n${wholething}\n"; } # One last check: make sure it passes the regexp test, if the # user asked for that. We have to do it here, so that the # test can match against information in the header as well # as in the text of the log message. # How annoying to duplicate so much code just because I # can't figure out a way to evaluate scalars on the trailing # operator portion of a regular expression. Grrr. if ($Case_Insensitive) { unless ($Regexp_Gate && ($wholething =~ /$Regexp_Gate/oi)) { print LOG_OUT "${wholething}\n"; } } else { unless ($Regexp_Gate && ($wholething =~ /$Regexp_Gate/o)) { print LOG_OUT "${wholething}\n"; } } } } } if ($XML_Output) { print LOG_OUT "\n"; } close (LOG_OUT); if (! $Output_To_Stdout) { # If accumulating, append old data to new before renaming. But # don't append the most recent entry, since it's already in the # new log due to CVS's idiosyncratic interpretation of "log -d". if ($Cumulative && -f $logfile_here) { open (NEW_LOG, ">>$tmpfile") or die "trouble appending to $tmpfile ($!)"; open (OLD_LOG, "<$logfile_here") or die "trouble reading from $logfile_here ($!)"; my $started_first_entry = 0; my $passed_first_entry = 0; while () { if (! $passed_first_entry) { if ((! $started_first_entry) && /^(\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d)/) { $started_first_entry = 1; } elsif (/^(\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d)/) { $passed_first_entry = 1; print NEW_LOG $_; } } else { print NEW_LOG $_; } } close (NEW_LOG); close (OLD_LOG); } if (-f $logfile_here) { rename ($logfile_here, $logfile_bak); } rename ($tmpfile, $logfile_here); } } } sub parse_date_and_author () { # Parses the date/time and author out of a line like: # # date: 1999/02/19 23:29:05; author: apharris; state: Exp; my $line = shift; my ($year, $mon, $mday, $hours, $min, $secs, $author) = $line =~ m#(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);\s+author:\s+([^;]+);# or die "Couldn't parse date ``$line''"; die "Bad date or Y2K issues" unless ($year > 1969 and $year < 2258); # Kinda arbitrary, but useful as a sanity check my $time = timegm($secs,$min,$hours,$mday,$mon-1,$year-1900); return ($time, $author); } # Here we take a bunch of qunks and convert them into printed # summary that will include all the information the user asked for. sub pretty_file_list () { if ($Hide_Filenames and (! $XML_Output)) { return ""; } my $qunksref = shift; my @qunkrefs = @$qunksref; my @filenames; my $beauty = ""; # The accumulating header string for this entry. my %non_unanimous_tags; # Tags found in a proper subset of qunks my %unanimous_tags; # Tags found in all qunks my %all_branches; # Branches found in any qunk my $common_dir = undef; # Dir prefix common to all files ("" if none) my $fbegun = 0; # Did we begin printing filenames yet? # First, loop over the qunks gathering all the tag/branch names. # We'll put them all in non_unanimous_tags, and take out the # unanimous ones later. foreach my $qunkref (@qunkrefs) { # Keep track of whether all the files in this commit were in the # same directory, and memorize it if so. We can make the output a # little more compact by mentioning the directory only once. if ((scalar (@qunkrefs)) > 1) { if (! (defined ($common_dir))) { my ($base, $dir); ($base, $dir, undef) = fileparse ($$qunkref{'filename'}); if ((! (defined ($dir))) # this first case is sheer paranoia or ($dir eq "") or ($dir eq "./") or ($dir eq ".\\")) { $common_dir = ""; } else { $common_dir = $dir; } } elsif ($common_dir ne "") { # Already have a common dir prefix, so how much of it can we preserve? $common_dir = &common_path_prefix ($$qunkref{'filename'}, $common_dir); } } else # only one file in this entry anyway, so common dir not an issue { $common_dir = ""; } if (defined ($$qunkref{'branch'})) { $all_branches{$$qunkref{'branch'}} = 1; } if (defined ($$qunkref{'tags'})) { foreach my $tag (@{$$qunkref{'tags'}}) { $non_unanimous_tags{$tag} = 1; } } } # Any tag held by all qunks will be printed specially... but only if # there are multiple qunks in the first place! if ((scalar (@qunkrefs)) > 1) { foreach my $tag (keys (%non_unanimous_tags)) { my $everyone_has_this_tag = 1; foreach my $qunkref (@qunkrefs) { if ((! (defined ($$qunkref{'tags'}))) or (! (grep ($_ eq $tag, @{$$qunkref{'tags'}})))) { $everyone_has_this_tag = 0; } } if ($everyone_has_this_tag) { $unanimous_tags{$tag} = 1; delete $non_unanimous_tags{$tag}; } } } if ($XML_Output) { # If outputting XML, then our task is pretty simple, because we # don't have to detect common dir, common tags, branch prefixing, # etc. We just output exactly what we have, and don't worry about # redundancy or readability. foreach my $qunkref (@qunkrefs) { my $filename = $$qunkref{'filename'}; my $revision = $$qunkref{'revision'}; my $tags = $$qunkref{'tags'}; my $branch = $$qunkref{'branch'}; my $branchroots = $$qunkref{'branchroots'}; $filename = &xml_escape ($filename); # probably paranoia $revision = &xml_escape ($revision); # definitely paranoia $beauty .= "\n"; $beauty .= "${filename}\n"; $beauty .= "${revision}\n"; if ($branch) { $branch = &xml_escape ($branch); # more paranoia $beauty .= "${branch}\n"; } foreach my $tag (@$tags) { $tag = &xml_escape ($tag); # by now you're used to the paranoia $beauty .= "${tag}\n"; } foreach my $root (@$branchroots) { $root = &xml_escape ($root); # which is good, because it will continue $beauty .= "${root}\n"; } $beauty .= "\n"; } # Theoretically, we could go home now. But as long as we're here, # let's print out the common_dir and utags, as a convenience to # the receiver (after all, earlier code calculated that stuff # anyway, so we might as well take advantage of it). if ((scalar (keys (%unanimous_tags))) > 1) { foreach my $utag ((keys (%unanimous_tags))) { $utag = &xml_escape ($utag); # the usual paranoia $beauty .= "${utag}\n"; } } if ($common_dir) { $common_dir = &xml_escape ($common_dir); $beauty .= "${common_dir}\n"; } # That's enough for XML, time to go home: return $beauty; } # Else not XML output, so complexly compactify for chordate # consumption. At this point we have enough global information # about all the qunks to organize them non-redundantly for output. if ($common_dir) { # Note that $common_dir still has its trailing slash $beauty .= "$common_dir: "; } if ($Show_Branches) { # For trailing revision numbers. my @brevisions; foreach my $branch (keys (%all_branches)) { foreach my $qunkref (@qunkrefs) { if ((defined ($$qunkref{'branch'})) and ($$qunkref{'branch'} eq $branch)) { if ($fbegun) { # kff todo: comma-delimited in XML too? Sure. $beauty .= ", "; } else { $fbegun = 1; } my $fname = substr ($$qunkref{'filename'}, length ($common_dir)); $beauty .= $fname; $$qunkref{'printed'} = 1; # Just setting a mark bit, basically if ($Show_Tags && (defined @{$$qunkref{'tags'}})) { my @tags = grep ($non_unanimous_tags{$_}, @{$$qunkref{'tags'}}); if (@tags) { $beauty .= " (tags: "; $beauty .= join (', ', @tags); $beauty .= ")"; } } if ($Show_Revisions) { # Collect the revision numbers' last components, but don't # print them -- they'll get printed with the branch name # later. $$qunkref{'revision'} =~ /.+\.([\d]+)$/; push (@brevisions, $1); # todo: we're still collecting branch roots, but we're not # showing them anywhere. If we do show them, it would be # nifty to just call them revision "0" on a the branch. # Yeah, that's the ticket. } } } $beauty .= " ($branch"; if (@brevisions) { if ((scalar (@brevisions)) > 1) { $beauty .= ".["; $beauty .= (join (',', @brevisions)); $beauty .= "]"; } else { $beauty .= ".$brevisions[0]"; } } $beauty .= ")"; } } # Okay; any qunks that were done according to branch are taken care # of, and marked as printed. Now print everyone else. foreach my $qunkref (@qunkrefs) { next if (defined ($$qunkref{'printed'})); # skip if already printed if ($fbegun) { $beauty .= ", "; } else { $fbegun = 1; } $beauty .= substr ($$qunkref{'filename'}, length ($common_dir)); # todo: Shlomo's change was this: # $beauty .= substr ($$qunkref{'filename'}, # (($common_dir eq "./") ? "" : length ($common_dir))); $$qunkref{'printed'} = 1; # Set a mark bit. if ($Show_Revisions || $Show_Tags) { my $started_addendum = 0; if ($Show_Revisions) { $started_addendum = 1; $beauty .= " ("; $beauty .= "$$qunkref{'revision'}"; } if ($Show_Tags && (defined $$qunkref{'tags'})) { my @tags = grep ($non_unanimous_tags{$_}, @{$$qunkref{'tags'}}); if ((scalar (@tags)) > 0) { if ($started_addendum) { $beauty .= ", "; } else { $beauty .= " (tags: "; } $beauty .= join (', ', @tags); $started_addendum = 1; } } if ($started_addendum) { $beauty .= ")"; } } } # Unanimous tags always come last. if ($Show_Tags && %unanimous_tags) { $beauty .= " (utags: "; $beauty .= join (', ', keys (%unanimous_tags)); $beauty .= ")"; } # todo: still have to take care of branch_roots? $beauty = "* $beauty:"; return $beauty; } sub common_path_prefix () { my $path1 = shift; my $path2 = shift; my ($dir1, $dir2); (undef, $dir1, undef) = fileparse ($path1); (undef, $dir2, undef) = fileparse ($path2); # Transmogrify Windows filenames to look like Unix. # (It is far more likely that someone is running cvs2cl.pl under # Windows than that they would genuinely have backslashes in their # filenames.) $dir1 =~ tr#\\#/#; $dir2 =~ tr#\\#/#; my $accum1 = ""; my $accum2 = ""; my $last_common_prefix = ""; while ($accum1 eq $accum2) { $last_common_prefix = $accum1; last if ($accum1 eq $dir1); my ($tmp1) = split (/\//, (substr ($dir1, length ($accum1)))); my ($tmp2) = split (/\//, (substr ($dir2, length ($accum2)))); $accum1 .= "$tmp1/" if ((defined ($tmp1)) and $tmp1); $accum2 .= "$tmp2/" if ((defined ($tmp2)) and $tmp2); } return $last_common_prefix; } sub preprocess_msg_text () { my $text = shift; # Strip out carriage returns (as they probably result from DOSsy editors). $text =~ s/\r\n/\n/g; # If it *looks* like two newlines, make it *be* two newlines: $text =~ s/\n\s*\n/\n\n/g; if ($XML_Output) { $text = &xml_escape ($text); $text = "${text}\n"; } elsif (! $No_Wrap) { # Strip off lone newlines, but only for lines that don't begin with # whitespace or a mail-quoting character, since we want to preserve # that kind of formatting. Also don't strip newlines that follow a # period; we handle those specially next. And don't strip # newlines that precede an open paren. 1 while ($text =~ s/(^|\n)([^>\s].*[^.\n])\n([^>\n])/$1$2 $3/g); # If a newline follows a period, make sure that when we bring up the # bottom sentence, it begins with two spaces. 1 while ($text =~ s/(^|\n)([^>\s].*)\n([^>\n])/$1$2 $3/g); } return $text; } sub last_line_len () { my $files_list = shift; my @lines = split (/\n/, $files_list); my $last_line = pop (@lines); return length ($last_line); } # A custom wrap function, sensitive to some common constructs used in # log entries. sub wrap_log_entry () { my $text = shift; # The text to wrap. my $left_pad_str = shift; # String to pad with on the left. # These do NOT take left_pad_str into account: my $length_remaining = shift; # Amount left on current line. my $max_line_length = shift; # Amount left for a blank line. my $wrapped_text = ""; # The accumulating wrapped entry. my $user_indent = ""; # Inherited user_indent from prev line. my $first_time = 1; # First iteration of the loop? my $suppress_line_start_match = 0; # Set to disable line start checks. my @lines = split (/\n/, $text); while (@lines) # Don't use `foreach' here, it won't work. { my $this_line = shift (@lines); chomp $this_line; if ($this_line =~ /^(\s+)/) { $user_indent = $1; } else { $user_indent = ""; } # If it matches any of the line-start regexps, print a newline now... if ($suppress_line_start_match) { $suppress_line_start_match = 0; } elsif (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/) || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/) || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/) || ($this_line =~ /^(\s+)(\S+)/) || ($this_line =~ /^(\s*)- +/) || ($this_line =~ /^()\s*$/) || ($this_line =~ /^(\s*)\*\) +/) || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/)) { # Make a line break immediately, unless header separator is set # and this line is the first line in the entry, in which case # we're getting the blank line for free already and shouldn't # add an extra one. unless (($After_Header ne " ") and ($first_time)) { if ($this_line =~ /^()\s*$/) { $suppress_line_start_match = 1; $wrapped_text .= "\n${left_pad_str}"; } $wrapped_text .= "\n${left_pad_str}"; } $length_remaining = $max_line_length - (length ($user_indent)); } # Now that any user_indent has been preserved, strip off leading # whitespace, so up-folding has no ugly side-effects. $this_line =~ s/^\s*//; # Accumulate the line, and adjust parameters for next line. my $this_len = length ($this_line); if ($this_len == 0) { # Blank lines should cancel any user_indent level. $user_indent = ""; $length_remaining = $max_line_length; } elsif ($this_len >= $length_remaining) # Line too long, try breaking it. { # Walk backwards from the end. At first acceptable spot, break # a new line. my $idx = $length_remaining - 1; if ($idx < 0) { $idx = 0 }; while ($idx > 0) { if (substr ($this_line, $idx, 1) =~ /\s/) { my $line_now = substr ($this_line, 0, $idx); my $next_line = substr ($this_line, $idx); $this_line = $line_now; # Clean whitespace off the end. chomp $this_line; # The current line is ready to be printed. $this_line .= "\n${left_pad_str}"; # Make sure the next line is allowed full room. $length_remaining = $max_line_length - (length ($user_indent)); # Strip next_line, but then preserve any user_indent. $next_line =~ s/^\s*//; # Sneak a peek at the user_indent of the upcoming line, so # $next_line (which will now precede it) can inherit that # indent level. Otherwise, use whatever user_indent level # we currently have, which might be none. my $next_next_line = shift (@lines); if ((defined ($next_next_line)) && ($next_next_line =~ /^(\s+)/)) { $next_line = $1 . $next_line if (defined ($1)); # $length_remaining = $max_line_length - (length ($1)); $next_next_line =~ s/^\s*//; } else { $next_line = $user_indent . $next_line; } if (defined ($next_next_line)) { unshift (@lines, $next_next_line); } unshift (@lines, $next_line); # Our new next line might, coincidentally, begin with one of # the line-start regexps, so we temporarily turn off # sensitivity to that until we're past the line. $suppress_line_start_match = 1; last; } else { $idx--; } } if ($idx == 0) { # We bottomed out because the line is longer than the # available space. But that could be because the space is # small, or because the line is longer than even the maximum # possible space. Handle both cases below. if ($length_remaining == ($max_line_length - (length ($user_indent)))) { # The line is simply too long -- there is no hope of ever # breaking it nicely, so just insert it verbatim, with # appropriate padding. $this_line = "\n${left_pad_str}${this_line}"; } else { # Can't break it here, but may be able to on the next round... unshift (@lines, $this_line); $length_remaining = $max_line_length - (length ($user_indent)); $this_line = "\n${left_pad_str}"; } } } else # $this_len < $length_remaining, so tack on what we can. { # Leave a note for the next iteration. $length_remaining = $length_remaining - $this_len; if ($this_line =~ /\.$/) { $this_line .= " "; $length_remaining -= 2; } else # not a sentence end { $this_line .= " "; $length_remaining -= 1; } } # Unconditionally indicate that loop has run at least once. $first_time = 0; $wrapped_text .= "${user_indent}${this_line}"; } # One last bit of padding. $wrapped_text .= "\n"; return $wrapped_text; } sub xml_escape () { my $txt = shift; $txt =~ s/&/&/g; $txt =~ s//>/g; return $txt; } sub maybe_read_user_map_file () { my %expansions; if ($User_Map_File) { open (MAPFILE, "<$User_Map_File") or die ("Unable to open $User_Map_File ($!)"); while () { next if /^\s*#/; # Skip comment lines. next if not /:/; # Skip lines without colons. # It is now safe to split on ':'. my ($username, $expansion) = split ':'; chomp $expansion; $expansion =~ s/^'(.*)'$/$1/; $expansion =~ s/^"(.*)"$/$1/; # If it looks like the expansion has a real name already, then # we toss the username we got from CVS log. Otherwise, keep # it to use in combination with the email address. if ($expansion =~ /^\s*<{0,1}\S+@.*/) { # Also, add angle brackets if none present if (! ($expansion =~ /<\S+@\S+>/)) { $expansions{$username} = "$username <$expansion>"; } else { $expansions{$username} = "$username $expansion"; } } else { $expansions{$username} = $expansion; } } close (MAPFILE); } return %expansions; } sub parse_options () { # Check this internally before setting the global variable. my $output_file; # If this gets set, we encountered unknown options and will exit at # the end of this subroutine. my $exit_with_admonishment = 0; while (my $arg = shift (@ARGV)) { if ($arg =~ /^-h$|^-help$|^--help$|^--usage$|^-?$/) { $Print_Usage = 1; } elsif ($arg =~ /^--debug$/) { # unadvertised option, heh $Debug = 1; } elsif ($arg =~ /^--version$/) { $Print_Version = 1; } elsif ($arg =~ /^-g$|^--global-opts$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; # Don't assume CVS is called "cvs" on the user's system: $Log_Source_Command =~ s/(^\S*)/$1 $narg/; } elsif ($arg =~ /^-l$|^--log-opts$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; $Log_Source_Command .= " $narg"; } elsif ($arg =~ /^-f$|^--file$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; $output_file = $narg; } elsif ($arg =~ /^--accum$/) { $Cumulative = 1; } elsif ($arg =~ /^--fsf$/) { $FSF_Style = 1; } elsif ($arg =~ /^-U$|^--usermap$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; $User_Map_File = $narg; } elsif ($arg =~ /^-W$|^--window$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; $Max_Checkin_Duration = $narg; } elsif ($arg =~ /^-I$|^--ignore$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; push (@Ignore_Files, $narg); } elsif ($arg =~ /^-C$|^--case-insensitive$/) { $Case_Insensitive = 1; } elsif ($arg =~ /^-R$|^--regexp$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; $Regexp_Gate = $narg; } elsif ($arg =~ /^--stdout$/) { $Output_To_Stdout = 1; } elsif ($arg =~ /^--version$/) { $Print_Version = 1; } elsif ($arg =~ /^-d$|^--distributed$/) { $Distributed = 1; } elsif ($arg =~ /^-P$|^--prune$/) { $Prune_Empty_Msgs = 1; } elsif ($arg =~ /^-S$|^--separate-header$/) { $After_Header = "\n\n"; } elsif ($arg =~ /^--no-wrap$/) { $No_Wrap = 1; } elsif ($arg =~ /^--gmt$|^--utc$/) { $UTC_Times = 1; } elsif ($arg =~ /^-w$|^--day-of-week$/) { $Show_Day_Of_Week = 1; } elsif ($arg =~ /^-r$|^--revisions$/) { $Show_Revisions = 1; } elsif ($arg =~ /^-t$|^--tags$/) { $Show_Tags = 1; } elsif ($arg =~ /^-b$|^--branches$/) { $Show_Branches = 1; } elsif ($arg =~ /^-F$|^--follow$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; push (@Follow_Branches, $narg); } elsif ($arg =~ /^--stdin$/) { $Input_From_Stdin = 1; } elsif ($arg =~ /^--header$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; $ChangeLog_Header = &slurp_file ($narg); if (! defined ($ChangeLog_Header)) { $ChangeLog_Header = ""; } } elsif ($arg =~ /^--xml$/) { $XML_Output = 1; } elsif ($arg =~ /^--hide-filenames$/) { $Hide_Filenames = 1; $After_Header = ""; } else { # Just add a filename as argument to the log command $Log_Source_Command .= " $arg"; } } ## Check for contradictions... if ($Output_To_Stdout && $Distributed) { print STDERR "cannot pass both --stdout and --distributed\n"; $exit_with_admonishment = 1; } if ($Output_To_Stdout && $output_file) { print STDERR "cannot pass both --stdout and --file\n"; $exit_with_admonishment = 1; } if ($XML_Output && $Cumulative) { print STDERR "cannot pass both --xml and --accum\n"; $exit_with_admonishment = 1; } # Or if any other error message has already been printed out, we # just leave now: if ($exit_with_admonishment) { &usage (); exit (1); } elsif ($Print_Usage) { &usage (); exit (0); } elsif ($Print_Version) { &version (); exit (0); } ## Else no problems, so proceed. if ($output_file) { $Log_File_Name = $output_file; } } sub slurp_file () { my $filename = shift || die ("no filename passed to slurp_file()"); my $retstr; open (SLURPEE, "<${filename}") or die ("unable to open $filename ($!)"); my $saved_sep = $/; undef $/; $retstr = ; $/ = $saved_sep; close (SLURPEE); return $retstr; } sub debug () { if ($Debug) { my $msg = shift; print STDERR $msg; } } sub version () { print "cvs2cl.pl version ${VERSION}; distributed under the GNU GPL.\n"; } sub usage () { &version (); print <<'END_OF_INFO'; Generate GNU-style ChangeLogs in CVS working copies. Notes about the output format(s): The default output of cvs2cl.pl is designed to be compact, formally unambiguous, but still easy for humans to read. It is largely self-explanatory, I hope; the one abbreviation that might not be obvious is "utags". That stands for "universal tags" -- a universal tag is one held by all the files in a given change entry. If you need output that's easy for a program to parse, use the --xml option. Note that with XML output, just about all available information is included with each change entry, whether you asked for it or not, on the theory that your parser can ignore anything it's not looking for. Notes about the options and arguments (the actual options are listed last in this usage message): * The -I and -F options may appear multiple times. * To follow trunk revisions, use "-F trunk" ("-F TRUNK" also works). This is okay because no would ever, ever be crazy enough to name a branch "trunk", right? Right. * For the -U option, the UFILE should be formatted like CVSROOT/users. That is, each line of UFILE looks like this jrandom:jrandom@red-bean.com or maybe even like this jrandom:'Jesse Q. Random ' Don't forget to quote the portion after the colon if necessary. * Many people want to filter by date. To do so, invoke cvs2cl.pl like this: cvs2cl.pl -l "-d'DATESPEC'" where DATESPEC is any date specification valid for "cvs log -d". (Note that CVS 1.10.7 and below requires there be no space between -d and its argument). Options/Arguments: -h, -help, --help, or -? Show this usage and exit --version Show version and exit -r, --revisions Show revision numbers in output -b, --branches Show branch names in revisions when possible -t, --tags Show tags (symbolic names) in output --stdin Read from stdin, don't run cvs log --stdout Output to stdout not to ChangeLog -d, --distributed Put ChangeLogs in subdirs -f FILE, --file FILE Write to FILE instead of "ChangeLog" --fsf Use this if log data is in FSF ChangeLog style -W SECS, --window SECS Window of time within which log entries unify -U UFILE, --usermap UFILE Expand usernames to email addresses from UFILE -R REGEXP, --regexp REGEXP Include only entries that match REGEXP -I REGEXP, --ignore REGEXP Ignore files whose names match REGEXP -C, --case-insensitive Any regexp matching is done case-insensitively -F BRANCH, --follow BRANCH Show only revisions on or ancestral to BRANCH -S, --separate-header Blank line between each header and log message --no-wrap Don't auto-wrap log message (recommend -S also) --gmt, --utc Show times in GMT/UTC instead of local time --accum Add to an existing ChangeLog (incompat w/ --xml) -w, --day-of-week Show day of week --header FILE Get ChangeLog header from FILE ("-" means stdin) --xml Output XML instead of ChangeLog format --hide-filenames Don't show filenames (ignored for XML output) -P, --prune Don't show empty log messages -g OPTS, --global-opts OPTS Invoke like this "cvs OPTS log ..." -l OPTS, --log-opts OPTS Invoke like this "cvs ... log OPTS" FILE1 [FILE2 ...] Show only log information for the named FILE(s) See http://www.red-bean.com/cvs2cl for maintenance and bug info. END_OF_INFO } __END__ =head1 NAME cvs2cl.pl - produces GNU-style ChangeLogs in CVS working copies, by running "cvs log" and parsing the output. Shared log entries are unified in an intuitive way. =head1 DESCRIPTION This script generates GNU-style ChangeLog files from CVS log information. Basic usage: just run it inside a working copy and a ChangeLog will appear. It requires repository access (i.e., 'cvs log' must work). Run "cvs2cl.pl --help" to see more advanced options. See http://www.red-bean.com/cvs2cl for updates, and for instructions on getting anonymous CVS access to this script. Maintainer: Karl Fogel Please report bugs to . =head1 README This script generates GNU-style ChangeLog files from CVS log information. Basic usage: just run it inside a working copy and a ChangeLog will appear. It requires repository access (i.e., 'cvs log' must work). Run "cvs2cl.pl --help" to see more advanced options. See http://www.red-bean.com/cvs2cl for updates, and for instructions on getting anonymous CVS access to this script. Maintainer: Karl Fogel Please report bugs to . =head1 PREREQUISITES This script requires C, C, and C. It also seems to require C or higher. =pod OSNAMES any =pod SCRIPT CATEGORIES Version_Control/CVS =cut -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- Note about a bug-slash-opportunity: ----------------------------------- There's a bug in Text::Wrap, which affects cvs2cl. This script reveals it: #!/usr/bin/perl -w use Text::Wrap; my $test_text = "This script demonstrates a bug in Text::Wrap. The very long line following this paragraph will be relocated relative to the surrounding text: ==================================================================== See? When the bug happens, we'll get the line of equal signs below this paragraph, even though it should be above."; # Print out the test text with no wrapping: print "$test_text"; print "\n"; print "\n"; # Now print it out wrapped, and see the bug: print wrap ("\t", " ", "$test_text"); print "\n"; print "\n"; If the line of equal signs were one shorter, then the bug doesn't happen. Interesting. Anyway, rather than fix this in Text::Wrap, we might as well write a new wrap() which has the following much-needed features: * initial indentation, like current Text::Wrap() * subsequent line indentation, like current Text::Wrap() * user chooses among: force-break long words, leave them alone, or die()? * preserve existing indentation: chopped chunks from an indented line are indented by same (like this line, not counting the asterisk!) * optional list of things to preserve on line starts, default ">" Note that the last two are essentially the same concept, so unify in implementation and give a good interface to controlling them. And how about: Optionally, when encounter a line pre-indented by same as previous line, then strip the newline and refill, but indent by the same. Yeah... pvpgn-1.99.7.2.1+dfsg/scripts/flat2cdb.pl000066400000000000000000000041051462640461400177050ustar00rootroot00000000000000#!/usr/bin/perl $cdb_command = "/usr/local/bin/cdb"; if (scalar(@ARGV) != 2) { &usage; exit; } &header; # remove a trailing / $dirpath = $ARGV[0]; $dirpath =~ s!(.*)/$!$1!; $dirpath2 = $ARGV[1]; $dirpath2 =~ s!(.*)/$!$1!; $max_uid = -1; opendir FILEDIR, $dirpath or die "Error opening filedir!\n"; while ($filename = readdir FILEDIR) { if ($filename =~ m/^\./) { next; } #ignore . and .. if ( ! (($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat $dirpath ."/". $filename )) { print "Error stat-ing the file $pathdir/$filename!\n" ; next; } $type = ($mode & 070000) >> 12; if ($type != 0) { print "File ". $dirpath ."/". $filename ." its not regular\n"; next; } convertfile($filename); } closedir FILEDIR; sub convertfile { my $filen = shift; my ($key, $val, $outpath); open FILE, $dirpath ."/". $filen or die "Error opening file ". $dirpath ."/". $filen ."\n"; $outpath = $dirpath2 ."/". $filen; if (open FILEOUT, $outpath) { die "Found existent file : ". $outpath ."!! Plese provide an empty output directory !!"; } close FILEOUT; open CDB, "| $cdb_command -c -t '". $outpath . ".tmp' '". $outpath ."'" || die "Error running $cdb_command\n"; print "Converting file ". $filen ."... "; while($line = ) { if ($line =~ m/^#/) { next; } if ($line =~ m/^"(.*)"="(.*)"\s*$/) { $key = $1; $val = $2; $key =~ s/\\\\/\\/g; print CDB "+". length($key) .",". length($val) .":". $key ."->". $val ."\n"; } } print CDB "\n"; close CDB; close FILE; print "done\n"; } sub header { print "Account files flat2cdb converting tool.\n"; print " Copyright (C) 2003 Dizzy (dizzy\@roedu.net)\n"; print " People Versus People Gaming Network (www.pvpgn.org)\n\n"; } sub usage { &header; print "Usage:\n\n\tflat2cdb.pl \n\n"; print "\t \t: directory with the input flat account files\n"; print "\t \t: directory where the output cdb account files will reside\n"; } pvpgn-1.99.7.2.1+dfsg/scripts/fsgs2bnetd.pl000066400000000000000000000035261462640461400202730ustar00rootroot00000000000000#!/usr/bin/perl #Usage: fsgs2bnetd "source directory" "destination directory" #source directory: directory contains fsgs account files #destnation directory: directory to save newly created bnetd # accounts,should exist before run this # #there is another change made to bnetd account in this script #i think fsgs account filename format is better #than using bnetd's , so i kept it in fsgs format. #you may change it by edit the script yourself $ARGV[1] eq "" && die "error arguments"; $s_dir=$ARGV[0]; $d_dir=$ARGV[1]; opendir(DIR,$s_dir) || die "error open dir"; @files= grep { /^[^.]/ && -f "$s_dir/$_" } readdir(DIR); closedir DIR; $userid=1; $fsgs{'name'}="\"BNET\\\\acct\\\\username\""; $fsgs{'password'}="\"BNET\\\\acct\\\\passhash1\""; foreach (@files) { open(S_FILE,"$s_dir/$_") || die "error open s_file: $_\n"; $dest_file=lc("$d_dir/$_"); open(D_FILE,">$dest_file") || die "error open d_file: $_\n"; while () { chop($_); ($name,$value)=split(/:/,$_); foreach (keys %fsgs) { if ($_ eq $name) { if ( $_ eq "password" ) { $value=&passconv($value); } print D_FILE "$fsgs{$_}=\"$value\"\n"; break; } } } print D_FILE "\"BNET\\\\acct\\\\userid\"=\"$userid\"\n"; $userid++; close(S_FILE); close(D_FILE); } sub passconv { ($f_pass)=@_; my ($d_pass)=""; $f_pass=lc($f_pass); $length=length($f_pass); for ($i=0;$i<=$length;$i=$i+2) { $a=2*(int(($i/2)/4)*8+3-$i/2); $d_pass .= substr($f_pass,$a,2); } return $d_pass; } pvpgn-1.99.7.2.1+dfsg/scripts/ladder.py000066400000000000000000000302371462640461400175010ustar00rootroot00000000000000#!/usr/bin/env python # -*- Mode: Python; tab-width: 4 -*- # # Copyright (C) 2001 Gianluigi Tiesi # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # Ranking patch by 20020503 # Small "unsigned int" fix by 20020818 # # ========================================================================== __version__ = "0.9" ### Only if CGI_MODE = 1 CGI_MODE=0 FILE="/opt/bnetd/var/ladders/ladder.D2DV" MAX=100 from struct import unpack,calcsize from string import find,split,join from os import stat from sys import argv,exit,stdout from getopt import getopt #### Templates modes = [ 'html', 'ansi', 'ascii', 'python' ] templates = {} for m in modes: templates[m] = {} ### html ### # templates['html']['header']=""" D2 Closed Realm Ladder

D2 Closed Realm Ladder

""" # templates['html']['footer']="""

Generated by ladder.py v %s - © 2001 Sherpya

""" % __version__ # %s for description of ladder type templates['html']['summary'] = """ Ladder for %s """ # templates['html']['tbheader'] = """#Charnamelevelclassexp """ # %s for charname templates['html']['normal'] = """%s""" templates['html']['hardcore'] = { 0 : """%s""", 1 : """%s""" } # %s charname - %d level - %s class - %d experience templates['html']['entry'] = """%d%s%d%s%d """ # templates['html']['separator'] = """  """ #### html #### ascii / ansi line = '-' * 59 + '\n' s10 = ' ' * 10 s14 = ' ' * 14 s5 = ' ' * 5 text = 'D2 Closed Ladder' esc = '\033' off = esc + '[0m' colors = { 'grey': esc + '[1;30m', 'red': esc + '[1;31m', 'green': esc + '[1;32m', 'yellow': esc + '[1;33m', 'blue': esc + '[1;34m', 'purple': esc + '[1;35m', 'magenta': esc + '[1;36m', 'white': esc + '[1;37m', 'green': esc + '[1;32m' } templates['ascii']['header'] = line + (int((len(line) - len(text))/ 2)) * ' ' + text + '\n' + line templates['ascii']['footer'] = 'generated by ladder.py (c) Sherpya [sherpya@netfarm.it]\n' templates['ascii']['summary'] = 'Ladder for %s\n\n' templates['ascii']['tbheader'] = ' # charname' + s14 + 'level' + s10 + 'class' + s10 + 'exp' + '\n\n' templates['ascii']['normal'] = '%s' templates['ascii']['hardcore'] = { 0 : '*%s', 1: '^%s' } templates['ascii']['entry'] = '%3d %-23s %2d %16s %10d\n' templates['ascii']['separator'] = line + '\n' line = colors['blue'] + ( '-' * 59) + off + '\n' templates['ansi']['header'] = line + (int((len(line) - len(text) - 10)/ 2)) * ' ' + colors['green'] + text + off + '\n' + line templates['ansi']['footer'] = colors['green'] + 'generated by ' + colors['blue'] + 'ladder.py' + colors['green'] + ' (c) Sherpya [sherpya@netfarm.it]' + off + '\n' templates['ansi']['summary'] = colors['white'] + 'Ladder for %s' + off + '\n\n' templates['ansi']['tbheader'] = colors['green'] + ' # charname' + s14 + 'level' + s10 + 'class' + s10 + 'exp' + off + '\n\n' templates['ansi']['normal'] = colors['yellow'] + '%s' templates['ansi']['hardcore'] = { 0 : colors['red'] + '%s', 1: colors['grey'] + '%s' } templates['ansi']['entry'] = colors['yellow'] + '%3d %-30s %2d %16s %10d' + off + '\n' templates['ansi']['separator'] = line + '\n' del text #### ascii / ansi ### Some struct from d2cs/d2dbs source # # ladder header (4 + 4 = 8): # bn_int maxtype # bn_int checksum LD_HEAD="<2i" szLD_HEAD = calcsize(LD_HEAD) # # ladder info (4 + 2 + 1 + 1 + 16 = 24): # bn_int experience # bn_short status # bn_byte level # bn_byte class; # char charname[16]; LD_INFO=" 0: try: experience, status, level, _class, charname = unpack(LD_INFO, data.read(szLD_INFO)) except: ### Bad data size = size - szLD_INFO continue size = size - szLD_INFO ## Avoid null chars if not experience: continue charname = remove_null(charname) died = 0 if status & S_EXP: _type = 'exp' difficulty = ((status >> 0x08) & 0x0f) / 5 else: _type = 'nor' difficulty = ((status >> 0x08) & 0x0f) / 5 if status & S_HC: hc = 1 if status & S_DEAD: died = 1 else: hc = 0 c_class = classes[_class] if difficulty and diff[_type].has_key(difficulty): prefix = diff[_type][difficulty][hc][c_class[1]] else: prefix = None char = (experience, { 'charname' : charname, 'prefix' : prefix, 'experience' : experience, 'class' : c_class[0], 'sex' : c_class[0], 'level' : level, 'type' : _type, 'difficulty' : difficulty, 'hc' : hc, 'died' : died }) ## Dupe char? why? if char not in temp[_type]: temp[_type].append(char) data.close() ## Sorting by exp temp['nor'].sort() temp['nor'].reverse() temp['exp'].sort() temp['exp'].reverse() for _type in temp.keys(): for ch in temp[_type]: ladder[_type].append(ch[1]) del temp return ladder def generate(ladder, mode, output, max): output.write(templates[mode]['header']) for _type in ladder.keys(): count = 1 output.write(templates[mode]['summary'] % desc[_type]) output.write(templates[mode]['tbheader']) for ch in ladder[_type]: if ch['prefix']: charname = "%s %s" % (ch['prefix'], ch['charname']) else: charname = ch['charname'] if ch['hc']: charname = templates[mode]['hardcore'][ch['died']] % charname else: charname = templates[mode]['normal'] % charname output.write(templates[mode]['entry'] % (count, charname, ch['level'], ch['class'], ch['experience'])) count = count + 1 if count > max: break output.write(templates[mode]['separator']) output.write(templates[mode]['footer']) def pickle_to(ladder, output): try: from cPickle import dump except: from pickle import dump try: out = open(output, "wb") except: print "Cannot open %s for pickle dump" % output exit() dump(ladder, out) out.close() ### Main ### CGI MODE if CGI_MODE: print "Content-Type: text/html" print ladder = get_ladder(FILE) generate(ladder, 'html', stdout, MAX) exit() args = argv[1:] optlist, args = getopt(args, "hi:o:m:n:") if len(args): for bad in args: print "%s: Unrecognized option %s" % (argv[0], bad) exit() ### defaults file = None output = None # stdout mode = modes[0] real_max = 1000 max = 100 def show_help(): print print "ladder.py v%s - (c) 2001 Sherpya " % __version__ print "Usage: ladder.py -i ladder_file [-o outputfile] [-m mode] [-n max ladder chars]" print print " -i ladder_file, is the ladder file like ladder.D2DV" print " -o output file, if omitted defaults to stdout" print " -m mode, avaiables mode are: %s, defaults to %s" % (join(modes,', '), modes[0]) print " -n max_char, max char to display in each ladder, defaults to %d" % max print print " note: python output mode creates a python object usable by pickle module" print for opt in optlist: # Help if opt[0] == '-h': show_help() exit() # Input file if opt[0] == '-i': file = opt[1] continue # Output file if opt[0] == '-o': output = opt[1] continue # Output mode (html, ansi, ascii, python) if opt[0] == '-m': if opt[1] in modes: mode = opt[1] continue else: print "Invalid mode %s, valid modes are %s" % (opt[1], join(modes, ', ')) exit() # Max chars in ladder if opt[0] == '-n': try: max = int(opt[1]) except: max = 0 if (max < 2) or max > real_max: print "Invalid value for max char in ladder must be > 1 and < %d" % real_max exit() continue if not file: show_help() exit() ladder = get_ladder(file) if mode == 'python': if output: pickle_to(ladder, output) else: print "Cannot dump python object to stdout" exit() if output: try: output = open(output, "wb") except: print "Cannot open %s for writing" % output exit() else: output = stdout generate(ladder, mode, output, max) pvpgn-1.99.7.2.1+dfsg/scripts/lastlogin.pl000066400000000000000000000016151462640461400202230ustar00rootroot00000000000000#!/usr/bin/perl #Usage: lastlogins.pl $ARGV[1] eq "" && die "error arguments"; $userdir=$ARGV[0]; opendir(DIR,$userdir) || die "error open dir $userdir"; @files= grep { /^[^.]/ && -f "$userdir/$_" } readdir(DIR); closedir DIR; $tbl{'name'}='"BNET\\acct\\lastlogin_time"'; foreach (@files) { open(S_FILE,"$s_dir/$_") || die "error open s_file"; $dest_file=lc("$d_dir/$_"); while () { chop($_); ($name,$value)=split(/:/,$_); foreach (keys %tbl) { if ($_ eq $name) { if ( $_ eq "password" ) { $value=&passconv($value); } break; } } } $userid++; close(S_FILE); } pvpgn-1.99.7.2.1+dfsg/scripts/localize/000077500000000000000000000000001462640461400174715ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/scripts/localize/pvpgn_localize_generator.cs000066400000000000000000000317161462640461400251120ustar00rootroot00000000000000/* Copyright (c) 2014 HarpyWar (harpywar@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Text; using System.Linq; using System.Xml.Serialization; namespace pvpgn_localize_generator { class Program { /// /// C++ localization function name /// const string func_name = "localize"; static string outfile = "output.xml"; static string dirpath; static Root _data = new Root(); static void Main(string[] args) { if (args.Length == 0 || args.Length > 2) { Console.WriteLine("This utility generates XML file for next translation from hardcoded text arguments in function {0}(...) that in *.cpp, *.h, *.lua files\n(c) 2014 HarpyWar (harpywar@gmail.com)", func_name); Console.WriteLine("\nUsage: {0} [path to 'pvpgn' repository directory] {{output.xml}}", AppDomain.CurrentDomain.FriendlyName); Console.WriteLine(" (to make update of existing xml file, pass it in a second parameter)\n", AppDomain.CurrentDomain.FriendlyName); Environment.Exit(0); } dirpath = args[0]; if (args.Length == 2) outfile = args[1]; if (File.Exists(outfile)) { // deserialize xml to data var xsr = new XmlSerializer(typeof(Root)); var reader = new StreamReader(outfile); _data = (Root)xsr.Deserialize(reader); reader.Close(); } string[] cppfiles = Directory.GetFiles(dirpath, "*.cpp", SearchOption.AllDirectories); string[] hfiles = Directory.GetFiles(dirpath, "*.h", SearchOption.AllDirectories); string[] luafiles = Directory.GetFiles(dirpath, "*.lua", SearchOption.AllDirectories); string[] allfiles = cppfiles.Concat(hfiles).Concat(luafiles).ToArray(); // process each file foreach (var f in allfiles) { parse_file(f); } // sort items by file -> function _data.Items = _data.Items.OrderBy(x => x.Function).OrderBy(x => x.File).OrderBy(x => Path.GetExtension(x.File)).ToList(); // serialize data to xml var xsw = new XmlSerializer(typeof(Root)); using (var fs = new FileStream(outfile, FileMode.Create)) { xsw.Serialize(fs, _data); } // replace empty "" strings (for comfort further translation) var data = File.ReadAllText(outfile); data = data.Replace("", ""); File.WriteAllText(outfile, data); Console.WriteLine("\n{0} items saved in {1}: ", _data.Items.Count, outfile); //Console.WriteLine("\nPress any key to exit..."); //Console.ReadKey(); } /// /// Parse a single file /// /// private static void parse_file(string filepath) { string[] lines = File.ReadAllLines(filepath); string filename = Path.GetFileName(filepath); string text, f, function = string.Empty; int ln = 0; // line number int uid = 1; foreach (string l in lines) { ln++; try { if ((f = is_function(l)) != null) function = f; // remember last function if ((text = find_localize_text(l)) == null) continue; // ignore duplicate strings in the same file and function if (_data.Items.Find(x => x.Original == text && x.File == filename && x.Function == function) != null) continue; // find the original text in list for reference var reference = _data.Items.Find(x => x.Original == text && x.Translate.RefId == null); // set unique Id while (_data.Items.Find(x => x.Id == uid.ToString()) != null) { uid++; } _data.Items.Add(new Root.StringItem() { Id = uid.ToString(), File = filename, Function = function, Original = text, Translate = new Root.StringItem.TranslateItem() { // reference to exist translation RefId = (reference != null) ? reference.Id : null, } }); // insert example author if (_data.meta.Authors.Count == 0) _data.meta.Authors.Add(new Root.Meta.AuthorItem() { Name = "nomad" , Email = "nomad@example.com"}); Console.WriteLine("{0}, {1}(): {2}", filename, function, text); } catch (Exception e) { Console.WriteLine("Error on parse file \"{0}\" on line #{1}: {2}", filename, ln, l); Console.WriteLine(e.Message); } } } /// /// Return a text from the first string argument of the func_name /// /// /// text or null private static string find_localize_text(string line) { int func_pos, bracket1_pos, quote1_pos, quote2_pos; func_pos = bracket1_pos = quote1_pos = quote2_pos = -1; string text = null; for (int i = 0; i < line.Length; i++) { if (func_pos >= 0) { if (bracket1_pos > 0) { if (quote1_pos > 0) { if (quote2_pos > 0) { text = line.Substring(quote1_pos, quote2_pos - quote1_pos); break; } // 3) find last quote if (line.Substring(i, 1) == "\"" && line.Substring(i - 1, 1) != "\\") quote2_pos = i; continue; } // 3) find first quote if (line.Substring(i, 1) == "\"") quote1_pos = ++i; continue; } // 2) find first bracket if (line.Substring(i, 1) == "(") bracket1_pos = i; continue; } // 1) find function name if (line.Substring(i, (i + func_name.Length > line.Length) ? line.Length - i : func_name.Length) == func_name) func_pos = i; } return escape_text(text); } /// /// Filter text corresponding to XML rules /// /// /// text or null (if null passed) private static string escape_text(string text) { if (text == null) return null; text = text.Replace("\\\"", "\""); text = text.Replace("<", "<"); text = text.Replace(">", ">"); return text; } /// /// Is word a function? /// /// /// string or null private static string is_function(string line) { int j; line = line.Trim(); if (line.Length == 0) return null; // last line must have ) or { if (!line.Contains(";") && line[line.Length - 1] == ')' || line[line.Length - 1] == '{') { string[] words = line.Split(); if (words.Length > 0) { bool bad = false; // exclude small words and reserved words that can not be at the beginning of the function definition foreach (string r in reserved_words) if (r.Length > words[0].Length) continue; else if (words[0].Substring(0, r.Length) == r) bad = true; if (!bad) { // find function name in words for (int i = 0; i < words.Length; i++) { if (words[i].Trim() == string.Empty) continue; if (words[i][0] == '(') return words[i - 1]; else if ((j = words[i].IndexOf("(", 0)) != -1) return words[i].Substring(0, j); } } } } return null; } /// /// Ignore these words when find a function name /// static string[] reserved_words = new string[] { func_name, // function definition "LIST_TRAVERSE", "type", "while", "switch", "class", "new", "goto", "for", "sizeof", "struct", "throw", "try", "catch", "typedef", "enum", "if", "else", "{","}", "(",")", "<<",">>", "||","|", "&&","&", "//", "/", "*", // comments "!", }; } #region Serializer Class [XmlRoot("root")] public class Root { public Root() { Items = new List(); } [XmlElement("meta")] public Meta meta = new Meta(); [XmlArray("items"), XmlArrayItem(typeof(StringItem), ElementName = "item")] public List Items { get; set; } public class Meta { public Meta() { language = new LanguageItem(); Authors = new List(); } [XmlElement("language"), DefaultValue("change_me")] public LanguageItem language { get; set; } [XmlArray("authors"), XmlArrayItem(typeof(AuthorItem), ElementName = "author")] public List Authors { get; set; } public class LanguageItem { [XmlAttribute("tag")] public string Tag = "enUS"; [XmlText] public string InnerText = "English"; } public class AuthorItem { [XmlAttribute("name")] public string Name; [XmlAttribute("email")] public string Email; } } public class StringItem { [XmlAttribute("id")] public string Id; [XmlAttribute("file")] public string File; [XmlAttribute("function")] public string Function; [XmlElement("original")] public string Original; [XmlElement("translate")] public TranslateItem Translate; public class TranslateItem { [XmlAttribute("refid")] public string RefId; [XmlText] public string InnerText; } } #endregion } } pvpgn-1.99.7.2.1+dfsg/scripts/localize/pvpgn_localize_validator.cs000066400000000000000000000111501462640461400250770ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; using System.Xml.Serialization; namespace pvpgn_localize_validator { class Program { static void Main(string[] args) { if (args.Length == 0) { Console.WriteLine("This utility checks XML translation file for missing translations, arguments and illegal characters\n(c) 2014 HarpyWar (harpywar@gmail.com)"); Console.WriteLine("\nUsage: {0} [filename.xml]\n", AppDomain.CurrentDomain.FriendlyName); Environment.Exit(0); } process_file(args[0]); } static void process_file(string filename) { try { XDocument doc = XDocument.Load(filename, LoadOptions.SetLineInfo); // * no author name var authors = doc.Descendants("author"); foreach (var author in authors) { if (author.Attribute("email").Value == "nomad@example.com") logerror(author, "Please, remove or update example author."); } var items = doc.Descendants("item"); XElement original, translate; XAttribute id; foreach (var item in items) { original = item.Element("original"); translate = item.Element("translate"); // * check missing references if ((id = translate.Attribute("refid")) != null) { if (items.Where(x => x.Attribute("id").Value == id.Value).Count() == 0) logerror(translate, string.Format("string id=\"{0}\" is not found", id.Value)); } else check_string(original, translate); } } catch (Exception e) { Console.WriteLine("[ERROR] " + e.Message); } } static void check_string(XElement original, XElement translate) { string a = original.Value; string b = translate.Value; if (a.Trim().Length == 0) { logerror(translate, "empty original string"); return; } if (b.Trim().Length == 0) { logerror(translate, "empty translation"); return; } // * illegal characters if (!validate_symbols(a) || !validate_symbols(b)) logerror(translate, string.Format("string contains one of invalid characters: {0}", string.Join(", ", invalid_symbols))); // * missing arguments if (a.Count(f => f == '{') != b.Count(f => f == '{') && a.Count(f => f == '}') != b.Count(f => f == '}')) logerror(translate, "missing argument in translation?"); // * reduntant or missing spaces at start if (get_pos(a, ' ') != get_pos(b, ' ')) logerror(translate, "spaces count at the beginning of the translation is not equal with original"); // * reduntant or missing spaces at the end if (get_pos(a, ' ', true) != get_pos(b, ' ', true)) logerror(translate, "spaces count at the end of the translation is not equal with original"); } static int get_pos(string str, char c, bool reverse = false) { int pos = 0; if (!reverse) for (int i = 0; i < str.Length; i++) { if (str[i] == c) pos++; else break; } else for (int i = str.Length-1; i > 0; i--) { if (str[i] == c) pos++; else break; } return pos; } static string[] invalid_symbols = new string[] { ">", "<" }; static bool validate_symbols(string str) { foreach(var s in invalid_symbols) if (str.Contains(s)) return false; return true; } static void logerror(XElement el, string text) { IXmlLineInfo info = el; int lineNumber = info.LineNumber; Console.WriteLine("[WARNING] line {0}: {1}", lineNumber, text); } } } pvpgn-1.99.7.2.1+dfsg/scripts/localize/update.bat000066400000000000000000000024001462640461400214370ustar00rootroot00000000000000@echo off pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\common.xml if exist ..\..\conf\i18n\deDE\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\deDE\common.xml if exist ..\..\conf\i18n\csCZ\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\csCZ\common.xml if exist ..\..\conf\i18n\esES\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\esES\common.xml if exist ..\..\conf\i18n\frFR\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\frFR\common.xml if exist ..\..\conf\i18n\itIT\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\itIT\common.xml if exist ..\..\conf\i18n\jaJA\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\jaJA\common.xml if exist ..\..\conf\i18n\koKR\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\koKR\common.xml if exist ..\..\conf\i18n\plPL\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\plPL\common.xml if exist ..\..\conf\i18n\ruRU\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\ruRU\common.xml if exist ..\..\conf\i18n\zhCN\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\zhCN\common.xml if exist ..\..\conf\i18n\zhTW\common.xml pvpgn_localize_generator.exe ..\..\ ..\..\conf\i18n\zhTW\common.xml pausepvpgn-1.99.7.2.1+dfsg/scripts/localize/validate.bat000066400000000000000000000021631462640461400217540ustar00rootroot00000000000000@echo off if exist ..\..\conf\i18n\deDE\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\deDE\common.xml if exist ..\..\conf\i18n\csCZ\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\csCZ\common.xml if exist ..\..\conf\i18n\esES\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\esES\common.xml if exist ..\..\conf\i18n\frFR\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\frFR\common.xml if exist ..\..\conf\i18n\itIT\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\itIT\common.xml if exist ..\..\conf\i18n\jaJA\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\jaJA\common.xml if exist ..\..\conf\i18n\koKR\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\koKR\common.xml if exist ..\..\conf\i18n\plPL\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\plPL\common.xml if exist ..\..\conf\i18n\ruRU\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\ruRU\common.xml if exist ..\..\conf\i18n\zhCN\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\zhCN\common.xml if exist ..\..\conf\i18n\zhTW\common.xml pvpgn_localize_validator.exe ..\..\conf\i18n\zhTW\common.xml pausepvpgn-1.99.7.2.1+dfsg/scripts/login_testusers.sh000066400000000000000000000016241462640461400214570ustar00rootroot00000000000000#!/bin/sh # tries to login with a massive number of test accounts # See make_testusers.sh for a way to create these accounts. # number of accounts to connect with numaccts=400 # "prefix" of account names name="bob" # account password pass="bob" # delay between printing messages delay=30 # where to connect server=localhost # number of zero-padded columns in suffix padding=6 # how many seconds until logout total=600 num=0 while [ "${num}" -lt "${numaccts}" ]; do num="`expr \"${num}\" '+' '1'`" form="`printf \"%0${padding}d\" \"${num}\"`" ( echo '' echo "${name}${form}" echo "${pass}" total=0; while [ "${total}" -lt "${maxtime}" ]; do sleep "${delay}" echo "My name is ${name}${form}" total="`expr \"${total}\" '+' \"${delay}\"`" done echo "/quit" ) | telnet "${server}" 6112 & done exit 0 pvpgn-1.99.7.2.1+dfsg/scripts/make_testusers.sh000066400000000000000000000013521462640461400212620ustar00rootroot00000000000000#!/bin/sh # creates massive numbers of dummy "test" accounts # number of accounts to connect with numaccts=400 # "prefix" of account names name="bob" # account password pass="bob" # number of zero-padded columns in suffix padding=6 # "users" directory users=/usr/local/bnetd/var/users # bnpass command bnpass=/usr/local/bnetd/bin/bnpass hash="`echo \"${pass}\" | \"${bnpass}\" | sed -e 's/^.*"\([0-9a-f]*\)"/\1/'`" num=0 while [ "${num}" -lt 400 ]; do num="`expr \"${num}\" '+' '1'`" form="`printf \"%06d\" \"${num}\"`" ( echo '"BNET\\acct\\username"="'"${name}${form}"'"' echo '"BNET\\acct\\passhash1"="'"${hash}"'"' echo '"BNET\\acct\\userid"="'"${num}"'"' ) > "${users}/${form}" done exit 0 pvpgn-1.99.7.2.1+dfsg/scripts/process.pl000066400000000000000000000176641462640461400177200ustar00rootroot00000000000000#!/usr/bin/perl -w #!/usr/local/bin/perl -w #!/opt/perl/bin/perl -w # # Copyright (C) 1999 Mark Baysinger (mbaysing@ucsd.edu) # Copyright (C) 1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) # Copyright (C) 2001 Roland Haeder (webmaster@ai-project-now.de) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Version of this script $version = "v1.05"; # modify to use correct locations $infile = "./bnetdlist.txt"; #$infile = "/var/tmp/bnetdlist.txt"; $outfile = "./bnetdlist.html"; #$outfile = "/home/httpd/html/bnetdlist.html"; $tmpfile = $outfile . ".temp"; # document look $page_bg_color = "#202020"; $link_color = "#5070FF"; $alink_color = "#FF3030"; $vlink_color = "#8090FF"; $tbl_border_color = "#404040"; $tbl_border_ramp = "0"; $tbl_border_width = "2"; $tbl_padding = "1"; $tbl_head_bg_color = "$tbl_border_color"; $tbl_bg_color = "#000000"; $tbl_text_color = "#80A0FF"; $title_bg_color = "$page_bg_color"; $title_text_color = "#FFFFFF"; $sum_bg_color = "$page_bg_color"; $sum_text_color = "$title_text_color"; # header strings $title = "BNETD Server Status"; $header = "
$title
"; open(INFILE, "<$infile") || die("Can't open \"$infile\" for reading: $!\n"); $/ = "\#\#\#\n"; @servers = ; chomp @servers; close INFILE; open(OUTFILE, ">$tmpfile") || die("Can't open \"$tmpfile\" for writing: $!\n"); print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE " \n"; print OUTFILE " $title\n"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE "
$header
\n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE "
\n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; $allservers = 0; $allusers = 0; $allgames = 0; foreach $server (@servers) { print OUTFILE " \n"; $/ = "\#\#\n"; # Fix-up for special characters $server =~ s/&/&/g; $server =~ s//>/g; @serverinfo = split /^\#\#\n/m, $server; chomp @serverinfo; chop @serverinfo; # put fields into descriptive variable names $addr = $serverinfo[0]; $loc = $serverinfo[2]; if (length($loc)<1) { $loc = " "; } $desc = $serverinfo[8]; if (length($desc)<1) { $desc = " "; } $soft = $serverinfo[3] . " " . $serverinfo[4]; $osys = $serverinfo[9]; if (length($osys)<1) { $osys = "unknown"; } $url = $serverinfo[10]; $contact = $serverinfo[11]; if (length($contact)<1) { $contact = " "; } $email = $serverinfo[12]; $currusers = $serverinfo[5]; $totusers = $serverinfo[15]; $currchans = $serverinfo[6]; $currgames = $serverinfo[7]; $totgames = $serverinfo[14]; $uptime = sprintf("%02d:%02d:%02d",int $serverinfo[13]/3600, int ($serverinfo[13]%3600)/60, int $serverinfo[13]%60); # print it out print OUTFILE " \n"; # IP print OUTFILE " \n"; print OUTFILE " \n"; if (length($url)<1) { print OUTFILE " \n"; } else { print OUTFILE " \n"; } print OUTFILE " \n"; if (length($email)<1) { print OUTFILE " \n"; } else { print OUTFILE " \n"; } print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; $allservers++; $allusers+=${currusers}; $allgames+=${currgames}; } print OUTFILE "
AddressLocationDescriptionURLUptimeContactSoftwareUCG
$addr$loc$desc $url$uptime$contact$contact ($email)$soft
$osys
${currusers}c
${totusers}t
${currchans}${currgames}c
${totgames}t
\n"; print OUTFILE "
\n"; print OUTFILE "

\n"; print OUTFILE "
\n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE " \n"; print OUTFILE "
\n"; print OUTFILE " \n"; print OUTFILE " servers: " . $allservers . "
\n"; print OUTFILE " users: " . $allusers . "
\n"; print OUTFILE " games: " . $allgames . "
\n"; print OUTFILE "
\n"; print OUTFILE " Last updated " . gmtime(time) . " GMT
\n"; print OUTFILE "

\n"; print OUTFILE "
\n"; print OUTFILE "
\n"; print OUTFILE "\n"; close(OUTFILE); unlink($outfile); rename($tmpfile, $outfile); exit(0); pvpgn-1.99.7.2.1+dfsg/scripts/pvpgn_hash.inc.php000066400000000000000000000060611462640461400213100ustar00rootroot00000000000000> 6) + 1; for($i=0; $i < $nblk * 16; $i++) $blks[$i] = 0; for($i=0; $i < strlen($str); $i++) { $blks[$i >> 2] |= ord(substr($str, $i, 1)) << ( ($i % 4) * 8); } return $blks; } function safe_add($x, $y) { $lsw = ($x & 0xFFFF) + ($y & 0xFFFF); $msw = ($x >> 16) + ($y >> 16) + ($lsw >> 16); return ($msw << 16) | ($lsw & 0xFFFF); } function safe_not( $num ) { $lsw = (~($num & 0xFFFF)) & 0xFFFF; $msw = (~($num >> 16)) & 0xFFFF; return ($msw << 16) | $lsw; } function safe_rol( $num, $amt ) { $leftmask = 0xffff | (0xffff << 16); $leftmask <<= 32 - $amt; $rightmask = 0xffff | (0xffff << 16); $rightmask <<= $amt; $rightmask = safe_not($rightmask); $remains = $num & $leftmask; $remains >>= 32 - $amt; $remains &= $rightmask; $res = ($num << $amt) | $remains; return $res; } function ft($t, $b, $c, $d) { if($t < 20) return ($b & $c) | ((safe_not($b)) & $d); if($t < 40) return $d ^ $c ^ $b; if($t < 60) return ($c & $b) | ($d & $c) | ($d & $b); return $d ^ $c ^ $b; } function kt($t) { if ($t < 20) { return 0x5a82 << 16 | 0x7999; } else if ($t < 40) { return 0x6ed9 << 16 | 0xeba1; } else if ($t < 60) { return 0x8f1b << 16 | 0xbcdc; } else { return 0xca62 << 16 | 0xc1d6; } } function pvpgn_hash($str) { $x = str2blks_pvpgn($str); $a = 0x6745 << 16 | 0x2301; $b = 0xefcd << 16 | 0xab89; $c = 0x98ba << 16 | 0xdcfe; $d = 0x1032 << 16 | 0x5476; $e = 0xc3d2 << 16 | 0xe1f0; for($i = 0; $i < sizeof($x); $i += 16) { $olda = $a; $oldb = $b; $oldc = $c; $oldd = $d; $olde = $e; for($j = 0; $j < 16; $j++) { $w[$j] = $x[$i+$j]; } for($j = 0; $j < 64; $j++) { $ww = $w[$j] ^ $w[$j+8] ^ $w[$j+2] ^ $w[$j+13]; $w[$j+16] = 1 << ($ww%32); } for($j = 0; $j < 80; $j++) { if ($j<20) { $t = safe_add(safe_add(safe_rol($a, 5), ft($j, $b, $c, $d)), safe_add(safe_add($e, $w[$j]), kt($j))); } else { $t = safe_add(safe_add(safe_rol($t, 5), ft($j, $b, $c, $d)), safe_add(safe_add($e, $w[$j]), kt($j))); } $e = $d; $d = $c; $c = safe_rol($b, 30); $b = $a; $a = $t; } $a = safe_add($t, $olda); $b = safe_add($b, $oldb); $c = safe_add($c, $oldc); $d = safe_add($d, $oldd); $e = safe_add($e, $olde); } return sprintf("%08x%08x%08x%08x%08x",$a,$b,$c,$d,$e); } ?>pvpgn-1.99.7.2.1+dfsg/scripts/pvpgn_wol_hash.inc.php000066400000000000000000000021011462640461400221600ustar00rootroot000000000000008) { return false; } $pwd1=$pass; $esi=8; $pwd2=array(null,null,null,null,null,null,null,null); $p1=0; $p2=0; $edx=""; for($i=0;$i<8;$i++) { if(ord($pwd1[$p1]) & 1) { $edx = ord($pwd1[$p1]) << 1; $edx &= ord($pwd1[$esi]); } else { $edx = ord($pwd1[$p1]) ^ ord($pwd1[$esi]); } $pwd2[$p2] = $edx; $p2++; $esi--; $p1++; } $WOL_HASH_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; $p1 = 0; $p2 = 0; for($i=0;$i<8;$i++) { $edx = $pwd2[$p2] & 0x3f; $p2++; $pwd1[$p1] = $WOL_HASH_CHAR[$edx]; $p1++; } return $pwd1; } ?>pvpgn-1.99.7.2.1+dfsg/scripts/rc.bnetd000066400000000000000000000025251462640461400173150ustar00rootroot00000000000000#!/bin/sh # # /etc/rc.d/rc.bnetd # # Copyright (C) 2003 rakware@go.ro # BSD style RC startup script # start/stop/restart the pvpgn Battle.net emulator. # # to make bnetd start automatically at boot, make this # file executable: chmod +x /etc/rc.d/rc.bnetd # also make sure that your rc.local script executes it # # 30.10.2003 # added sleep 3 to bnetd_up(down) so pidof can do # the work right bnetd_up() { sleep 3; if `echo `pidof bnetd`` >>/dev/null; then echo "OK" else echo "FAILED" fi } bnetd_down() { sleep 3; if `echo `pidof bnetd`` >>/dev/null; then echo "FAILED" else echo "OK" fi } bnetd_start() { if [ -x /usr/local/sbin/bnetd -a -r /usr/local/bnetd/etc/bnetd.conf ]; then /usr/local/sbin/bnetd echo -n "starting pvpgn... : "; bnetd_up fi } bnetd_stop() { killall -9 bnetd echo -n "killing pvpgn... : "; bnetd_down } bnetd_restart() { bnetd_stop sleep 3 bnetd_start } bnetd_status() { if `echo `pidof bnetd`` >>/dev/null; then echo "pvpgn is UP and running" else echo "pvpgn is NOT running" fi } case "$1" in 'start') bnetd_start ;; 'stop') bnetd_stop ;; 'restart') bnetd_restart ;; 'status') bnetd_status ;; *) # Default is "start", for backwards compatibility with previous # Slackware versions. This may change to a 'usage' error someday. bnetd_start esac pvpgn-1.99.7.2.1+dfsg/scripts/reform.awk000066400000000000000000000051401462640461400176650ustar00rootroot00000000000000#!/usr/bin/awk -f # This file converts tcpdump output into a hexdump with # ASCII strings on the right. It replaces packet headers # with "*"s to reduce visual noise. # # Use it like: # tcpdump -s 2000 -x [] | reform.awk # To save a dump: # tcpdump -s 2000 -w dumpfile # To replay it: # tcpdump -x -s 2000 -r dumpfile | reform.awk function hexvalue(nybble) { if (nybble=="0") { return 0; } if (nybble=="1") { return 1; } if (nybble=="2") { return 2; } if (nybble=="3") { return 3; } if (nybble=="4") { return 4; } if (nybble=="5") { return 5; } if (nybble=="6") { return 6; } if (nybble=="7") { return 7; } if (nybble=="8") { return 8; } if (nybble=="9") { return 9; } if (nybble=="a") { return 10; } if (nybble=="b") { return 11; } if (nybble=="c") { return 12; } if (nybble=="d") { return 13; } if (nybble=="e") { return 14; } if (nybble=="f") { return 15; } } # ab67 483d cac9 08ca 3efb 35f8 9406 36e8 { if (substr($0,1,1)=="\t") { printf(" "); str = ""; for (i=1; i<=NF; i++) { nybble1 = substr($(i),1,1); nybble2 = substr($(i),2,1); byte1 = 16*hexvalue(nybble1)+hexvalue(nybble2); if (headerleft) { headerleft--; printf("** "); str = str " "; } else { printf("%02X ",byte1); if (byte1<32 || byte1>126) { str = str "."; } else { str = str sprintf("%c",0+byte1); } } nybble3 = substr($(i),3,1); nybble4 = substr($(i),4,1); if (nybble3=="" && nybble4=="") { printf(" "); } else { byte2 = 16*hexvalue(nybble3)+hexvalue(nybble4); if (headerleft) { headerleft--; printf("** "); str = str " "; } else { printf("%02X ",byte2); if (byte2<32 || byte2>126) { str = str "."; } else { str = str sprintf("%c",0+byte2); } } } if (i==4) { printf(" "); } } for (; i<=8; i++) { if (i==4) { printf(" "); } printf(" "); } printf(" %s\n",str); } else { printf("%s\n",$0); headerleft = 40; } } pvpgn-1.99.7.2.1+dfsg/scripts/sql/000077500000000000000000000000001462640461400164665ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/scripts/sql/repairlevels.pl000066400000000000000000000045351462640461400215270ustar00rootroot00000000000000#!/usr/bin/perl $| = 1; use DBI; &header; if (scalar(@ARGV) < 5) { &usage; exit; } $levelxpfile = shift; $dbhost = shift; $dbname = shift; $dbuser = shift; $dbpass = shift; open LEVELFD, $levelxpfile or die "Could not open XP level file ($levelxpfile)"; print "Reading from XP file ...\n"; while($line = ) { $line =~ s/#.*//; if ($line !~ m/^\s*$/) { ($level, $startxp, $neededxp, $lossfactor, $mingames) = split(/\s+/,$line); print "level: $level startxp: $startxp neededxp: $neededxp lossfactor: $lossfactor mingames: $mingames\n"; $xplevel[$level]->{startxp} = $startxp; $xplevel[$level]->{neededxp} = $neededxp; $xplevel[$level]->{lossfactor} = $lossfactor; $xplevel[$level]->{mingames} = $mingames; } } close LEVELFD; print "...done\n\nConnecting to db..."; $dbh = DBI->connect("dbi:mysql:$dbname;host=$dbhost", $dbuser, $dbpass) or die "Could not connect to db (mysql, db: $dbname, host: $dbhost, user: $dbuser, pass: $dbpass)"; $sth = $dbh->prepare("SELECT uid, solo_xp, solo_level FROM Record WHERE uid > 0") or die "Error preparing query!"; $sth->execute() or die "Error executing query!"; while($row = $sth->fetchrow_hashref) { $level = $row->{solo_level}; $xp = $row->{solo_xp}; if ($level eq "" or $xp eq "" or $level < 1) { next; } for($i = $level; $i < 50 ; $i++) { if ($xplevel[$i]->{startxp} > $xp) { $i--; last; } } $level = $i; for($i = $level; $i > 1; $i++) { if ($xplevel[$i]->{needed_xp} <= $xp) { last; } } $level = $i; if ($level < 1) {$level = 1;} if ($level != $row->{solo_level}) { print "Repaired $uid from (level: " , $row->{solo_level}, " xp: $xp) to (level: $level xp: $xp)\n"; $dbh->do("UPDATE profile SET solo_level = '$level' WHERE uid = '".$row->{uid}."'"); } } $sth->finish(); $dbh->disconnect(); sub header { print "Account db accounts XP levels repair tool.\n"; print " Copyright (C) 2002 Dizzy (dizzy\@roedu.net)\n"; print " People Versus People Gaming Network (www.pvpgn.org)\n\n"; } sub usage { print "Usage:\n\nrepairlevels.pl \n\n"; print " \t: path/name of the file with the XP level data (ex. bnxplevel.txt)\n"; print " \t: the database host\n"; print " \t: the database name\n"; print " \t: the database user\n"; print " \t: the database password\n"; } pvpgn-1.99.7.2.1+dfsg/scripts/storage/000077500000000000000000000000001462640461400173335ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/scripts/storage/cdb2sql.pl000066400000000000000000000061041462640461400212230ustar00rootroot00000000000000#!/usr/bin/perl $bncdb = "bncdb"; if (scalar(@ARGV) != 1) { &usage; exit; } &header; # remove a trailing / $dirpath = $ARGV[0]; $dirpath =~ s!(.*)/$!$1!; opendir FILEDIR, $dirpath or die "Error opening filedir!\n"; while ($filename = readdir FILEDIR) { if ($filename =~ m/^\./) { next; } #ignore . and .. if ( ! (($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat "$dirpath/$filename" )) { print STDERR "Error stat-ing the file $pathdir/$filename!\n" ; next; } $type = ($mode & 070000) >> 12; if ($type != 0) { print STDERR "File $dirpath/$filename its not regular\n"; next; } convert_cdb2sql("$dirpath/$filename"); } closedir FILEDIR; sub convert_cdb2sql { my $filen = shift; my ($userid, $count, $alist); $pid = open(FILE, "$bncdb -d \"".$filen."\" |"); print STDERR "Converting file $filen ... "; $count = 0; $userid = ""; undef @alist; while($line = ) { if ($line =~ m/:.*->.*/) { $line =~ m/^\+([0-9]+),([0-9]+):([A-Za-z0-9]+)\\(.*)$/; # key length and value length $keylen = $1; $vallen = $2; $tab = $3; $tab =~ tr/A-Z/a-z/; $str = $4; # skip Team as we now have another team structure if ($tab =~ m/^team$/i) { print STDERR "WARNING: skipping Team information!\n"; next; } elsif ($tab =~ m/^\s*$/) { print STDERR "WARNING: skipping empty tab!\n"; next; } $alist[$count]{tab} = $tab; $alist[$count]{col} = &escape_key(substr($str, 0, $keylen - length($tab) - 1)); $alist[$count]{val} = substr($str, $keylen - length($tab) + 1); if ($alist[$count]{col} =~ m!userid$!) { $userid = $alist[$count]{val}; } $count++; } } if ($userid ne "") { &db_set($dbh, $userid, $alist); } close FILE; waitpid $pid, 0; print STDERR "done\n"; } sub header { print STDERR "Account files to db accounts converting tool.\n"; print STDERR " Copyright (C) 2002,2005 Dizzy (dizzy\@roedu.net)\n"; print STDERR " Player Versus Player Gaming Network (www.pvpgn.org)\n\n"; } sub usage { &header; print STDERR "Usage:\n\n\tcdb2sql.pl \n\n"; print STDERR "\t \t: directory with the account files\n"; } sub db_set { my $dbh = shift; my $userid = shift; my $alist = shift; my ($i); print("INSERT INTO BNET (uid) VALUES ($userid);\n"); print("INSERT INTO profile (uid) VALUES ($userid);\n"); print("INSERT INTO Record (uid) VALUES ($userid);\n"); print("INSERT INTO friend (uid) VALUES ($userid);\n"); for($i=0; $i> 12; if ($type != 0) { print STDERR "File $dirpath/$filename its not regular\n"; next; } convertplain2db("$dirpath/$filename"); } closedir FILEDIR; sub convertplain2db { my $filen = shift; my ($userid, $count, $alist); open FILE, $filen or die "Error opening file $filen\n"; print STDERR "Converting file $filen ... "; $count = 0; $userid = ""; undef @alist; while($line = ) { if ($line =~ m/".*"=".*"/) { $line =~ m/^"([A-Za-z0-9]+)\\\\(.*)"="(.*)"/; $tab = $1; # skip Team as we now have another team structure if ($tab =~ m/^team$/i) { print STDERR "WARNING: skipping Team information!\n"; next; } elsif ($tab =~ m/^\s*$/) { print STDERR "WARNING: skipping empty tab!\n"; next; } $alist[$count]{tab} = $tab; $alist[$count]{col} = &escape_key($2); $alist[$count]{val} = $3; $alist[$count]{col} =~ s!\\\\!_!g; if ($alist[$count]{col} =~ m!userid$!) { $userid = $alist[$count]{val}; } $count++; } } if ($userid ne "") { &db_set($dbh, $userid, $alist); } close FILE; print STDERR "done\n"; } sub header { print STDERR "Account files to db accounts converting tool.\n"; print STDERR " Copyright (C) 2002,2005 Dizzy (dizzy\@roedu.net)\n"; print STDERR " Player Versus Player Gaming Network (www.pvpgn.org)\n\n"; } sub usage { &header; print STDERR "Usage:\n\n\tfiles2db.pl \n\n"; print STDERR "\t \t: directory with the account files\n"; } sub db_set { my $dbh = shift; my $userid = shift; my $alist = shift; my ($i); print("INSERT INTO BNET (uid) VALUES ($userid);\n"); print("INSERT INTO profile (uid) VALUES ($userid);\n"); print("INSERT INTO Record (uid) VALUES ($userid);\n"); print("INSERT INTO friend (uid) VALUES ($userid);\n"); for($i=0; $i #include #include #include #include "compat/strdup.h" #include "compat/strcasecmp.h" #include "compat/strncasecmp.h" #include "compat/pdir.h" #include "common/list.h" #include "common/elist.h" #include "common/eventlog.h" #include "common/util.h" #include "common/field_sizes.h" #include "common/bnethash.h" #include "common/introtate.h" #include "prefs.h" #include "account_wrap.h" #include "common/hashtable.h" #include "connection.h" #include "watch.h" #include "friends.h" #include "team.h" #include "common/tag.h" #include "ladder.h" #include "clan.h" #include "server.h" #include "attrgroup.h" #include "attrlayer.h" #include "storage.h" #include "common/flags.h" #include "common/xalloc.h" #include "common/xstring.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_hashtable * accountlist_head = NULL; static t_hashtable * accountlist_uid_head = NULL; unsigned int maxuserid = 0; /* This is to force the creation of all accounts when we initially load the accountlist. */ static int force_account_add = 0; static unsigned int account_hash(char const * username); static t_account * account_load(t_attrgroup *); static int account_load_friends(t_account * account); static int account_unload_friends(t_account * account); static void account_destroy(t_account * account); static t_account * accountlist_add_account(t_account * account); static unsigned int account_hash(char const *username) { register unsigned int h; register unsigned int len = std::strlen(username); int c; for (h = 5381; len > 0; --len, ++username) { h += h << 5; c = (int)*username; // FIXME: (HarpyWar) I add this condition because if we call connlist_find_connection_by_accountname // with wrong account name, then it fails on std::isupper(c) if (c < -1 || c > 255) break; if (std::isupper(c) == 0) h ^= *username; else h ^= std::tolower(c); } return h; } static t_account * account_create(char const * username, char const * passhash1) { t_account * account; if (username && !passhash1) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL passhash1"); return NULL; } if (username && account_check_name(username)) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid chars in username"); return NULL; } account = (t_account*)xmalloc(sizeof(t_account)); account->name = NULL; account->clanmember = NULL; account->attrgroup = NULL; account->friends = NULL; account->teams = NULL; account->conn = NULL; FLAG_ZERO(&account->flags); account->namehash = 0; /* hash it later before inserting */ account->uid = 0; /* hash it later before inserting */ if (username) { /* actually making a new account */ /* first check if such a username already owns an account. * we search in the memory hash mainly for non-indexed storage types. * indexed storage types check themselves if the username exists already * in the storage (see storage_sql.c) */ if (accountlist_find_account(username)) { eventlog(eventlog_level_debug, __FUNCTION__, "user \"{}\" already has an account", username); goto err; } account->attrgroup = attrgroup_create_newuser(username); if (!account->attrgroup) { eventlog(eventlog_level_error, __FUNCTION__, "failed to add user"); goto err; } account->name = xstrdup(username); if (account_set_strattr(account, "BNET\\acct\\username", username) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not set username"); goto err; } if (account_set_numattr(account, "BNET\\acct\\userid", maxuserid + 1) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not set userid"); goto err; } if (account_set_strattr(account, "BNET\\acct\\passhash1", passhash1) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not set passhash1"); goto err; } if (account_set_numattr(account, "BNET\\acct\\ctime", (unsigned int)now)) { eventlog(eventlog_level_error, __FUNCTION__, "could not set ctime"); goto err; } } return account; err: account_destroy(account); return NULL; } static void account_destroy(t_account * account) { assert(account); friendlist_close(account->friends); teams_destroy(account->teams); if (account->attrgroup) attrgroup_destroy(account->attrgroup); if (account->name) xfree(account->name); xfree(account); } extern unsigned int account_get_uid_real(t_account const * account, char const * fn, unsigned int ln) { if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account (from {}:{})", fn, ln); return 0; } return account->uid; } extern int account_match(t_account * account, char const * username) { unsigned int userid = 0; unsigned int namehash; char const * tname; if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!username) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL username"); return -1; } if (username[0] == '#') if (str_to_uint(&username[1], &userid) < 0) userid = 0; if (userid) { if (account->uid == userid) return 1; } else { namehash = account_hash(username); if (account->namehash == namehash && (tname = account_get_name(account))) { if (strcasecmp(tname, username) == 0) return 1; } } return 0; } extern int account_save(t_account * account, unsigned flags) { assert(account); return attrgroup_save(account->attrgroup, flags); } extern int account_flush(t_account * account, unsigned flags) { int res; assert(account); res = attrgroup_flush(account->attrgroup, flags); if (res < 0) return res; account_unload_friends(account); return res; } extern char const * account_get_strattr_real(t_account * account, char const * key, char const * fn, unsigned int ln) { if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account (from {}:{})", fn, ln); return NULL; } if (!key) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key (from {}:{})", fn, ln); return NULL; } return attrgroup_get_attr(account->attrgroup, key); } extern int account_set_strattr(t_account * account, char const * key, char const * val) { if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return 0; } if (!key) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return -1; } return attrgroup_set_attr(account->attrgroup, key, val); } static t_account * account_load(t_attrgroup *attrgroup) { t_account * account; assert(attrgroup); if (!(account = account_create(NULL, NULL))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create account"); return NULL; } account->attrgroup = attrgroup; return account; } static t_account * account_load_new(char const * name, unsigned uid) { t_account *account; t_attrgroup *attrgroup; if (name && account_check_name(name)) return NULL; force_account_add = 1; /* disable the protection */ attrgroup = attrgroup_create_nameuid(name, uid); if (!attrgroup) { force_account_add = 0; return NULL; } if (!(account = account_load(attrgroup))) { eventlog(eventlog_level_error, __FUNCTION__, "could not load account"); attrgroup_destroy(attrgroup); force_account_add = 0; return NULL; } if (!accountlist_add_account(account)) { eventlog(eventlog_level_error, __FUNCTION__, "could not add account to list"); account_destroy(account); force_account_add = 0; return NULL; } force_account_add = 0; return account; } static int _cb_read_accounts(t_attrgroup *attrgroup, void *data) { unsigned int *count = (unsigned int *)data; t_account *account; if (!(account = account_load(attrgroup))) { eventlog(eventlog_level_error, __FUNCTION__, "could not load account"); attrgroup_destroy(attrgroup); return -1; } if (!accountlist_add_account(account)) { eventlog(eventlog_level_error, __FUNCTION__, "could not add account to list"); account_destroy(account); return -1; } /* might as well free up the memory since we probably won't need it */ account_flush(account, FS_FORCE); /* force unload */ (*count)++; return 0; } extern int accountlist_load_all(int flag) { static bool loaded = false; // all accounts already loaded ? if (loaded) { return 0; } auto t0 = std::chrono::steady_clock::now(); unsigned int count = 0; force_account_add = 1; // disable the protection int ret = attrgroup_read_accounts(flag, _cb_read_accounts, &count); force_account_add = 0; // enable the protection auto t1 = std::chrono::steady_clock::now(); if (ret == 0) { loaded = true; eventlog(eventlog_level_info, __FUNCTION__, "Successfully loaded {} user accounts in {} milliseconds", count, std::chrono::duration_cast(t1 - t0).count()); return 0; } else if (ret == -1) { eventlog(eventlog_level_error, __FUNCTION__, "got error reading user accounts"); return -1; } else { return 0; } } extern int accountlist_create(void) { eventlog(eventlog_level_info, __FUNCTION__, "started creating accountlist"); if (!(accountlist_head = hashtable_create(prefs_get_hashtable_size()))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create accountlist_head"); return -1; } if (!(accountlist_uid_head = hashtable_create(prefs_get_hashtable_size()))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create accountlist_uid_head"); return -1; } /* load accounts without force, indexed storage types wont be loading */ accountlist_load_all(ST_NONE); maxuserid = storage->read_maxuserid(); return 0; } extern int accountlist_destroy(void) { t_entry * curr; t_account * account; HASHTABLE_TRAVERSE(accountlist_head, curr) { if (!(account = (t_account*)entry_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL account in list"); else { if (account_flush(account, FS_FORCE) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not save account"); account_destroy(account); } hashtable_remove_entry(accountlist_head, curr); } HASHTABLE_TRAVERSE(accountlist_uid_head, curr) { hashtable_remove_entry(accountlist_head, curr); } if (hashtable_destroy(accountlist_head) < 0) return -1; accountlist_head = NULL; if (hashtable_destroy(accountlist_uid_head) < 0) return -1; accountlist_uid_head = NULL; return 0; } extern t_hashtable * accountlist(void) { return accountlist_head; } extern t_hashtable * accountlist_uid(void) { return accountlist_uid_head; } extern unsigned int accountlist_get_length(void) { return hashtable_get_length(accountlist_head); } extern int accountlist_save(unsigned flags) { return attrlayer_save(flags); } extern int accountlist_flush(unsigned flags) { return attrlayer_flush(flags); } extern t_account * accountlist_find_account(char const * username) { unsigned int userid = 0; t_entry * curr; t_account * account; if (!username) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL username"); return NULL; } if (username[0] == '#') { if (str_to_uint(&username[1], &userid) < 0) userid = 0; } else if (!(prefs_get_savebyname())) if (str_to_uint(username, &userid) < 0) userid = 0; /* all accounts in list must be hashed already, no need to check */ if (userid) { account = accountlist_find_account_by_uid(userid); if (account) return account; } if ((!(userid)) || (userid && ((username[0] == '#') || (std::isdigit((int)username[0]))))) { unsigned int namehash; char const * tname; namehash = account_hash(username); HASHTABLE_TRAVERSE_MATCHING(accountlist_head, curr, namehash) { account = (t_account*)entry_get_data(curr); if ((tname = account_get_name(account))) { if (strcasecmp(tname, username) == 0) { hashtable_entry_release(curr); return account; } } } } return account_load_new(username, 0); } extern t_account * accountlist_find_account_by_uid(unsigned int uid) { t_entry * curr; t_account * account; if (uid) { HASHTABLE_TRAVERSE_MATCHING(accountlist_uid_head, curr, uid) { account = (t_account*)entry_get_data(curr); if (account->uid == uid) { hashtable_entry_release(curr); return account; } } } return account_load_new(NULL, uid); } extern char const *accountlist_find_vague_account(t_account * account, char const *vague_username) { char const *tname; if (!vague_username) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL vague_username"); return NULL; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } if (tname = account_get_name(account)) { if (find_substr((char*)tname, vague_username)) { return tname; } return NULL; } return NULL; } extern int accountlist_allow_add(void) { if (force_account_add) return 1; /* the permission was forced */ if (prefs_get_max_accounts() == 0) return 1; /* allow infinite accounts */ if (prefs_get_max_accounts() <= hashtable_get_length(accountlist_head)) return 0; /* maximum account limit reached */ return 1; /* otherwise let them proceed */ } static t_account * accountlist_add_account(t_account * account) { unsigned int uid; char const * username; if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } username = account_get_name(account); uid = account_get_numattr(account, "BNET\\acct\\userid"); if (!username || std::strlen(username) < 1) { eventlog(eventlog_level_error, __FUNCTION__, "got bad account (empty username)"); return NULL; } if (uid < 1) { ERROR2("got bad account (bad uid: {}) for \"{}\", fix it!", uid, username); return NULL; } /* check whether the account limit was reached */ if (!accountlist_allow_add()) { eventlog(eventlog_level_warn, __FUNCTION__, "account limit reached (current is {}, storing {})", prefs_get_max_accounts(), hashtable_get_length(accountlist_head)); return NULL; } /* delayed hash, do it before inserting account into the list */ account->namehash = account_hash(username); account->uid = uid; /* FIXME: this check actually (with the new attr system) happens too late * we already have created the attrgroup here which is "dirty" and refusing * an account here will trigger attrgroup_destroy which will "sync" the * bad data to the storage! The codes should make sure we don't fail here */ /* mini version of accountlist_find_account(username) || accountlist_find_account(uid) */ { t_entry * curr; t_account * curraccount; char const * tname; if (uid <= maxuserid) HASHTABLE_TRAVERSE_MATCHING(accountlist_uid_head, curr, uid) { curraccount = (t_account*)entry_get_data(curr); if (curraccount->uid == uid) { eventlog(eventlog_level_debug, __FUNCTION__, "BUG: user \"{}\":" UID_FORMAT " already has an account (\"{}\":" UID_FORMAT ")", username, uid, account_get_name(curraccount), curraccount->uid); hashtable_entry_release(curr); return NULL; } } HASHTABLE_TRAVERSE_MATCHING(accountlist_head, curr, account->namehash) { curraccount = (t_account*)entry_get_data(curr); if ((tname = account_get_name(curraccount))) { if (strcasecmp(tname, username) == 0) { eventlog(eventlog_level_debug, __FUNCTION__, "BUG: user \"{}\":" UID_FORMAT " already has an account (\"{}\":" UID_FORMAT ")", username, uid, tname, curraccount->uid); hashtable_entry_release(curr); return NULL; } } } } if (hashtable_insert_data(accountlist_head, account, account->namehash) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add account to list"); return NULL; } if (hashtable_insert_data(accountlist_uid_head, account, uid)<0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add account to list"); return NULL; } /* hashtable_stats(accountlist_head); */ if (uid>maxuserid) maxuserid = uid; return account; } extern t_account * accountlist_create_account(const char *username, const char *passhash1) { t_account *res; assert(username != NULL); assert(passhash1 != NULL); res = account_create(username, passhash1); if (!res) return NULL; /* eventlog reported ealier */ if (!accountlist_add_account(res)) { account_destroy(res); return NULL; /* eventlog reported earlier */ } account_save(res, FS_FORCE); /* sync new account to storage */ return res; } extern int account_check_name(char const * name) { unsigned int i; char ch; if (!name) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL name"); return -1; } size_t namelen = std::strlen(name); for (i = 0; i < namelen; i++) { /* These are the Battle.net rules but they are too strict. * We want to allow any characters that wouldn't cause * problems so this should test for what is _not_ allowed * instead of what is. */ ch = name[i]; /* hardcoded safety checks */ if (ch == '/' || ch == '\\') return -1; if (std::isalnum((unsigned char)ch)) continue; if (std::strchr(prefs_get_account_allowed_symbols(), ch)) continue; return -1; } if (i < MIN_USERNAME_LEN || i >= MAX_USERNAME_LEN) return -1; return 0; } extern char const * account_get_name_real(t_account * account, char const * fn, unsigned int ln) { char const * temp; if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account (from {}:{})", fn, ln); return NULL; /* FIXME: places assume this can't fail */ } if (account->name) /* we have a cached username so return it */ return account->name; /* we dont have a cached username so lets get it from attributes */ if (!(temp = account_get_strattr(account, "BNET\\acct\\username"))) eventlog(eventlog_level_error, __FUNCTION__, "account has no username"); else account->name = xstrdup(temp); return account->name; } extern int account_check_mutual(t_account * account, int myuserid) { if (account == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!myuserid) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL userid"); return -1; } if (account->friends != NULL) { t_friend * fr; if ((fr = friendlist_find_uid(account->friends, myuserid)) != NULL) { friend_set_mutual(fr, FRIEND_ISMUTUAL); return 0; } } else { int i; int n = account_get_friendcount(account); int frienduid; for (i = 0; i < n; i++) { frienduid = account_get_friend(account, i); if (!frienduid) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL friend"); continue; } if (myuserid == frienduid) return 0; } } // If friend isnt in list return -1 to tell func NO return -1; } extern t_list * account_get_friends(t_account * account) { if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } if (!FLAG_ISSET(account->flags, ACCOUNT_FLAG_FLOADED)) if (account_load_friends(account) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not load friend list"); return NULL; } return account->friends; } static int account_load_friends(t_account * account) { int i; int n; int frienduid; t_account * acc; t_friend * fr; int newlist = 0; if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (FLAG_ISSET(account->flags, ACCOUNT_FLAG_FLOADED)) return 0; if (account->friends == NULL) { account->friends = list_create(); newlist = 1; } n = account_get_friendcount(account); for (i = 0; i < n; i++) { frienduid = account_get_friend(account, i); if (!frienduid) { account_remove_friend(account, i); continue; } fr = NULL; if (newlist || (fr = friendlist_find_uid(account->friends, frienduid)) == NULL) { if ((acc = accountlist_find_account_by_uid(frienduid)) == NULL) { if (account_remove_friend(account, i) == 0) { i--; n--; } continue; } if (account_check_mutual(acc, account_get_uid(account)) == 0) friendlist_add_account(account->friends, acc, FRIEND_ISMUTUAL); else friendlist_add_account(account->friends, acc, FRIEND_NOTMUTUAL); } else { if ((acc = friend_get_account(fr)) == NULL) { account_remove_friend(account, i); continue; } if (account_check_mutual(acc, account_get_uid(account)) == 0) friend_set_mutual(fr, FRIEND_ISMUTUAL); else friend_set_mutual(fr, FRIEND_NOTMUTUAL); } } if (!newlist) friendlist_purge(account->friends); FLAG_SET(&account->flags, ACCOUNT_FLAG_FLOADED); return 0; } static int account_unload_friends(t_account * account) { if (account->friends != nullptr && friendlist_unload(account->friends) < 0) return -1; FLAG_CLEAR(&account->flags, ACCOUNT_FLAG_FLOADED); return 0; } extern int account_set_clanmember(t_account * account, t_clanmember * clanmember) { if (account == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } account->clanmember = clanmember; return 0; } extern t_clanmember * account_get_clanmember(t_account * account) { t_clanmember * member; if (account == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } if ((member = account->clanmember) && (clanmember_get_clan(member)) && (clan_get_created(clanmember_get_clan(member)) > 0) && (clanmember_get_fullmember(member) == 1)) return member; else return NULL; } extern t_clanmember * account_get_clanmember_forced(t_account * account) { if (account == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } return account->clanmember; } extern t_clan * account_get_clan(t_account * account) { if (account == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } if (account->clanmember && (clanmember_get_clan(account->clanmember) != NULL) && (clan_get_created(clanmember_get_clan(account->clanmember)) > 0) && (clanmember_get_fullmember(account->clanmember) == 1)) return clanmember_get_clan(account->clanmember); else return NULL; } extern t_clan * account_get_creating_clan(t_account * account) { if (account == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } if (account->clanmember && (clanmember_get_clan(account->clanmember) != NULL) && ((clan_get_created(clanmember_get_clan(account->clanmember)) <= 0) || (clanmember_get_fullmember(account->clanmember) == 0))) return clanmember_get_clan(account->clanmember); else return NULL; } int account_set_conn(t_account * account, t_connection * conn) { if (!(account)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } account->conn = conn; return 0; } t_connection * account_get_conn(t_account * account) { if (!(account)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } return account->conn; } void account_add_team(t_account * account, t_team * team) { assert(account); assert(team); if (!(account->teams)) account->teams = list_create(); list_append_data(account->teams, team); } t_team * account_find_team_by_accounts(t_account * account, t_account **accounts, t_clienttag clienttag) { if ((account->teams)) return _list_find_team_by_accounts(accounts, clienttag, account->teams); else return NULL; } t_team * account_find_team_by_teamid(t_account * account, unsigned int teamid) { if ((account->teams)) return _list_find_team_by_teamid(teamid, account->teams); else return NULL; } t_list * account_get_teams(t_account * account) { assert(account); return account->teams; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/account.h000066400000000000000000000111101462640461400176620ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ACCOUNT_TYPES #define INCLUDED_ACCOUNT_TYPES #ifndef JUST_NEED_TYPES #define JUST_NEED_TYPES #include "common/list.h" #include "common/elist.h" #include "team.h" #include "attrgroup.h" #undef JUST_NEED_TYPES #else #include "common/list.h" #include "common/elist.h" #include "team.h" #include "attrgroup.h" #endif #define ACCOUNT_FLAG_NONE 0 #define ACCOUNT_FLAG_FLOADED 1 /* friends list loaded */ namespace pvpgn { namespace bnetd { struct connection; struct _clanmember; struct clan; typedef struct account_struct #ifdef ACCOUNT_INTERNAL_ACCESS { t_attrgroup * attrgroup; char * name; /* profiling proved 99% of getstrattr its from get_name */ unsigned int namehash; /* cached from attrs */ unsigned int uid; /* cached from attrs */ unsigned int flags; struct connection * conn; struct _clanmember * clanmember; t_list * friends; t_list * teams; } #endif t_account; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ACCOUNT_PROTOS #define INCLUDED_ACCOUNT_PROTOS #define JUST_NEED_TYPES #include "common/hashtable.h" #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern unsigned int maxuserid; extern int accountlist_reload(void); extern int account_check_name(char const * name); #define account_get_uid(A) account_get_uid_real(A,__FILE__,__LINE__) extern unsigned int account_get_uid_real(t_account const * account, char const * fn, unsigned int ln); extern int account_match(t_account * account, char const * username); extern int account_save(t_account *account, unsigned flags); extern char const * account_get_strattr_real(t_account * account, char const * key, char const * fn, unsigned int ln); #define account_get_strattr(A,K) account_get_strattr_real(A,K,__FILE__,__LINE__) extern int account_set_strattr(t_account * account, char const * key, char const * val); extern int accountlist_create(void); extern int accountlist_destroy(void); extern t_hashtable * accountlist(void); extern t_hashtable * accountlist_uid(void); extern int accountlist_load_all(int flag); extern unsigned int accountlist_get_length(void); extern int accountlist_save(unsigned flags); extern int accountlist_flush(unsigned flags); extern t_account * accountlist_find_account(char const * username); extern t_account * accountlist_find_account_by_uid(unsigned int uid); extern char const *accountlist_find_vague_account(t_account * account, char const *vague_username); extern int accountlist_allow_add(void); extern t_account * accountlist_create_account(const char *username, const char *passhash1); /* names and passwords */ extern char const * account_get_name_real(t_account * account, char const * fn, unsigned int ln); # define account_get_name(A) account_get_name_real(A,__FILE__,__LINE__) extern int account_check_mutual(t_account * account, int myuserid); extern t_list * account_get_friends(t_account * account); extern int account_set_clanmember(t_account * account, _clanmember * clanmember); extern _clanmember * account_get_clanmember(t_account * account); extern _clanmember * account_get_clanmember_forced(t_account * account); extern clan * account_get_clan(t_account * account); extern clan * account_get_creating_clan(t_account * account); extern int account_set_conn(t_account * account, t_connection * conn); extern t_connection * account_get_conn(t_account * account); extern void account_add_team(t_account * account, t_team * team); extern t_team * account_find_team_by_accounts(t_account * account, t_account **accounts, t_clienttag clienttag); extern t_team * account_find_team_by_teamid(t_account * account, unsigned int teamid); extern t_list * account_get_teams(t_account * account); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/account_wrap.cpp000066400000000000000000002366041462640461400212670ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define CHARACTER_INTERNAL_ACCESS #include "common/setup_before.h" #include "account_wrap.h" #include #include #include #include "compat/strcasecmp.h" #include "common/bnet_protocol.h" #include "common/bnettime.h" #include "common/eventlog.h" #include "common/list.h" #include "common/tag.h" #include "common/util.h" #include "account.h" #include "anongame_infos.h" #include "character.h" #include "clan.h" #include "command.h" #include "connection.h" #include "friends.h" #include "i18n.h" #include "ladder.h" #include "prefs.h" #include "server.h" #include "team.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static unsigned int char_icon_to_uint(const char * icon); extern unsigned int account_get_numattr_real(t_account * account, char const * key, char const * fn, unsigned int ln) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account (from {}:{})", fn, ln); return 0; } if (key == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key (from {}:{})", fn, ln); return 0; } char const * temp = account_get_strattr(account, key); if (temp == nullptr) return 0; unsigned int val; if (str_to_uint(temp, &val) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "not a numeric string \"{}\" for key \"{}\"", temp, key); return 0; } return val; } extern int account_set_numattr(t_account * account, char const * key, unsigned int val) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (key == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return -1; } return account_set_strattr(account, key, std::to_string(val).c_str()); } extern int account_get_boolattr_real(t_account * account, char const * key, char const * fn, unsigned int ln) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account (from {}:{})", fn, ln); return -1; } if (key == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key (from {}:{})", fn, ln); return -1; } char const * temp = account_get_strattr(account, key); if (temp == nullptr) return -1; switch (str_get_bool(temp)) { case 1: return 1; case 0: return 0; default: eventlog(eventlog_level_error, __FUNCTION__, "bad boolean value \"{}\" for key \"{}\"", temp, key); return -1; } } extern int account_set_boolattr(t_account * account, char const * key, int val) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (key == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return -1; } return account_set_strattr(account, key, val ? "true" : "false"); } extern char const * account_get_rawattr_real(t_account * account, char const * key, char const * fn, unsigned int ln) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account (from {}:{})", fn, ln); return nullptr; } if (key == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key (from {}:{})", fn, ln); return nullptr; } char const * temp = account_get_strattr(account, key); if (temp == nullptr) return nullptr; size_t length = std::strlen(temp) / 3; char * result = (char *)xmalloc(length); if (result == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "failed to create result"); return nullptr; } hex_to_str(temp, result, length); return result; } extern int account_set_rawattr(t_account * account, char const * key, char const * val, int length) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (key == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return -1; } char * temp_buffer = (char *)xmalloc(length * 3 + 1); str_to_hex(temp_buffer, val, length); int result = account_set_strattr(account, key, temp_buffer); xfree((void *)temp_buffer); return result; } /****************************************************************/ extern char const * account_get_pass(t_account * account) { return account_get_strattr(account, "BNET\\acct\\passhash1"); } extern int account_set_pass(t_account * account, char const * passhash1) { return account_set_strattr(account, "BNET\\acct\\passhash1", passhash1); } extern char const * account_get_salt(t_account * account) { return account_get_rawattr(account, "BNET\\acct\\salt"); } extern int account_set_salt(t_account * account, char const * salt) { return account_set_rawattr(account, "BNET\\acct\\salt", salt, 32); } extern char const * account_get_verifier(t_account * account) { return account_get_rawattr(account, "BNET\\acct\\verifier"); } extern int account_set_verifier(t_account * account, char const * verifier) { return account_set_rawattr(account, "BNET\\acct\\verifier", verifier, 32); } /****************************************************************/ extern int account_get_auth_admin(t_account * account, char const * channelname) { if (channelname == nullptr) return account_get_boolattr(account, "BNET\\auth\\admin"); std::string temp("BNET\\auth\\admin\\" + std::string(channelname)); return account_get_boolattr(account, temp.c_str()); } extern int account_get_auth_admin(t_account * account, std::string channelname) { if (channelname.empty()) return account_get_boolattr(account, "BNET\\auth\\admin"); std::string temp("BNET\\auth\\admin\\" + channelname); return account_get_boolattr(account, temp.c_str()); } extern int account_set_auth_admin(t_account * account, char const * channelname, int val) { if (channelname == nullptr) return account_set_boolattr(account, "BNET\\auth\\admin", val); std::string temp("BNET\\auth\\admin\\" + std::string(channelname)); return account_set_boolattr(account, temp.c_str(), val); } extern int account_set_auth_admin(t_account * account, std::string channelname, int val) { if (channelname.empty()) return account_set_boolattr(account, "BNET\\auth\\admin", val); std::string temp("BNET\\auth\\admin\\" + channelname); return account_set_boolattr(account, temp.c_str(), val); } extern int account_get_auth_announce(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\announce"); } extern int account_get_auth_botlogin(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\botlogin"); } extern int account_get_auth_bnetlogin(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\normallogin"); } extern int account_get_auth_operator(t_account * account, char const * channelname) { if (channelname == nullptr) return account_get_boolattr(account, "BNET\\auth\\operator"); std::string temp("BNET\\auth\\operator\\" + std::string(channelname)); return account_get_boolattr(account, temp.c_str()); } extern int account_get_auth_operator(t_account * account, std::string channelname) { if (channelname.empty()) return account_get_boolattr(account, "BNET\\auth\\operator"); std::string temp("BNET\\auth\\operator\\" + channelname); return account_get_boolattr(account, temp.c_str()); } extern int account_set_auth_operator(t_account * account, char const * channelname, int val) { if (channelname == nullptr) return account_set_boolattr(account, "BNET\\auth\\operator", val); std::string temp("BNET\\auth\\operator\\" + std::string(channelname)); return account_set_boolattr(account, temp.c_str(), val); } extern int account_set_auth_operator(t_account * account, std::string channelname, int val) { if (channelname.empty()) return account_set_boolattr(account, "BNET\\auth\\operator", val); std::string temp("BNET\\auth\\operator\\" + channelname); return account_set_boolattr(account, temp.c_str(), val); } extern int account_get_auth_voice(t_account * account, char const * channelname) { std::string temp("BNET\\auth\\voice\\" + std::string(channelname)); return account_get_boolattr(account, temp.c_str()); } extern int account_get_auth_voice(t_account * account, std::string channelname) { std::string temp("BNET\\auth\\voice\\" + channelname); return account_get_boolattr(account, temp.c_str()); } extern int account_set_auth_voice(t_account * account, char const * channelname, int val) { std::string temp("BNET\\auth\\voice\\" + std::string(channelname)); return account_set_boolattr(account, temp.c_str(), val); } extern int account_set_auth_voice(t_account * account, std::string channelname, int val) { std::string temp("BNET\\auth\\voice\\" + channelname); return account_set_boolattr(account, temp.c_str(), val); } extern int account_get_auth_changepass(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\changepass"); } extern int account_get_auth_changeprofile(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\changeprofile"); } extern int account_get_auth_createnormalgame(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\createnormalgame"); } extern int account_get_auth_joinnormalgame(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\joinnormalgame"); } extern int account_get_auth_createladdergame(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\createladdergame"); } extern int account_get_auth_joinladdergame(t_account * account) { return account_get_boolattr(account, "BNET\\auth\\joinladdergame"); } extern int account_get_auth_lock(t_account * account) { #ifdef WIN32 // do not allow login with illegal Windows filenames // 1) account will not created on plain file storage // 2) it may cause a crash when writing a userlog commands into a file with that name for (int i = 0; i < (sizeof(ILLEGALFILENAMES) / sizeof(*ILLEGALFILENAMES)); i++) { if (strcasecmp(account_get_name(account), ILLEGALFILENAMES[i]) == 0) { account_set_auth_lockreason(account, "user name is illegal"); eventlog(eventlog_level_debug, __FUNCTION__, "user name is invalid (reserved file name on Windows)"); return true; } } #endif // check for unlock if (unsigned int locktime = account_get_auth_locktime(account)) { if ((locktime - std::time(NULL)) < 0) { account_set_auth_lock(account, 0); account_set_auth_locktime(account, 0); account_set_auth_lockreason(account, ""); account_set_auth_lockby(account, ""); } } return account_get_boolattr(account, "BNET\\auth\\lock"); } extern unsigned int account_get_auth_locktime(t_account * account) { return account_get_numattr(account, "BNET\\auth\\locktime"); } extern char const * account_get_auth_lockreason(t_account * account) { return account_get_strattr(account, "BNET\\auth\\lockreason"); } extern char const * account_get_auth_lockby(t_account * account) { return account_get_strattr(account, "BNET\\auth\\lockby"); } extern int account_set_auth_lock(t_account * account, int val) { return account_set_boolattr(account, "BNET\\auth\\lock", val); } extern int account_set_auth_locktime(t_account * account, unsigned int val) { return account_set_numattr(account, "BNET\\auth\\locktime", val); } extern int account_set_auth_lockreason(t_account * account, char const * val) { return account_set_strattr(account, "BNET\\auth\\lockreason", val); } extern int account_set_auth_lockby(t_account * account, char const * val) { return account_set_strattr(account, "BNET\\auth\\lockby", val); } extern int account_get_auth_mute(t_account * account) { // check for unmute if (unsigned int locktime = account_get_auth_mutetime(account)) { if ((locktime - std::time(NULL)) < 0) { account_set_auth_mute(account, 0); account_set_auth_mutetime(account, 0); account_set_auth_mutereason(account, ""); account_set_auth_muteby(account, ""); } } return account_get_boolattr(account, "BNET\\auth\\mute"); } extern unsigned int account_get_auth_mutetime(t_account * account) { return account_get_numattr(account, "BNET\\auth\\mutetime"); } extern char const * account_get_auth_mutereason(t_account * account) { return account_get_strattr(account, "BNET\\auth\\mutereason"); } extern char const * account_get_auth_muteby(t_account * account) { return account_get_strattr(account, "BNET\\auth\\muteby"); } extern int account_set_auth_mute(t_account * account, int val) { return account_set_boolattr(account, "BNET\\auth\\mute", val); } extern int account_set_auth_mutetime(t_account * account, unsigned int val) { return account_set_numattr(account, "BNET\\auth\\mutetime", val); } extern int account_set_auth_mutereason(t_account * account, char const * val) { return account_set_strattr(account, "BNET\\auth\\mutereason", val); } extern int account_set_auth_muteby(t_account * account, char const * val) { return account_set_strattr(account, "BNET\\auth\\muteby", val); } /* Return text with account lock */ extern std::string account_get_locktext(t_connection * c, t_account * account, bool with_author, bool for_mute) { std::string msgtemp; // append author of ban if (with_author) { if (char const * author = (for_mute) ? account_get_auth_muteby(account) : account_get_auth_lockby(account)) if (author && author[0] != '\0') { msgtemp += localize(c, " by {}", author); } } // append remaining time if (unsigned int locktime = (for_mute) ? account_get_auth_mutetime(account) : account_get_auth_locktime(account)) msgtemp += localize(c, " for {}", seconds_to_timestr(locktime - now)); else msgtemp += localize(c, " permanently"); // append reason char const * reason = (for_mute) ? account_get_auth_mutereason(account) : account_get_auth_lockreason(account); if (reason && reason[0] != '\0') { msgtemp += localize(c, " with a reason \"{}\"", reason); } return msgtemp; } extern std::string account_get_mutetext(t_connection * c, t_account * account, bool with_author) { return account_get_locktext(c, account, with_author, true); } /****************************************************************/ extern std::string account_get_sex(t_account * account) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return std::string(); } char const * temp = account_get_strattr(account, "profile\\sex"); if (temp == nullptr) return std::string(); return std::string(temp); } extern std::string account_get_age(t_account * account) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return std::string(); } char const * temp = account_get_strattr(account, "profile\\age"); if (temp == nullptr) return std::string(); return std::string(temp); } extern std::string account_get_loc(t_account * account) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return std::string(); } char const * temp = account_get_strattr(account, "profile\\location"); if (temp == nullptr) return std::string(); return std::string(temp); } extern std::string account_get_desc(t_account * account) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return std::string(); } char const * temp = account_get_strattr(account, "profile\\description"); if (temp == nullptr) return std::string(); return std::string(temp); } /****************************************************************/ /* Account creation time */ extern unsigned int account_get_ll_ctime(t_account * account) { return account_get_numattr(account, "BNET\\acct\\ctime"); } extern unsigned int account_get_ll_time(t_account * account) { return account_get_numattr(account, "BNET\\acct\\lastlogin_time"); } extern int account_set_ll_time(t_account * account, unsigned int t) { return account_set_numattr(account, "BNET\\acct\\lastlogin_time", t); } extern t_clienttag account_get_ll_clienttag(t_account * account) { char const * clienttag; t_clienttag clienttag_uint; clienttag = account_get_strattr(account, "BNET\\acct\\lastlogin_clienttag"); clienttag_uint = tag_str_to_uint(clienttag); return clienttag_uint; } extern int account_set_ll_clienttag(t_account * account, t_clienttag clienttag) { return account_set_strattr(account, "BNET\\acct\\lastlogin_clienttag", tag_uint_to_str2(clienttag).c_str()); } extern char const * account_get_ll_user(t_account * account) { return account_get_strattr(account, "BNET\\acct\\lastlogin_user"); } extern int account_set_ll_user(t_account * account, char const * user) { return account_set_strattr(account, "BNET\\acct\\lastlogin_user", user); } extern int account_set_ll_user(t_account * account, std::string user) { return account_set_strattr(account, "BNET\\acct\\lastlogin_user", user.c_str()); } extern char const * account_get_ll_owner(t_account * account) { return account_get_strattr(account, "BNET\\acct\\lastlogin_owner"); } extern int account_set_ll_owner(t_account * account, char const * owner) { return account_set_strattr(account, "BNET\\acct\\lastlogin_owner", owner); } extern int account_set_ll_owner(t_account * account, std::string owner) { return account_set_strattr(account, "BNET\\acct\\lastlogin_owner", owner.c_str()); } extern char const * account_get_ll_ip(t_account * account) { return account_get_strattr(account, "BNET\\acct\\lastlogin_ip"); } extern int account_set_ll_ip(t_account * account, char const * ip) { return account_set_strattr(account, "BNET\\acct\\lastlogin_ip", ip); } extern int account_set_ll_ip(t_account * account, std::string ip) { return account_set_strattr(account, "BNET\\acct\\lastlogin_ip", ip.c_str()); } /****************************************************************/ extern unsigned int account_get_normal_wins(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\wins"); return account_get_numattr(account, key.c_str()); } extern int account_inc_normal_wins(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\wins"); return account_set_numattr(account, key.c_str(), account_get_normal_wins(account, clienttag) + 1); } extern int account_set_normal_wins(t_account * account, t_clienttag clienttag, unsigned wins) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\wins"); return account_set_numattr(account, key.c_str(), wins); } extern unsigned int account_get_normal_losses(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\losses"); return account_get_numattr(account, key.c_str()); } extern int account_inc_normal_losses(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\losses"); return account_set_numattr(account, key.c_str(), account_get_normal_losses(account, clienttag) + 1); } extern int account_set_normal_losses(t_account * account, t_clienttag clienttag, unsigned losses) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\losses"); return account_set_numattr(account, key.c_str(), losses); } extern unsigned int account_get_normal_draws(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\draws"); return account_get_numattr(account, key.c_str()); } extern int account_inc_normal_draws(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\draws"); return account_set_numattr(account, key.c_str(), account_get_normal_draws(account, clienttag) + 1); } extern int account_set_normal_draws(t_account * account, t_clienttag clienttag, unsigned draws) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\draws"); return account_set_numattr(account, key.c_str(), draws); } extern unsigned int account_get_normal_disconnects(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\disconnects"); return account_get_numattr(account, key.c_str()); } extern int account_inc_normal_disconnects(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\disconnects"); return account_set_numattr(account, key.c_str(), account_get_normal_disconnects(account, clienttag) + 1); } extern int account_set_normal_disconnects(t_account * account, t_clienttag clienttag, unsigned discs) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\disconnects"); return account_set_numattr(account, key.c_str(), discs); } extern int account_set_normal_last_time(t_account * account, t_clienttag clienttag, t_bnettime t) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\last game"); return account_set_strattr(account, key.c_str(), bnettime_get_str(t)); } extern int account_set_normal_last_result(t_account * account, t_clienttag clienttag, char const * result) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\last game result"); return account_set_strattr(account, key.c_str(), result); } extern int account_set_normal_last_result(t_account * account, t_clienttag clienttag, std::string result) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\last game result"); return account_set_strattr(account, key.c_str(), result.c_str()); } /****************************************************************/ extern unsigned int account_get_ladder_active_wins(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active wins"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_active_wins(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int wins) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) +"\\" + std::to_string(id) + "\\active wins"); return account_set_numattr(account, key.c_str(), wins); } extern unsigned int account_get_ladder_active_losses(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active losses"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_active_losses(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int losses) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active losses"); return account_set_numattr(account, key.c_str(), losses); } extern unsigned int account_get_ladder_active_draws(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active draws"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_active_draws(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int draws) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active draws"); return account_set_numattr(account, key.c_str(), draws); } extern unsigned int account_get_ladder_active_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active disconnects"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_active_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int disconnects) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active disconnects"); return account_set_numattr(account, key.c_str(), disconnects); } extern unsigned int account_get_ladder_active_rating(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active rating"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_active_rating(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int rating) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active rating"); return account_set_numattr(account, key.c_str(), rating); } extern int account_get_ladder_active_rank(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active rank"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_active_rank(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int rank) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active rank"); return account_set_numattr(account, key.c_str(), rank); } extern char const * account_get_ladder_active_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return nullptr; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active last game"); return account_get_strattr(account, key.c_str()); } extern int account_set_ladder_active_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id, t_bnettime t) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\active last game"); return account_set_strattr(account, key.c_str(), bnettime_get_str(t)); } /****************************************************************/ extern unsigned int account_get_ladder_wins(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\wins"); return account_get_numattr(account, key.c_str()); } extern int account_inc_ladder_wins(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\wins"); return account_set_numattr(account, key.c_str(), account_get_ladder_wins(account, clienttag, id) + 1); } extern int account_set_ladder_wins(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned wins) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\wins"); return account_set_numattr(account, key.c_str(), wins); } extern unsigned int account_get_ladder_losses(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\losses"); return account_get_numattr(account, key.c_str()); } extern int account_inc_ladder_losses(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\losses"); return account_set_numattr(account, key.c_str(), account_get_ladder_losses(account, clienttag, id) + 1); } extern int account_set_ladder_losses(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned losses) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\losses"); return account_set_numattr(account, key.c_str(), losses); } extern unsigned int account_get_ladder_draws(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\draws"); return account_get_numattr(account, key.c_str()); } extern int account_inc_ladder_draws(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\draws"); return account_set_numattr(account, key.c_str(), account_get_ladder_draws(account, clienttag, id) + 1); } extern int account_set_ladder_draws(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned draws) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\draws"); return account_set_numattr(account, key.c_str(), draws); } extern unsigned int account_get_ladder_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\disconnects"); return account_get_numattr(account, key.c_str()); } extern int account_inc_ladder_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\disconnects"); return account_set_numattr(account, key.c_str(), account_get_ladder_disconnects(account, clienttag, id) + 1); } extern int account_set_ladder_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned discs) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\disconnects"); return account_set_numattr(account, key.c_str(), discs); } extern unsigned int account_get_ladder_rating(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\rating"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_rating(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned rating) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\rating"); return account_set_numattr(account, key.c_str(), rating); } extern int account_adjust_ladder_rating(t_account * account, t_clienttag clienttag, t_ladder_id id, int delta) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\rating"); /* don't allow rating to go below 1 */ unsigned int oldrating = account_get_ladder_rating(account, clienttag, id); unsigned int newrating; if (delta < 0 && oldrating <= (unsigned int)-delta) newrating = 1; else newrating = oldrating + delta; int retval = 0; if (account_set_numattr(account, key.c_str(), newrating) < 0) retval = -1; if (newrating > account_get_ladder_high_rating(account, clienttag, id)) { key = "Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\high rating"; if (account_set_numattr(account, key.c_str(), newrating) < 0) retval = -1; } return retval; } extern int account_get_ladder_rank(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\rank"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_rank(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int rank) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\rank"); int retval = 0; if (account_set_numattr(account, key.c_str(), rank) < 0) retval = -1; unsigned int oldrank = account_get_ladder_high_rank(account, clienttag, id); if (oldrank == 0 || rank < oldrank) { key = "Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\high rank"; if (account_set_numattr(account, key.c_str(), rank) < 0) retval = -1; } return retval; } extern unsigned int account_get_ladder_high_rating(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\high rating"); return account_get_numattr(account, key.c_str()); } extern unsigned int account_get_ladder_high_rank(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\high rank"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id, t_bnettime t) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\last game"); return account_set_strattr(account, key.c_str(), bnettime_get_str(t)); } extern char const * account_get_ladder_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return nullptr; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\last game"); return account_get_strattr(account, key.c_str()); } extern int account_set_ladder_last_result(t_account * account, t_clienttag clienttag, t_ladder_id id, char const * result) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + std::to_string(id) + "\\last game result"); return account_set_strattr(account, key.c_str(), result); } /****************************************************************/ extern unsigned int account_get_normal_level(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\level"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_level(t_account * account, t_clienttag clienttag, unsigned int level) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\level"); return account_set_numattr(account, key.c_str(), level); } extern unsigned int account_get_normal_class(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\class"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_class(t_account * account, t_clienttag clienttag, unsigned int chclass) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\class"); return account_set_numattr(account, key.c_str(), chclass); } extern unsigned int account_get_normal_diablo_kills(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\diablo kills"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_diablo_kills(t_account * account, t_clienttag clienttag, unsigned int diablo_kills) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\diablo kills"); return account_set_numattr(account, key.c_str(), diablo_kills); } extern unsigned int account_get_normal_strength(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\strength"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_strength(t_account * account, t_clienttag clienttag, unsigned int strength) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\strength"); return account_set_numattr(account, key.c_str(), strength); } extern unsigned int account_get_normal_magic(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\magic"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_magic(t_account * account, t_clienttag clienttag, unsigned int magic) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\magic"); return account_set_numattr(account, key.c_str(), magic); } extern unsigned int account_get_normal_dexterity(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\dexterity"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_dexterity(t_account * account, t_clienttag clienttag, unsigned int dexterity) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\dexterity"); return account_set_numattr(account, key.c_str(), dexterity); } extern unsigned int account_get_normal_vitality(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\vitality"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_vitality(t_account * account, t_clienttag clienttag, unsigned int vitality) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\vitality"); return account_set_numattr(account, key.c_str(), vitality); } extern unsigned int account_get_normal_gold(t_account * account, t_clienttag clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return 0; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\gold"); return account_get_numattr(account, key.c_str()); } extern int account_set_normal_gold(t_account * account, t_clienttag clienttag, unsigned int gold) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\0\\gold"); return account_set_numattr(account, key.c_str(), gold); } /****************************************************************/ extern int account_check_closed_character(t_account * account, t_clienttag clienttag, char const * realmname, char const * charname) { char const * charlist = account_get_closed_characterlist(account, clienttag, realmname); if (charlist == nullptr) { eventlog(eventlog_level_debug, __FUNCTION__, "no characters in Realm {}", realmname); return 0; } eventlog(eventlog_level_debug, __FUNCTION__, "got characterlist \"{}\" for Realm {}", charlist, realmname); size_t list_len = std::strlen(charlist); char const * start = charlist; char const * next_char = start; char tempname[32]; int name_len; for (std::size_t i = 0; i < list_len; i++, next_char++) { if (*next_char == ',') { name_len = next_char - start; std::strncpy(tempname, start, name_len); tempname[name_len] = '\0'; eventlog(eventlog_level_debug, __FUNCTION__, "found character \"{}\"", tempname); if (std::strcmp(tempname, charname) == 0) return 1; start = next_char + 1; } } name_len = next_char - start; std::strncpy(tempname, start, name_len); tempname[name_len] = '\0'; eventlog(eventlog_level_debug, __FUNCTION__, "found tail character \"{}\"", tempname); if (std::strcmp(tempname, charname) == 0) return 1; return 0; } extern char const * account_get_closed_characterlist(t_account * account, t_clienttag clienttag, char const * realmname) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return nullptr; } if (!realmname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realmname"); return nullptr; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return nullptr; } std::string realmkey("BNET\\CharacterList\\" + tag_uint_to_str2(clienttag) + "\\" + std::string(realmname) + "\\0"); eventlog(eventlog_level_trace, __FUNCTION__, "looking for '{}'", realmkey.c_str()); return account_get_strattr(account, realmkey.c_str()); } extern char const * account_get_closed_characterlist(t_account * account, t_clienttag clienttag, std::string realmname) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return nullptr; } if (realmname.empty()) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realmname"); return nullptr; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return nullptr; } std::string realmkey("BNET\\CharacterList\\" + tag_uint_to_str2(clienttag) + "\\" + realmname + "\\0"); eventlog(eventlog_level_trace, __FUNCTION__, "looking for '{}'", realmkey.c_str()); return account_get_strattr(account, realmkey.c_str()); } extern int account_set_closed_characterlist(t_account * account, t_clienttag clienttag, char const * charlist) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "clienttag='{}', charlist='{}'", tag_uint_to_str2(clienttag).c_str(), charlist); std::string key("BNET\\Characters\\" + tag_uint_to_str2(clienttag) + "\\0"); return account_set_strattr(account, key.c_str(), charlist); } extern int account_set_closed_characterlist(t_account * account, t_clienttag clienttag, std::string charlist) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "clienttag='{}', charlist='{}'", tag_uint_to_str2(clienttag).c_str(), charlist.c_str()); std::string key("BNET\\Characters\\" + tag_uint_to_str2(clienttag) + "\\0"); return account_set_strattr(account, key.c_str(), charlist.c_str()); } extern int account_add_closed_character(t_account * account, t_clienttag clienttag, t_character * ch) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } if (!ch) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "clienttag=\"{}\", realm=\"{}\", name=\"{}\"", tag_uint_to_str2(clienttag).c_str(), ch->realmname, ch->name); std::string key("BNET\\CharacterList\\" + tag_uint_to_str2(clienttag) + "\\" + std::string(ch->realmname) + "\\0"); std::string chars_in_realm; char const * old_list = account_get_strattr(account, key.c_str()); if (old_list) chars_in_realm = std::string(old_list) + "," + std::string(ch->name); else chars_in_realm = std::string(ch->name); eventlog(eventlog_level_debug, __FUNCTION__, "new character list for realm \"{}\" is \"{}\"", ch->realmname, chars_in_realm.c_str()); account_set_strattr(account, key.c_str(), chars_in_realm.c_str()); key = "BNET\\Characters\\" + tag_uint_to_str2(clienttag) + "\\" + std::string(ch->realmname) + "\\" + std::string(ch->name) + "\\0"; char hex_buffer[356]; str_to_hex(hex_buffer, (char*)ch->data, ch->datalen); account_set_strattr(account, key.c_str(), hex_buffer); /* eventlog(eventlog_level_debug,__FUNCTION__,"key \"%s\"", key); eventlog(eventlog_level_debug,__FUNCTION__,"value \"%s\"", hex_buffer); */ return 0; } extern int account_set_friend(t_account * account, int friendnum, unsigned int frienduid) { if (frienduid == 0 || friendnum < 0 || friendnum >= prefs_get_max_friends()) return -1; std::string key("friend\\" + std::to_string(friendnum) + "\\uid"); return account_set_numattr(account, key.c_str(), frienduid); } extern unsigned int account_get_friend(t_account * account, int friendnum) { if (friendnum < 0 || friendnum >= prefs_get_max_friends()) { // bogus name (user himself) instead of NULL, otherwise clients might crash eventlog(eventlog_level_error, __FUNCTION__, "invalid friendnum {} (max: {})", friendnum, prefs_get_max_friends()); return 0; } std::string key("friend\\" + std::to_string(friendnum) + "\\uid"); int tmp = account_get_numattr(account, key.c_str()); if (!tmp) { // ok, looks like we have a problem. Maybe friends still stored in old format? key = "friend\\" + std::to_string(friendnum) + "\\name"; char const * name = account_get_strattr(account, key.c_str()); if (!name) { eventlog(eventlog_level_error, __FUNCTION__, "could not find friend (friendno: {} of '{}')", friendnum, account_get_name(account)); return 0; } t_account * acct = accountlist_find_account(name); if (acct != nullptr) { tmp = account_get_uid(acct); account_set_friend(account, friendnum, tmp); account_set_strattr(account, key.c_str(), nullptr); //remove old username-based friend now return tmp; } account_set_strattr(account, key.c_str(), nullptr); //remove old username-based friend now eventlog(eventlog_level_warn, __FUNCTION__, "unexistant friend name ('{}') in old storage format", name); return 0; } return tmp; } static int account_set_friendcount(t_account * account, int count) { if (count < 0 || count > prefs_get_max_friends()) return -1; return account_set_numattr(account, "friend\\count", count); } extern int account_get_friendcount(t_account * account) { return account_get_numattr(account, "friend\\count"); } extern int account_add_friend(t_account * my_acc, t_account * facc) { if (my_acc == nullptr || facc == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } unsigned int my_uid = account_get_uid(my_acc); unsigned int fuid = account_get_uid(facc); if (my_acc == facc) return -2; int nf = account_get_friendcount(my_acc); if (nf >= prefs_get_max_friends()) return -3; t_list *flist = account_get_friends(my_acc); if (flist == nullptr) return -1; if (friendlist_find_account(flist, facc) != nullptr) return -4; account_set_friend(my_acc, nf, fuid); account_set_friendcount(my_acc, nf + 1); if (account_check_mutual(facc, my_uid) == 0) friendlist_add_account(flist, facc, FRIEND_ISMUTUAL); else friendlist_add_account(flist, facc, FRIEND_NOTMUTUAL); return 0; } extern int account_remove_friend(t_account * account, int friendnum) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } int n = account_get_friendcount(account); if (friendnum < 0 || friendnum >= n) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid friendnum (friendnum: {} max: {})", friendnum, n); return -1; } for (auto i = friendnum; i < n - 1; i++) account_set_friend(account, i, account_get_friend(account, i + 1)); account_set_friend(account, n - 1, 0); /* FIXME: should delete the attribute */ account_set_friendcount(account, n - 1); return 0; } extern int account_remove_friend2(t_account * account, const char * frienduid) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (frienduid == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL friend username"); return -1; } t_list *flist = account_get_friends(account); if (flist == nullptr) return -1; t_friend *fr = friendlist_find_username(flist, frienduid); if (fr == nullptr) return -2; int n = account_get_friendcount(account); unsigned int uid = account_get_uid(friend_get_account(fr)); for (auto i = 0; i < n; i++) { if (account_get_friend(account, i) == uid) { t_account * facc = friend_get_account(fr); t_list * fflist = account_get_friends(facc); t_friend * ffr = friendlist_find_account(fflist, account); account_remove_friend(account, i); if (facc && fflist && ffr) friend_set_mutual(ffr, FRIEND_NOTMUTUAL); friendlist_remove_friend(flist, fr); return i; } } return -2; } // Some Extra Commands for REAL admins to promote other users to Admins // And Moderators of channels extern int account_set_admin(t_account * account) { return account_set_strattr(account, "BNET\\auth\\admin", "true"); } extern int account_set_demoteadmin(t_account * account) { return account_set_strattr(account, "BNET\\auth\\admin", "false"); } extern unsigned int account_get_command_groups(t_account * account) { return account_get_numattr(account, "BNET\\auth\\command_groups"); } extern int account_set_command_groups(t_account * account, unsigned int groups) { return account_set_numattr(account, "BNET\\auth\\command_groups", groups); } // WAR3 Play Game & Profile Funcs extern std::string race_get_str(unsigned int race) { switch (race) { case W3_RACE_ORCS: return "orcs"; case W3_RACE_HUMANS: return "humans"; case W3_RACE_UNDEAD: case W3_ICON_UNDEAD: return "undead"; case W3_RACE_NIGHTELVES: return "nightelves"; case W3_RACE_RANDOM: case W3_ICON_RANDOM: return "random"; case W3_RACE_DEMONS: case W3_ICON_DEMONS: return "demons"; default: eventlog(eventlog_level_warn, __FUNCTION__, "unknown race: {}", race); return std::string(); } } extern int account_inc_racewins(t_account * account, unsigned int intrace, t_clienttag clienttag) { std::string race = race_get_str(intrace); if (race.empty()) return -1; std::string table("Record\\" + tag_uint_to_str2(clienttag) + "\\" + race + "\\wins"); unsigned int wins = account_get_numattr(account, table.c_str()); wins++; return account_set_numattr(account, table.c_str(), wins); } extern int account_get_racewins(t_account * account, unsigned int intrace, t_clienttag clienttag) { std::string race = race_get_str(intrace); if (race.empty()) return 0; std::string table("Record\\" + tag_uint_to_str2(clienttag) + "\\" + race + "\\wins"); return account_get_numattr(account, table.c_str()); } extern int account_inc_racelosses(t_account * account, unsigned int intrace, t_clienttag clienttag) { std::string race = race_get_str(intrace); if (race.empty()) return -1; std::string table("Record\\" + tag_uint_to_str2(clienttag) + "\\" + race + "\\losses"); unsigned int losses = account_get_numattr(account, table.c_str()); losses++; return account_set_numattr(account, table.c_str(), losses); } extern int account_get_racelosses(t_account * account, unsigned int intrace, t_clienttag clienttag) { std::string race = race_get_str(intrace); if (race.empty()) return 0; std::string table("Record\\" + tag_uint_to_str2(clienttag) + "\\" + race + "\\losses"); return account_get_numattr(account, table.c_str()); } extern int account_update_xp(t_account * account, t_clienttag clienttag, t_game_result gameresult, unsigned int opponlevel, int * xp_diff, t_ladder_id id) { int xp = account_get_ladder_xp(account, clienttag, id); //get current xp if (xp < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got negative XP"); return -1; } int mylevel = account_get_ladder_level(account, clienttag, id); //get accounts level if (mylevel > W3_XPCALC_MAXLEVEL) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid level: {}", mylevel); return -1; } if (mylevel <= 0) //if level is negative or 0 then set it to 1 mylevel = 1; if (opponlevel < 1) opponlevel = 1; int xpdiff = 0, placeholder; switch (gameresult) { case game_result_win: ladder_war3_xpdiff(mylevel, opponlevel, &xpdiff, &placeholder); break; case game_result_loss: ladder_war3_xpdiff(opponlevel, mylevel, &placeholder, &xpdiff); break; default: eventlog(eventlog_level_error, __FUNCTION__, "got invalid game result: {}", gameresult); return -1; } *xp_diff = xpdiff; xp += xpdiff; if (xp < 0) xp = 0; return account_set_ladder_xp(account, clienttag, id, xp); } extern int account_get_ladder_xp(t_account * account, t_clienttag clienttag, t_ladder_id id) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\xp"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_xp(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int xp) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\xp"); return account_set_numattr(account, key.c_str(), xp); } extern int account_get_ladder_level(t_account * account, t_clienttag clienttag, t_ladder_id id) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\level"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_level(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int level) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\level"); return account_set_numattr(account, key.c_str(), level); } extern int account_adjust_ladder_level(t_account * account, t_clienttag clienttag, t_ladder_id id) { int xp = account_get_ladder_xp(account, clienttag, id); if (xp < 0) xp = 0; int mylevel = account_get_ladder_level(account, clienttag, id); if (mylevel < 1) mylevel = 1; if (mylevel > W3_XPCALC_MAXLEVEL) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid level: {}", mylevel); return -1; } mylevel = ladder_war3_updatelevel(mylevel, xp); return account_set_ladder_level(account, clienttag, id, mylevel); } //Other funcs used in profiles and PG saving extern void account_get_raceicon(t_account * account, char * raceicon, unsigned int * raceiconnumber, unsigned int * wins, t_clienttag clienttag) //Based of wins for each race, Race with most wins, gets shown in chat channel { unsigned int random = account_get_racewins(account, W3_RACE_RANDOM, clienttag); unsigned int humans = account_get_racewins(account, W3_RACE_HUMANS, clienttag); unsigned int orcs = account_get_racewins(account, W3_RACE_ORCS, clienttag); unsigned int undead = account_get_racewins(account, W3_RACE_UNDEAD, clienttag); unsigned int nightelf = account_get_racewins(account, W3_RACE_NIGHTELVES, clienttag); if (orcs >= humans && orcs >= undead && orcs >= nightelf && orcs >= random) { *raceicon = 'O'; *wins = orcs; } else if (humans >= orcs && humans >= undead && humans >= nightelf && humans >= random) { *raceicon = 'H'; *wins = humans; } else if (nightelf >= humans && nightelf >= orcs && nightelf >= undead && nightelf >= random) { *raceicon = 'N'; *wins = nightelf; } else if (undead >= humans && undead >= orcs && undead >= nightelf && undead >= random) { *raceicon = 'U'; *wins = undead; } else { *raceicon = 'R'; *wins = random; } unsigned int i = 1; while ((signed)*wins >= anongame_infos_get_ICON_REQ(i, clienttag) && anongame_infos_get_ICON_REQ(i, clienttag) > 0) i++; *raceiconnumber = i; } extern int account_get_profile_calcs(t_account * account, int xp, unsigned int Level) { unsigned int startlvl; if (Level == 1) startlvl = 1; else startlvl = Level - 1; for (auto i = startlvl; i < W3_XPCALC_MAXLEVEL; i++) { int xp_min = ladder_war3_get_min_xp(i); int xp_max = ladder_war3_get_min_xp(i + 1); if ((xp >= xp_min) && (xp < xp_max)) { //FIXME....... int t = (int)((((double)xp - (double)xp_min) / ((double)xp_max - (double)xp_min)) * 128); if (i < Level) return 128 + t; else return t; } } return 0; } extern int account_set_saveladderstats(t_account * account, unsigned int gametype, t_game_result result, unsigned int opponlevel, t_clienttag clienttag) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } //added for better tracking down of problems with gameresults eventlog(eventlog_level_trace, __FUNCTION__, "parsing game result for player: {} result: {}", account_get_name(account), (result == game_result_win) ? "WIN" : "LOSS"); unsigned int intrace = account_get_w3pgrace(account, clienttag); unsigned int uid = account_get_uid(account); t_ladder_id id; switch (gametype) { case ANONGAME_TYPE_1V1: //1v1 { id = ladder_id_solo; break; } case ANONGAME_TYPE_2V2: case ANONGAME_TYPE_3V3: case ANONGAME_TYPE_4V4: case ANONGAME_TYPE_5V5: case ANONGAME_TYPE_6V6: case ANONGAME_TYPE_2V2V2: case ANONGAME_TYPE_3V3V3: case ANONGAME_TYPE_4V4V4: case ANONGAME_TYPE_2V2V2V2: case ANONGAME_TYPE_3V3V3V3: { id = ladder_id_team; break; } case ANONGAME_TYPE_SMALL_FFA: { id = ladder_id_ffa; break; } default: eventlog(eventlog_level_error, __FUNCTION__, "Invalid Gametype? {}", gametype); return -1; } if (result == game_result_win) { account_inc_ladder_wins(account, clienttag, id); account_inc_racewins(account, intrace, clienttag); } if (result == game_result_loss) { account_inc_ladder_losses(account, clienttag, id); account_inc_racelosses(account, intrace, clienttag); } int xpdiff; account_update_xp(account, clienttag, result, opponlevel, &xpdiff, id); account_adjust_ladder_level(account, clienttag, id); unsigned int level = account_get_ladder_level(account, clienttag, id); unsigned int xp = account_get_ladder_xp(account, clienttag, id); LadderList* ladderList = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_default, ladder_time_default)); LadderReferencedObject reference(account); //consider using wins count for tertiary attribute ? ladderList->updateEntry(uid, level, xp, 0, reference); return 0; } extern int account_set_w3pgrace(t_account * account, t_clienttag clienttag, unsigned int race) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\w3pgrace"); return account_set_numattr(account, key.c_str(), race); } extern int account_get_w3pgrace(t_account * account, t_clienttag clienttag) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\w3pgrace"); return account_get_numattr(account, key.c_str()); } // Arranged Team Functions extern int account_set_currentatteam(t_account * account, unsigned int teamcount) { return account_set_numattr(account, "BNET\\current_at_team", teamcount); } extern int account_get_currentatteam(t_account * account) { return account_get_numattr(account, "BNET\\current_at_team"); } extern int account_get_highestladderlevel(t_account * account, t_clienttag clienttag) { unsigned int sololevel = account_get_ladder_level(account, clienttag, ladder_id_solo); unsigned int teamlevel = account_get_ladder_level(account, clienttag, ladder_id_team); unsigned int ffalevel = account_get_ladder_level(account, clienttag, ladder_id_ffa); unsigned int atlevel = 0; unsigned int t; if (account_get_teams(account)) { t_elem * curr = nullptr; t_team * team = nullptr; LIST_TRAVERSE(account_get_teams(account), curr) { team = (t_team*)elem_get_data(curr); if (team == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (team_get_clienttag(team) != clienttag) continue; t = team_get_level(team); if (t > atlevel) atlevel = t; } } eventlog(eventlog_level_debug, __FUNCTION__, "Checking for highest level in Solo,Team,FFA,AT Ladder Stats"); eventlog(eventlog_level_debug, __FUNCTION__, "Solo Level: {}, Team Level {}, FFA Level {}, Highest AT Team Level: {}", sololevel, teamlevel, ffalevel, atlevel); if (sololevel >= teamlevel && sololevel >= atlevel && sololevel >= ffalevel) return sololevel; if (teamlevel >= sololevel && teamlevel >= atlevel && teamlevel >= ffalevel) return teamlevel; if (atlevel >= sololevel && atlevel >= teamlevel && atlevel >= ffalevel) return atlevel; return ffalevel; // we should never get here return -1; } /* value = icons delimeted by space */ extern int account_set_user_iconstash(t_account * account, t_clienttag clienttag, char const * value) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\iconstash"); if (value) return account_set_strattr(account, key.c_str(), value); else return account_set_strattr(account, key.c_str(), "NULL"); } extern int account_set_user_iconstash(t_account * account, t_clienttag clienttag, std::string value) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\iconstash"); if (!value.empty()) return account_set_strattr(account, key.c_str(), value.c_str()); else return account_set_strattr(account, key.c_str(), "NULL"); } extern char const * account_get_user_iconstash(t_account * account, t_clienttag clienttag) { std::string key ("Record\\" + tag_uint_to_str2(clienttag) + "\\iconstash"); char const * retval = account_get_strattr(account, key.c_str()); if ((retval != nullptr) && (std::strcmp(retval, "NULL") != 0)) return retval; else return nullptr; } //BlacKDicK 04/20/2003 extern int account_set_user_icon(t_account * account, t_clienttag clienttag, char const * usericon) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\userselected_icon"); if (usericon) return account_set_strattr(account, key.c_str(), usericon); else return account_set_strattr(account, key.c_str(), "NULL"); } extern int account_set_user_icon(t_account * account, t_clienttag clienttag, std::string usericon) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\userselected_icon"); if (!usericon.empty()) return account_set_strattr(account, key.c_str(), usericon.c_str()); else return account_set_strattr(account, key.c_str(), "NULL"); } extern char const * account_get_user_icon(t_account * account, t_clienttag clienttag) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\userselected_icon"); char const * retval = account_get_strattr(account, key.c_str()); if ((retval != nullptr) && ((std::strcmp(retval, "NULL") != 0))) return retval; else return nullptr; } // Ramdom - Nothing, Grean Dragon Whelp, Azure Dragon (Blue Dragon), Red Dragon, Deathwing, Nothing // Humans - Peasant, Footman, Knight, Archmage, Medivh, Nothing // Orcs - Peon, Grunt, Tauren, Far Seer, Thrall, Nothing // Undead - Acolyle, Ghoul, Abomination, Lich, Tichondrius, Nothing // Night Elves - Wisp, Archer, Druid of the Claw, Priestess of the Moon, Furion Stormrage, Nothing // Demons - Nothing, ???(wich unit is nfgn), Infernal, Doom Guard, Pit Lord/Manaroth, Archimonde // ADDED TFT ICON BY DJP 07/16/2003 static const char * profile_code[12][6] = { { NULL, "ngrd", "nadr", "nrdr", "nbwm", NULL }, { "hpea", "hfoo", "hkni", "Hamg", "nmed", NULL }, { "opeo", "ogru", "otau", "Ofar", "Othr", NULL }, { "uaco", "ugho", "uabo", "Ulic", "Utic", NULL }, { "ewsp", "earc", "edoc", "Emoo", "Efur", NULL }, { NULL, "nfng", "ninf", "nbal", "Nplh", "Uwar" }, /* not used by RoC */ { NULL, "nmyr", "nnsw", "nhyc", "Hvsh", "Eevm" }, { "hpea", "hrif", "hsor", "hspt", "Hblm", "Hjai" }, { "opeo", "ohun", "oshm", "ospw", "Oshd", "Orex" }, { "uaco", "ucry", "uban", "uobs", "Ucrl", "Usyl" }, { "ewsp", "esen", "edot", "edry", "Ekee", "Ewrd" }, { NULL, "nfgu", "ninf", "nbal", "Nplh", "Uwar" } }; extern unsigned int account_get_icon_profile(t_account * account, t_clienttag clienttag) { unsigned int humans = account_get_racewins(account, W3_RACE_HUMANS, clienttag); // 1; unsigned int orcs = account_get_racewins(account, W3_RACE_ORCS, clienttag); // 2; unsigned int nightelf = account_get_racewins(account, W3_RACE_NIGHTELVES, clienttag); // 4; unsigned int undead = account_get_racewins(account, W3_RACE_UNDEAD, clienttag); // 8; unsigned int random = account_get_racewins(account, W3_RACE_RANDOM, clienttag); // 32; unsigned int race; // 0 = Humans, 1 = Orcs, 2 = Night Elves, 3 = Undead, 4 = Ramdom unsigned int level = 0; // 0 = under 25, 1 = 25 to 249, 2 = 250 to 499, 3 = 500 to 1499, 4 = 1500 or more (wins) int wins; int number_ctag = 0; /* moved the check for orcs in the first place so people with 0 wins get peon */ if (orcs >= humans && orcs >= undead && orcs >= nightelf && orcs >= random) { wins = orcs; race = 2; } else if (humans >= orcs && humans >= undead && humans >= nightelf && humans >= random) { wins = humans; race = 1; } else if (nightelf >= humans && nightelf >= orcs && nightelf >= undead && nightelf >= random) { wins = nightelf; race = 4; } else if (undead >= humans && undead >= orcs && undead >= nightelf && undead >= random) { wins = undead; race = 3; } else { wins = random; race = 0; } while (wins >= anongame_infos_get_ICON_REQ(level + 1, clienttag) && anongame_infos_get_ICON_REQ(level + 1, clienttag) > 0) level++; if (clienttag == CLIENTTAG_WAR3XP_UINT) number_ctag = 6; eventlog(eventlog_level_info, __FUNCTION__, "race -> {}; level -> {}; wins -> {}; profileicon -> {}", race, level, wins, profile_code[race + number_ctag][level]); return char_icon_to_uint(profile_code[race + number_ctag][level]); } extern unsigned int account_icon_to_profile_icon(char const * icon, t_account * account, t_clienttag ctag) { if (icon == nullptr) return account_get_icon_profile(account, ctag); const char * result; if (sizeof(icon) >= 4) { char tmp_icon[4]; int number_ctag = 0; std::strncpy(tmp_icon, icon, 4); tmp_icon[0] = tmp_icon[0] - 48; if (ctag == CLIENTTAG_WAR3XP_UINT) number_ctag = 6; if (tmp_icon[0] >= 1) { if (tmp_icon[1] == 'R') result = profile_code[0 + number_ctag][tmp_icon[0] - 1]; else if (tmp_icon[1] == 'H') result = profile_code[1 + number_ctag][tmp_icon[0] - 1]; else if (tmp_icon[1] == 'O') result = profile_code[2 + number_ctag][tmp_icon[0] - 1]; else if (tmp_icon[1] == 'U') result = profile_code[3 + number_ctag][tmp_icon[0] - 1]; else if (tmp_icon[1] == 'N') result = profile_code[4 + number_ctag][tmp_icon[0] - 1]; else if (tmp_icon[1] == 'D') result = profile_code[5 + number_ctag][tmp_icon[0] - 1]; else { eventlog(eventlog_level_warn, __FUNCTION__, "got unrecognized race on [{}] icon ", icon); result = profile_code[2][0]; } /* "opeo" */ } else { eventlog(eventlog_level_warn, __FUNCTION__, "got race_level<1 on [{}] icon ", icon); result = nullptr; } } else { eventlog(eventlog_level_error, __FUNCTION__, "got invalid icon length [{}] icon ", icon); result = nullptr; } return char_icon_to_uint(result); } extern int account_is_operator_or_admin(t_account * account, char const * channel) { if ((account_get_auth_operator(account, channel) == 1) || (account_get_auth_operator(account, nullptr) == 1) || (account_get_auth_admin(account, channel) == 1) || (account_get_auth_admin(account, nullptr) == 1)) return 1; else return 0; } static unsigned int char_icon_to_uint(const char * icon) { if (icon == nullptr) return 0; if (std::strlen(icon) != 4) return 0; unsigned int value; value = ((unsigned int)icon[0]) << 24; value |= ((unsigned int)icon[1]) << 16; value |= ((unsigned int)icon[2]) << 8; value |= ((unsigned int)icon[3]); return value; } extern char const * account_get_email(t_account * account) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return nullptr; } return account_get_strattr(account, "BNET\\acct\\email"); } extern int account_set_email(t_account * account, char const * email) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } return account_set_strattr(account, "BNET\\acct\\email", email); } extern int account_set_email(t_account * account, std::string email) { if (account == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } return account_set_strattr(account, "BNET\\acct\\email", email.c_str()); } extern int account_set_userlang(t_account * account, const char * lang) { if (lang) return account_set_strattr(account, "BNET\\acct\\userlang", lang); else return account_set_strattr(account, "BNET\\acct\\userlang", "NULL"); } extern int account_set_userlang(t_account * account, std::string lang) { if (!lang.empty()) return account_set_strattr(account, "BNET\\acct\\userlang", lang.c_str()); else return account_set_strattr(account, "BNET\\acct\\userlang", "NULL"); } extern char const * account_get_userlang(t_account * account) { char const * retval = account_get_strattr(account, "BNET\\acct\\userlang"); if ((retval != nullptr) && (std::strcmp(retval, "NULL") != 0)) return retval; else return nullptr; } /** * Westwood Online Extensions */ extern char const * account_get_wol_apgar(t_account * account) { if (account == nullptr) { ERROR0("got NULL account"); return nullptr; } return account_get_strattr(account, "WOL\\auth\\apgar"); } extern int account_set_wol_apgar(t_account * account, char const * apgar) { if (account == nullptr) { ERROR0("got NULL account"); return -1; } if (apgar == nullptr) { ERROR0("got NULL apgar"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[** WOL **] WOL\\auth\\apgar = {}", apgar); return account_set_strattr(account, "WOL\\auth\\apgar", apgar); } extern int account_set_wol_apgar(t_account * account, std::string apgar) { if (account == nullptr) { ERROR0("got NULL account"); return -1; } if (apgar.empty()) { ERROR0("got NULL apgar"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[** WOL **] WOL\\auth\\apgar = {}", apgar.c_str()); return account_set_strattr(account, "WOL\\auth\\apgar", apgar.c_str()); } extern int account_get_locale(t_account * account) { if (account == nullptr) { ERROR0("got NULL account"); return 0; } return account_get_numattr(account, "WOL\\acct\\locale"); } extern int account_set_locale(t_account * account, int locale) { if (account == nullptr) { ERROR0("got NULL account"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[** WOL **] WOL\\acct\\locale = {}", locale); return account_set_numattr(account, "WOL\\acct\\locale", locale); } extern int account_get_ladder_points(t_account * account, t_clienttag clienttag, t_ladder_id id) { std::string key("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\points"); return account_get_numattr(account, key.c_str()); } extern int account_set_ladder_points(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int points) { std::string key ("Record\\" + tag_uint_to_str2(clienttag) + "\\" + ladder_id_str.at(static_cast(id)) + "\\points"); return account_set_numattr(account, key.c_str(), points); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/account_wrap.h000066400000000000000000000465231462640461400207330ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ACCOUNT_WRAP_PROTOS #define INCLUDED_ACCOUNT_WRAP_PROTOS #define JUST_NEED_TYPES #include "account.h" #include "connection.h" #include "character.h" #include "common/bnettime.h" #include "ladder.h" #include "game.h" #include "common/tag.h" #undef JUST_NEED_TYPES #include namespace pvpgn { namespace bnetd { /* convenience functions */ extern unsigned int account_get_numattr_real(t_account * account, char const * key, char const * fn, unsigned int ln); #define account_get_numattr(A,K) account_get_numattr_real(A,K,__FILE__,__LINE__) extern int account_set_numattr(t_account * account, char const * key, unsigned int val); extern int account_get_boolattr_real(t_account * account, char const * key, char const * fn, unsigned int ln); #define account_get_boolattr(A,K) account_get_boolattr_real(A,K,__FILE__,__LINE__) extern int account_set_boolattr(t_account * account, char const * key, int val); /* warning: unlike the other account_get_*attr functions this one allocates memory for returning data - its up to the caller to clean it up */ extern char const * account_get_rawattr_real(t_account * account, char const * key, char const * fn, unsigned int ln); #define account_get_rawattr(A,K) account_get_rawattr_real(A,K,__FILE__,__LINE__) extern int account_set_rawattr(t_account * account, char const * key, char const * val, int length); extern char const * account_get_pass(t_account * account); extern int account_set_pass(t_account * account, char const * passhash1); extern char const * account_get_salt(t_account * account); extern int account_set_salt(t_account * account, char const * salt); extern char const * account_get_verifier(t_account * account); extern int account_set_verifier(t_account * account, char const * verifier); /* authorization */ extern int account_get_auth_admin(t_account * account, char const * channelname); extern int account_get_auth_admin(t_account * account, std::string channelname); extern int account_set_auth_admin(t_account * account, char const * channelname, int val); extern int account_set_auth_admin(t_account * account, std::string channelname, int val); extern int account_get_auth_announce(t_account * account); extern int account_get_auth_botlogin(t_account * account); extern int account_get_auth_bnetlogin(t_account * account); extern int account_get_auth_operator(t_account * account, char const * channelname); extern int account_get_auth_operator(t_account * account, std::string channelname); extern int account_set_auth_operator(t_account * account, char const * channelname, int val); extern int account_set_auth_operator(t_account * account, std::string channelname, int val); extern int account_get_auth_voice(t_account * account, char const * channelname); extern int account_get_auth_voice(t_account * account, std::string channelname); extern int account_set_auth_voice(t_account * account, char const * channelname, int val); extern int account_set_auth_voice(t_account * account, std::string channelname, int val); extern int account_get_auth_changepass(t_account * account); extern int account_get_auth_changeprofile(t_account * account); extern int account_get_auth_createnormalgame(t_account * account); extern int account_get_auth_joinnormalgame(t_account * account); extern int account_get_auth_createladdergame(t_account * account); extern int account_get_auth_joinladdergame(t_account * account); extern int account_get_auth_lock(t_account * account); extern unsigned int account_get_auth_locktime(t_account * account); extern char const * account_get_auth_lockreason(t_account * account); extern char const * account_get_auth_lockby(t_account * account); extern int account_set_auth_lock(t_account * account, int val); extern int account_set_auth_locktime(t_account * account, unsigned int val); extern int account_set_auth_lockreason(t_account * account, char const * val); extern int account_set_auth_lockby(t_account * account, char const * val); extern int account_get_auth_mute(t_account * account); extern unsigned int account_get_auth_mutetime(t_account * account); extern char const * account_get_auth_mutereason(t_account * account); extern char const * account_get_auth_muteby(t_account * account); extern int account_set_auth_mute(t_account * account, int val); extern int account_set_auth_mutetime(t_account * account, unsigned int val); extern int account_set_auth_mutereason(t_account * account, char const * val); extern int account_set_auth_muteby(t_account * account, char const * val); extern std::string account_get_locktext(t_connection * c, t_account * account, bool with_author = true, bool for_mute = false); extern std::string account_get_mutetext(t_connection * c, t_account * account, bool with_author = true); /* profile */ extern std::string account_get_sex(t_account * account); /* the profile attributes are updated directly in bnetd.c */ extern std::string account_get_age(t_account * account); extern std::string account_get_loc(t_account * account); extern std::string account_get_desc(t_account * account); /* last login */ extern unsigned int account_get_ll_ctime(t_account * account); extern unsigned int account_get_ll_time(t_account * account); extern int account_set_ll_time(t_account * account, unsigned int t); extern char const * account_get_ll_user(t_account * account); extern int account_set_ll_user(t_account * account, char const * user); extern int account_set_ll_user(t_account * account, std::string user); extern t_clienttag account_get_ll_clienttag(t_account * account); extern int account_set_ll_clienttag(t_account * account, t_clienttag clienttag); extern char const * account_get_ll_owner(t_account * account); extern int account_set_ll_owner(t_account * account, char const * owner); extern int account_set_ll_owner(t_account * account, std::string owner); extern char const * account_get_ll_ip(t_account * account); extern int account_set_ll_ip(t_account * account, char const * ip); /* normal stats */ extern unsigned int account_get_normal_wins(t_account * account, t_clienttag clienttag); extern int account_inc_normal_wins(t_account * account, t_clienttag clienttag); extern int account_set_normal_wins(t_account * account, t_clienttag clienttag, unsigned wins); extern unsigned int account_get_normal_losses(t_account * account, t_clienttag clienttag); extern int account_inc_normal_losses(t_account * account, t_clienttag clienttag); extern int account_set_normal_losses(t_account * account, t_clienttag clienttag, unsigned losses); extern unsigned int account_get_normal_draws(t_account * account, t_clienttag clienttag); extern int account_inc_normal_draws(t_account * account, t_clienttag clienttag); extern int account_set_normal_draws(t_account * account, t_clienttag clienttag, unsigned draws); extern unsigned int account_get_normal_disconnects(t_account * account, t_clienttag clienttag); extern int account_inc_normal_disconnects(t_account * account, t_clienttag clienttag); extern int account_set_normal_disconnects(t_account * account, t_clienttag clienttag, unsigned discs); extern int account_set_normal_last_time(t_account * account, t_clienttag clienttag, t_bnettime t); extern int account_set_normal_last_result(t_account * account, t_clienttag clienttag, char const * result); extern int account_set_normal_last_result(t_account * account, t_clienttag clienttag, std::string result); /* ladder stats (active) */ extern unsigned int account_get_ladder_active_wins(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_active_wins(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int wins); extern unsigned int account_get_ladder_active_losses(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_active_losses(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int losses); extern unsigned int account_get_ladder_active_draws(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_active_draws(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int draws); extern unsigned int account_get_ladder_active_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_active_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int disconnects); extern unsigned int account_get_ladder_active_rating(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_active_rating(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int rating); extern int account_get_ladder_active_rank(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_active_rank(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int rank); extern char const * account_get_ladder_active_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_active_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id, t_bnettime t); /* ladder stats (current) */ extern unsigned int account_get_ladder_wins(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_inc_ladder_wins(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_wins(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned wins); extern unsigned int account_get_ladder_losses(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_inc_ladder_draws(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_draws(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned draws); extern unsigned int account_get_ladder_draws(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_inc_ladder_losses(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_losses(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned losses); extern unsigned int account_get_ladder_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_inc_ladder_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_disconnects(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned discs); extern unsigned int account_get_ladder_rating(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_rating(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned rating); extern int account_adjust_ladder_rating(t_account * account, t_clienttag clienttag, t_ladder_id id, int delta); extern int account_get_ladder_rank(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_rank(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int rank); extern unsigned int account_get_ladder_high_rating(t_account * account, t_clienttag clienttag, t_ladder_id id); extern unsigned int account_get_ladder_high_rank(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id, t_bnettime t); extern char const * account_get_ladder_last_time(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_last_result(t_account * account, t_clienttag clienttag, t_ladder_id id, char const * result); /* Diablo normal stats */ extern unsigned int account_get_normal_level(t_account * account, t_clienttag clienttag); extern int account_set_normal_level(t_account * account, t_clienttag clienttag, unsigned int level); extern unsigned int account_get_normal_class(t_account * account, t_clienttag clienttag); extern int account_set_normal_class(t_account * account, t_clienttag clienttag, unsigned int chclass); extern unsigned int account_get_normal_diablo_kills(t_account * account, t_clienttag clienttag); extern int account_set_normal_diablo_kills(t_account * account, t_clienttag clienttag, unsigned int diablo_kills); extern unsigned int account_get_normal_strength(t_account * account, t_clienttag clienttag); extern int account_set_normal_strength(t_account * account, t_clienttag clienttag, unsigned int strength); extern unsigned int account_get_normal_magic(t_account * account, t_clienttag clienttag); extern int account_set_normal_magic(t_account * account, t_clienttag clienttag, unsigned int magic); extern unsigned int account_get_normal_dexterity(t_account * account, t_clienttag clienttag); extern int account_set_normal_dexterity(t_account * account, t_clienttag clienttag, unsigned int dexterity); extern unsigned int account_get_normal_vitality(t_account * account, t_clienttag clienttag); extern int account_set_normal_vitality(t_account * account, t_clienttag clienttag, unsigned int vitality); extern unsigned int account_get_normal_gold(t_account * account, t_clienttag clienttag); extern int account_set_normal_gold(t_account * account, t_clienttag clienttag, unsigned int gold); /* Diablo II closed characters */ extern char const * account_get_closed_characterlist(t_account * account, t_clienttag clienttag, char const * realmname); extern char const * account_get_closed_characterlist(t_account * account, t_clienttag clienttag, std::string realmname); extern int account_set_closed_characterlist(t_account * account, t_clienttag clienttag, char const * charlist); extern int account_set_closed_characterlist(t_account * account, t_clienttag clienttag, std::string charlist); extern int account_add_closed_character(t_account * account, t_clienttag clienttag, t_character * ch); extern int account_check_closed_character(t_account * account, t_clienttag clienttag, char const * realmname, char const * charname); extern int account_set_friend(t_account * account, int friendnum, unsigned int frienduid); extern unsigned int account_get_friend(t_account * account, int friendnum); extern int account_get_friendcount(t_account * account); extern int account_add_friend(t_account * my_acc, t_account * facc); extern int account_remove_friend(t_account * account, int friendnum); extern int account_remove_friend2(t_account * account, const char * friendname); extern std::string race_get_str(unsigned int race); extern int account_set_admin(t_account * account); extern int account_set_demoteadmin(t_account * account); extern unsigned int account_get_command_groups(t_account * account); extern int account_set_command_groups(t_account * account, unsigned int groups); extern int account_set_w3pgrace(t_account * account, t_clienttag clienttag, unsigned int race); extern int account_get_w3pgrace(t_account * account, t_clienttag clienttag); extern void account_get_raceicon(t_account * account, char * raceicon, unsigned int * raceiconnumber, unsigned int * wins, t_clienttag clienttag); //Used to call the save stats for all opponents extern int account_set_saveladderstats(t_account *a, unsigned int gametype, t_game_result result, unsigned int opponlevel, t_clienttag clienttag); extern int account_inc_racewins(t_account * account, unsigned int intrace, t_clienttag clienttag); extern int account_get_racewins(t_account * account, unsigned int intrace, t_clienttag clienttag); extern int account_inc_racelosses(t_account * account, unsigned int intrace, t_clienttag clienttag); extern int account_get_racelosses(t_account * account, unsigned int intrace, t_clienttag clienttag); extern int account_update_xp(t_account * account, t_clienttag clienttag, t_game_result gameresult, unsigned int opponlevel, int * xp_diff, t_ladder_id id); extern int account_get_ladder_xp(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_xp(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int xp); extern int account_get_ladder_level(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_level(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int level); extern int account_adjust_ladder_level(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_currentatteam(t_account * account, unsigned int teamcount); extern int account_get_currentatteam(t_account * account); extern int account_get_highestladderlevel(t_account * account, t_clienttag clienttag); //func will compare levels for AT, Solo/Team Ladder and out the higest level of the 3 // Determines the length of XP bar in profiles screen extern int account_get_profile_calcs(t_account * account, int xp, unsigned int level); extern unsigned int account_get_icon_profile(t_account * account, t_clienttag clienttag); extern int account_set_user_iconstash(t_account * account, t_clienttag clienttag, char const * value); extern int account_set_user_iconstash(t_account * account, t_clienttag clienttag, std::string value); extern char const * account_get_user_iconstash(t_account * account, t_clienttag clienttag); extern int account_set_user_icon(t_account * account, t_clienttag clienttag, char const * usericon); extern int account_set_user_icon(t_account * account, t_clienttag clienttag, std::string usericon); extern char const * account_get_user_icon(t_account * account, t_clienttag clienttag); extern unsigned int account_icon_to_profile_icon(char const * icon, t_account * account, t_clienttag ctag); extern char const * account_icon_default(t_account * account, t_clienttag clienttag); extern int account_is_operator_or_admin(t_account * account, char const * channel); extern int account_set_email(t_account * account, std::string email); extern char const * account_get_email(t_account * account); extern int account_set_userlang(t_account * account, const char * lang); extern int account_set_userlang(t_account * account, std::string lang); extern char const * account_get_userlang(t_account * account); /* Westwood Online Extensions */ extern char const * account_get_wol_apgar(t_account * account); extern int account_set_wol_apgar(t_account * account, char const * apgar); extern int account_set_wol_apgar(t_account * account, std::string apgar); extern int account_get_locale(t_account * account); extern int account_set_locale(t_account * account, int locale); extern int account_get_ladder_points(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int account_set_ladder_points(t_account * account, t_clienttag clienttag, t_ladder_id id, unsigned int points); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/adbanner.cpp000066400000000000000000000214461462640461400203500ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2005 Dizzy * Copyright (C) 2014 HarpyWar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "adbanner.h" #include #include #include #include #include #include #include #include "i18n.h" #include "json/json.hpp" #include "common/bn_type.h" #include "common/tag.h" #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "connection.h" #include "common/setup_after.h" using json = nlohmann::json; namespace pvpgn { namespace bnetd { AdBannerSelector AdBannerList; std::default_random_engine engine{}; bool AdBannerSelector::is_loaded() const noexcept { return this->m_loaded; } void AdBannerSelector::load(const std::string& filename) { if (this->is_loaded()) throw std::runtime_error("An ad banner file is already loaded"); std::ifstream file_stream(filename, std::ios::in); if (!file_stream.is_open()) throw std::runtime_error("Could not open " + filename); json j; try { file_stream >> j; } catch (const std::invalid_argument& e) { throw std::runtime_error("Invalid JSON file: " + std::string(e.what())); } const std::map extension_map = { { "pcx", EXTENSIONTAG_PCX }, { "smk", EXTENSIONTAG_SMK }, { "mng", EXTENSIONTAG_MNG }, { "png", EXTENSIONTAG_MNG } }; for (const auto& ad : j["ads"]) { try { if (ad["filename"].get().find('/') != std::string::npos || ad["filename"].get().find('\\') != std::string::npos) throw std::runtime_error("Paths are not supported (" + ad["filename"].get() + ")"); const std::size_t ext = ad["filename"].get().find('.'); if (ext == std::string::npos) throw std::runtime_error("Filename must contain an extension"); const std::string file_extension(ad["filename"].get().substr(ext + 1)); bn_int extensiontag = {}; for (const auto& m : extension_map) { if (strcasecmp(file_extension.c_str(), m.first.c_str()) == 0) { bn_int_tag_set(&extensiontag, m.second.c_str()); break; } } if (extensiontag == 0) { throw std::runtime_error("Unsupported file extension (" + file_extension + ")"); } t_clienttag ctag = 0; if (strcasecmp(ad["client"].get().c_str(), "NULL") != 0) { ctag = clienttag_str_to_uint(ad["client"].get().c_str()); if (std::strcmp(clienttag_uint_to_str(ctag), "UNKN") == 0) throw std::runtime_error("Unknown client tag (" + ad["client"].get() + ")"); } t_gamelang ltag = 0; if (strcasecmp(ad["lang"].get().c_str(), "NULL") != 0) { ltag = tag_str_to_uint(ad["lang"].get().c_str()); bool found; language_find_by_tag(ltag, found); if (!found) throw std::runtime_error("Unknown language (" + ad["lang"].get() + ")"); } this->m_banners.push_back(AdBanner(m_banners.size()+1, extensiontag, ad["filename"].get(), ad["url"].get(), ctag, ltag)); } catch (const std::runtime_error& e) { eventlog(eventlog_level_error, __FUNCTION__, "Could not load ad: {}", e.what()); continue; } } this->m_loaded = true; } void AdBannerSelector::unload() noexcept { this->m_banners.clear(); this->m_loaded = false; } std::size_t AdBannerSelector::size() const noexcept { return this->m_banners.size(); } const AdBanner* const AdBannerSelector::pick(t_clienttag client_tag, t_gamelang client_lang, std::size_t prev_ad_id) { switch (this->m_banners.size()) { case 0: return nullptr; case 1: return &this->m_banners.at(0); default: { bn_int ext; std::vector candidates = {}; std::for_each(this->m_banners.begin(), this->m_banners.end(), [client_tag, client_lang, &candidates, &ext](const AdBanner& ad) -> void { if ((ad.get_client() == client_tag || ad.get_client() == 0) && (ad.get_language() == client_lang || ad.get_language() == 0)) { bn_int_set(&ext, ad.get_extension_tag()); // Warcraft 3 if (client_tag == CLIENTTAG_WAR3XP_UINT || client_tag == CLIENTTAG_WARCRAFT3_UINT) { // ignore all formats except MNG for Warcraft 3, cause it's only one supported format if (bn_int_tag_eq(ext, EXTENSIONTAG_MNG) != 0) { return; } } // Starcraft, Warcraft 2, Diablo, Diablo 2 else { // ignore MNG, cause it's not supported format for other games if (bn_int_tag_eq(ext, EXTENSIONTAG_MNG) == 0) { return; } } candidates.push_back(ad.get_id()); } }); if (candidates.empty()) { return nullptr; } const AdBanner* ad; unsigned int idx = 0; if (client_tag == CLIENTTAG_WAR3XP_UINT || client_tag == CLIENTTAG_WARCRAFT3_UINT) { // Warcraft 3 client always send prev_ad_id = 0, because of that we use random selection instead of sequence std::uniform_int_distribution random(0, candidates.size() - 1); idx = random(engine); } else { // if prev_id in middle (for first and last idx=0) if (prev_ad_id != 0 && prev_ad_id != candidates.at(candidates.size() - 1)) { bool prev_found = false; for (unsigned int i = 0; i < candidates.size(); i++) { if (prev_found) { idx = i; break; } if (prev_ad_id == candidates.at(i)) { prev_found = true; } } } } ad = this->find(client_tag, client_lang, candidates.at(idx)); /* char lang[5] = {}; eventlog(eventlog_level_trace, __FUNCTION__, "Pick ad idx={} candidates={} id=0x{:08} prev_id=0x{:08} filename=\"{}\" link=\"{}\" client=\"{}\" lang=\"{}\"", idx, candidates.size(), ad->get_id(), prev_ad_id, ad->get_filename(), ad->get_url(), client_tag ? clienttag_uint_to_str(client_tag) : "NULL", client_lang ? tag_uint_to_str(lang, client_lang) : "NULL"); */ return ad; } } } const AdBanner* const AdBannerSelector::find(t_clienttag client_tag, t_gamelang client_lang, std::size_t ad_id) { auto result = std::find_if(m_banners.begin(), m_banners.end(), [client_tag, client_lang, ad_id](const AdBanner& a) -> bool { return (a.get_client() == client_tag || a.get_client() == 0) && (a.get_language() == client_lang || a.get_language() == 0) && a.get_id() == ad_id; }); return result != m_banners.end() ? &*result : nullptr; } /***************************************************************************************************/ AdBanner::AdBanner(std::size_t id, bn_int extensiontag, const std::string& filename, const std::string& url, t_clienttag clienttag, t_gamelang language) : m_id(id), m_extensiontag(bn_int_get(extensiontag)), m_filename(filename), m_url(url), m_client(clienttag), m_language(language) { char lang[5] = {}; eventlog(eventlog_level_info, __FUNCTION__, "Created ad id=0x{:08} filename=\"{}\" link=\"{}\" client=\"{}\" lang=\"{}\"", id, filename.c_str(), url.c_str(), clienttag ? clienttag_uint_to_str(clienttag) : "NULL", language ? tag_uint_to_str(lang, language) : "NULL"); } bool AdBanner::is_empty() const { return this->m_client == 0 && this->m_extensiontag == 0 && this->m_filename.empty() && this->m_id == 0 && this->m_language == 0 && this->m_url.empty(); } /* id starts from 1 */ std::size_t AdBanner::get_id() const noexcept { return this->m_id; } std::string AdBanner::get_filename() const noexcept { return this->m_filename; } unsigned int AdBanner::get_extension_tag() const noexcept { return this->m_extensiontag; } std::string AdBanner::get_url() const noexcept { return this->m_url; } t_clienttag AdBanner::get_client() const noexcept { return this->m_client; } t_gamelang AdBanner::get_language() const noexcept { return this->m_language; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/adbanner.h000066400000000000000000000051051462640461400200070ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ADBANNER_H #define INCLUDED_ADBANNER_H #include #include #include #include #include "common/tag.h" #include "common/bn_type.h" namespace pvpgn { namespace bnetd { /** * Forward Declarations */ class AdBanner; /***************************************************************************************************/ class AdBannerSelector { public: AdBannerSelector() = default; ~AdBannerSelector() = default; bool is_loaded() const noexcept; void load(const std::string& filename); void unload() noexcept; std::size_t size() const noexcept; const AdBanner* const pick(t_clienttag client_tag, t_gamelang client_lang, std::size_t prev_ad_id); const AdBanner* const find(t_clienttag client_tag, t_gamelang client_lang, std::size_t ad_id); private: bool m_loaded = false; std::vector m_banners; }; extern AdBannerSelector AdBannerList; /***************************************************************************************************/ class AdBanner { public: AdBanner() = delete; bool is_empty() const; std::size_t get_id() const noexcept; std::string get_filename() const noexcept; unsigned int get_extension_tag() const noexcept; std::string get_url() const noexcept; t_clienttag get_client() const noexcept; t_gamelang get_language() const noexcept; private: friend void AdBannerSelector::load(const std::string& filename); AdBanner(std::size_t id, bn_int extensiontag, const std::string& filename, const std::string& url, t_clienttag clienttag, t_gamelang language); const std::size_t m_id; const std::string m_filename; const unsigned int m_extensiontag; const std::string m_url; const t_clienttag m_client; const t_gamelang m_language; }; } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/alias_command.cpp000066400000000000000000000345521462640461400213670ustar00rootroot00000000000000/* * Copyright (C) 2000 Onlyer (onlyer@263.net) * Copyright (C) 2002 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define ALIAS_COMMAND_INTERNAL_ACCESS #include "common/setup_before.h" #include #include #include #include "alias_command.h" #include "compat/strcasecmp.h" #include "common/field_sizes.h" #include "common/util.h" #include "common/eventlog.h" #include "common/list.h" #include "common/xalloc.h" #include "message.h" #include "connection.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_list * aliaslist_head = NULL; #define MAX_ALIAS_LEN 32 static int list_aliases(t_connection * c); static char * replace_args(char const * in, unsigned int * offsets, int numargs, char const * text); static int do_alias(t_connection * c, char const * cmd, char const * args); static int list_aliases(t_connection * c) { t_elem const * elem1; t_elem const * elem2; t_alias const * alias; t_output * output; char temp[MAX_MESSAGE_LEN]; message_send_text(c, message_type_info, c, localize(c, "Alias list:")); LIST_TRAVERSE_CONST(aliaslist_head, elem1) { if (!(alias = (t_alias*)elem_get_data(elem1))) continue; std::sprintf(temp, "@%.128s", alias->alias); message_send_text(c, message_type_info, c, temp); LIST_TRAVERSE_CONST(alias->output, elem2) { if (!(output = (t_output*)elem_get_data(elem2))) continue; /* * FIXME: need a more user-friendly way to express this... maybe * add a help line to the file format? */ if (output->min == -1) std::sprintf(temp, "[*]%.128s", output->line); else if (output->max == -1) std::sprintf(temp, "[%d+]%.128s", output->min, output->line); else if (output->min == output->max) std::sprintf(temp, "[%d]%.128s", output->min, output->line); else std::sprintf(temp, "[%d-%d]%.128s", output->min, output->max, output->line); message_send_text(c, message_type_info, c, temp); } } return 0; } static char * replace_args(char const * in, unsigned int * offsets, int numargs, char const * text) { char * out; unsigned int inpos; unsigned int outpos; unsigned int off1; unsigned int off2; unsigned int size; off1 = off2 = 0; out = xstrdup(in); size = std::strlen(out); size_t textlen = std::strlen(text); for (inpos = outpos = 0; inpos < std::strlen(in); inpos++) { if (in[inpos] != '$') { out[outpos] = in[inpos]; outpos++; continue; } inpos++; if (in[inpos] == '*') { off1 = offsets[0]; off2 = textlen; } else if (in[inpos] == '{') { int arg1, arg2; char symbol; if (std::sscanf(&in[inpos], "{%d-%d}", &arg1, &arg2) != 2) { if (std::sscanf(&in[inpos], "{%d%c", &arg2, &symbol) != 2) { while (in[inpos] != '\0' && in[inpos] != '}') inpos++; continue; } else { if (symbol == '}') { if (arg2 < 0) { arg1 = 0; arg2 = -arg2; } else { arg1 = arg2; } } else if (symbol == '-') { arg1 = arg2; arg2 = numargs - 1; } else { while (in[inpos] != '\0' && in[inpos] != '}') inpos++; continue; } } } if (arg2 >= numargs) arg2 = numargs - 1; if (arg1 > arg2) { while (in[inpos] != '\0' && in[inpos] != '}') inpos++; continue; } off1 = offsets[arg1]; if (arg2 + 1 == numargs) off2 = textlen; else off2 = offsets[arg2 + 1] - 1; while (in[inpos] != '\0' && in[inpos] != '}') inpos++; } else if (std::isdigit((int)in[inpos])) { int arg; if (in[inpos] == '0') arg = 0; else if (in[inpos] == '1') arg = 1; else if (in[inpos] == '2') arg = 2; else if (in[inpos] == '3') arg = 3; else if (in[inpos] == '4') arg = 4; else if (in[inpos] == '5') arg = 5; else if (in[inpos] == '6') arg = 6; else if (in[inpos] == '7') arg = 7; else if (in[inpos] == '8') arg = 8; else arg = 9; if (arg >= numargs) continue; for (off1 = off2 = offsets[arg]; text[off2] != '\0' && text[off2] != ' ' && text[off2] != '\t'; off2++); } { char * newout; newout = (char*)xmalloc(size + (off2 - off1) + 1); /* curr + new + nul */ size = size + (off2 - off1) + 1; std::memmove(newout, out, outpos); xfree(out); out = newout; while (off1 < off2) out[outpos++] = text[off1++]; } } out[outpos] = '\0'; return out; } int count_args(char const * text) { int args = 1; char * pointer = (char *)text; int not_ws = 1; for (; *pointer != '\0'; pointer++) { if ((not_ws) && (*pointer == ' ')) not_ws = 0; if ((not_ws == 0) && (*pointer != ' ')) { not_ws = 1; args++; } } return args; } void get_offsets(char const * text, unsigned int * offsets) { char * pointer = (char *)text; int counter = 1; int not_ws = 1; offsets[0] = 0; for (; *pointer != '\0'; pointer++) { if ((not_ws) && (*pointer == ' ')) not_ws = 0; if ((not_ws == 0) && (*pointer != ' ')) { not_ws = 1; offsets[counter] = (unsigned int)(pointer - text); counter++; } } } static int do_alias(t_connection * c, char const * cmd, char const * text) { t_elem const * elem1; t_elem const * elem2; t_alias const * alias; t_output * output; unsigned int * offsets; int numargs; int match = -1; numargs = count_args(text) - 1; offsets = (unsigned int*)xmalloc(sizeof(unsigned int)*(numargs + 1)); get_offsets(text, offsets); LIST_TRAVERSE_CONST(aliaslist_head, elem1) { if (!(alias = (t_alias*)elem_get_data(elem1))) continue; if (std::strstr(alias->alias, cmd) == NULL) continue; LIST_TRAVERSE_CONST(alias->output, elem2) { if (!(output = (t_output*)elem_get_data(elem2))) continue; if (!output->line) continue; if ((output->min == -1) || ((output->min <= numargs) && (output->max == -1)) || ((output->min <= numargs) && (output->max >= numargs))) //TODO: initialize offsets { char * msgtmp; char * tmp2; const char * cmd = "%C"; match = 1; if ((msgtmp = replace_args(output->line, offsets, numargs + 1, text))) { if (msgtmp[0] != '\0') { if ((msgtmp[0] == '/') && (msgtmp[1] != '/')) // to make sure we don't get endless aliasing loop { tmp2 = (char*)xmalloc(std::strlen(msgtmp) + 3); std::sprintf(tmp2, "%s%s", cmd, msgtmp); xfree((void *)msgtmp); msgtmp = tmp2; } if (std::strlen(msgtmp) > MAX_MESSAGE_LEN) { msgtmp[MAX_MESSAGE_LEN] = '\0'; eventlog(eventlog_level_info, __FUNCTION__, "message line after alias expansion was too long, truncating it"); } message_send_formatted(c, msgtmp); } xfree((void *)msgtmp); /* avoid warning */ } else eventlog(eventlog_level_error, __FUNCTION__, "could not perform argument replacement"); } } } xfree(offsets); return match; } extern int aliasfile_load(char const * filename) { std::FILE * afp; char * buff; char * temp; unsigned int line; unsigned int pos; int inalias; t_alias * alias = NULL; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(afp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to open alias file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } aliaslist_head = list_create(); inalias = 0; for (line = 1; (buff = file_get_line(afp)); line++) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if (!(temp = std::strrchr(buff, '"'))) /* FIXME: assumes comments don't contain " */ temp = buff; if ((temp = std::strrchr(temp, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } switch (inalias) { case 0: if (buff[pos] != '@') /* not start of alias */ { eventlog(eventlog_level_error, __FUNCTION__, "expected start of alias stanza on line {} of alias file \"{}\" but found \"{}\"", line, filename, &buff[pos]); break; } inalias = 1; break; case 1: { if (buff[pos] == '\0') break; inalias = 2; alias = (t_alias*)xmalloc(sizeof(t_alias)); alias->output = 0; alias->alias = xstrdup(&buff[pos]); } break; case 2: { char * dummy; char * out = NULL; int min, max; t_output * output; min = max = 0; if (buff[pos] != '[') { eventlog(eventlog_level_error, __FUNCTION__, "expected output entry on line {} of alias file \"{}\" but found \"{}\"", line, filename, &buff[pos]); break; } if ((dummy = std::strchr(&buff[pos], ']'))) { if (dummy[1] != '\0') out = xstrdup(&dummy[1]); } if (buff[pos + 1] == '*') { min = max = -1; } else if (buff[pos + 1] == '+') { min = 1; max = -1; } else if (std::sscanf(&buff[pos], "[%i", &min) == 1) { if (*(dummy - 1) == '+') max = -1; else max = min; } else { eventlog(eventlog_level_error, __FUNCTION__, "error parsing min/max value for alias output"); } if (out != NULL) { output = (t_output*)xmalloc(sizeof(t_output)); output->min = min; output->max = max; output->line = out; alias->output = list_create(); list_append_data(alias->output, output); inalias = 3; } } break; case 3: { if (buff[pos] == '@') { inalias = 1; if (alias != NULL) list_append_data(aliaslist_head, alias); alias = NULL; break; } else if (buff[pos] == '[') { char * dummy; char * out = NULL; int min, max; t_output * output; min = max = 0; if ((dummy = std::strchr(&buff[pos], ']'))) { if (dummy[1] != '\0') out = xstrdup(&dummy[1]); } if (buff[pos + 1] == '*') { min = max = -1; } else if (buff[pos + 1] == '+') { min = 1; max = -1; } else if (std::sscanf(&buff[pos], "[%i", &min) == 1) { if (*(dummy - 1) == '+') max = -1; else max = min; } else { eventlog(eventlog_level_error, __FUNCTION__, "error parsing min/max value for alias output"); } if (out != NULL) { output = (t_output*)xmalloc(sizeof(t_output)); output->min = min; output->max = max; output->line = out; list_append_data(alias->output, output); } } else eventlog(eventlog_level_error, __FUNCTION__, "expected output entry or next alias stanza on line {} of file \"{}\"i but found \"{}\"", line, filename, &buff[pos]); break; } } } if (alias != NULL) list_append_data(aliaslist_head, alias); file_get_line(NULL); // clear file_get_line buffer std::fclose(afp); eventlog(eventlog_level_info, __FUNCTION__, "done loading aliases"); return 0; } extern int aliasfile_unload(void) { t_elem * elem1; t_elem * elem2; t_alias const * alias; t_output * output; if (aliaslist_head) { LIST_TRAVERSE(aliaslist_head, elem1) { if (!(alias = (t_alias*)elem_get_data(elem1))) /* should not happen */ { eventlog(eventlog_level_error, __FUNCTION__, "alias list contains NULL item"); continue; } if (list_remove_elem(aliaslist_head, &elem1) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove alias"); continue; } if (alias->output) { LIST_TRAVERSE(alias->output, elem2) { if (!(output = (t_output*)elem_get_data(elem2))) { eventlog(eventlog_level_error, __FUNCTION__, "output list contains NULL item"); continue; } if (list_remove_elem(alias->output, &elem2) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove output"); continue; } xfree((void *)output->line); /* avoid warning */ xfree((void *)output); } list_destroy(alias->output); } if (alias->alias) xfree((void *)alias->alias); xfree((void *)alias); } if (list_destroy(aliaslist_head) < 0) return -1; aliaslist_head = NULL; } return 0; } extern int handle_alias_command(t_connection * c, char const * text) { unsigned int i, j; char cmd[MAX_COMMAND_LEN]; for (i = j = 0; text[i] != ' ' && text[i] != '\0'; i++) /* get command */ if (j < sizeof(cmd)-1) cmd[j++] = text[i]; cmd[j] = '\0'; if (cmd[2] == '\0') return list_aliases(c); if (do_alias(c, cmd, text) < 0) { message_send_text(c, message_type_info, c, localize(c, "No such alias. Use // to show the list.")); return -1; } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/alias_command.h000066400000000000000000000033051462640461400210240ustar00rootroot00000000000000/* * Copyright (C) 2000 Onlyer (onlyer@263.net) * Copyright (C) 2002 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ALIAS_COMMAND_TYPES #define INCLUDED_ALIAS_COMMAND_TYPES #ifdef ALIAS_COMMAND_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES # include "common/list.h" #else # define JUST_NEED_TYPES # include "common/list.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { namespace bnetd { typedef struct { char const * line; int min; int max; } t_output; typedef struct { char const * alias; t_list * output; /* of t_output * */ } t_alias; } } #endif #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ALIAS_COMMAND_PROTOS #define INCLUDED_ALIAS_COMMAND_PROTOS #define JUST_NEED_TYPES #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int aliasfile_load(char const * filename); extern int aliasfile_unload(void); extern int handle_alias_command(t_connection * c, char const * text); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame.cpp000066400000000000000000002105661462640461400203660ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "anongame.h" #include #include #include #include "compat/strdup.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/tag.h" #include "common/queue.h" #include "common/bn_type.h" #include "common/list.h" #include "common/addr.h" #include "common/xalloc.h" #include "common/trans.h" #include "team.h" #include "account.h" #include "account_wrap.h" #include "connection.h" #include "prefs.h" #include "versioncheck.h" #include "tournament.h" #include "timer.h" #include "ladder.h" #include "server.h" #include "anongame_maplists.h" #include "anongame_gameresult.h" #include "common/setup_after.h" #define MAX_LEVEL 100 namespace pvpgn { namespace bnetd { /* [quetzal] 20020827 - this one get modified by anongame_queue player when there're enough * players and map has been chosen based on their preferences. otherwise its NULL */ static const char *mapname = NULL; static int players[ANONGAME_TYPES] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static t_connection *player[ANONGAME_TYPES][ANONGAME_MAX_GAMECOUNT]; /* [quetzal] 20020815 - queue to hold matching players */ static t_list *matchlists[ANONGAME_TYPES][MAX_LEVEL]; long average_anongame_search_time = 30; unsigned int anongame_search_count = 0; /**********************************************************************************/ static t_connection *_connlist_find_connection_by_uid(int uid); static int _anongame_gametype_to_queue(int type, int gametype); static int _anongame_level_by_queue(t_connection * c, int queue); static const char * _get_map_from_prefs(int queue, std::uint32_t cur_prefs, t_clienttag clienttag); static unsigned int _anongame_get_gametype_tab(int queue); static int _anongame_totalplayers(int queue); static int _anongame_totalteams(int queue); static int _handle_anongame_search(t_connection * c, t_packet const *packet); static int _anongame_queue(t_connection * c, int queue, std::uint32_t map_prefs); static int _anongame_compare_level(void const *a, void const *b); static int _anongame_order_queue(int queue); static int _anongame_match(t_connection * c, int queue); static int _anongame_search_found(int queue); /**********************************************************************************/ static t_connection *_connlist_find_connection_by_uid(int uid) { return connlist_find_connection_by_account(accountlist_find_account_by_uid(uid)); } /**********/ static char const *_anongame_queue_to_string(int queue) { switch (queue) { case ANONGAME_TYPE_1V1: return "PG 1v1"; case ANONGAME_TYPE_2V2: return "PG 2v2"; case ANONGAME_TYPE_3V3: return "PG 3v3"; case ANONGAME_TYPE_4V4: return "PG 4v4"; case ANONGAME_TYPE_SMALL_FFA: return "PG SFFA"; case ANONGAME_TYPE_AT_2V2: return "AT 2v2"; case ANONGAME_TYPE_TEAM_FFA: return "AT TFFA"; case ANONGAME_TYPE_AT_3V3: return "AT 3v3"; case ANONGAME_TYPE_AT_4V4: return "AT 4v4"; case ANONGAME_TYPE_TY: return "TOURNEY"; case ANONGAME_TYPE_5V5: return "PG 5v5"; case ANONGAME_TYPE_6V6: return "PG 6v6"; case ANONGAME_TYPE_2V2V2: return "PG 2v2v2"; case ANONGAME_TYPE_3V3V3: return "PG 3v3v3"; case ANONGAME_TYPE_4V4V4: return "PG 4v4v4"; case ANONGAME_TYPE_2V2V2V2: return "PG 2v2v2v2"; case ANONGAME_TYPE_3V3V3V3: return "PG 3v3v3v3"; case ANONGAME_TYPE_AT_2V2V2: return "AT 2v2v2"; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid queue number {}", queue); return "error"; } } static int _anongame_gametype_to_queue(int type, int gametype) { switch (type) { case 0: /* PG */ switch (gametype) { case 0: return ANONGAME_TYPE_1V1; case 1: return ANONGAME_TYPE_2V2; case 2: return ANONGAME_TYPE_3V3; case 3: return ANONGAME_TYPE_4V4; case 4: return ANONGAME_TYPE_SMALL_FFA; case 5: return ANONGAME_TYPE_5V5; case 6: return ANONGAME_TYPE_6V6; case 7: return ANONGAME_TYPE_2V2V2; case 8: return ANONGAME_TYPE_3V3V3; case 9: return ANONGAME_TYPE_4V4V4; case 10: return ANONGAME_TYPE_2V2V2V2; case 11: return ANONGAME_TYPE_3V3V3V3; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid PG game type: {}", gametype); return -1; } case 1: /* AT */ switch (gametype) { case 0: return ANONGAME_TYPE_AT_2V2; case 2: return ANONGAME_TYPE_AT_3V3; case 3: return ANONGAME_TYPE_AT_4V4; case 4: return ANONGAME_TYPE_AT_2V2V2; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid AT game type: {}", gametype); return -1; } case 2: /* TY */ return ANONGAME_TYPE_TY; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid type: {}", type); return -1; } } static int _anongame_level_by_queue(t_connection * c, int queue) { t_clienttag ct = conn_get_clienttag(c); switch (queue) { case ANONGAME_TYPE_1V1: return account_get_ladder_level(conn_get_account(c), ct, ladder_id_solo); case ANONGAME_TYPE_2V2: case ANONGAME_TYPE_3V3: case ANONGAME_TYPE_4V4: case ANONGAME_TYPE_5V5: case ANONGAME_TYPE_6V6: case ANONGAME_TYPE_2V2V2: case ANONGAME_TYPE_3V3V3: case ANONGAME_TYPE_4V4V4: case ANONGAME_TYPE_2V2V2V2: case ANONGAME_TYPE_3V3V3V3: return account_get_ladder_level(conn_get_account(c), ct, ladder_id_ffa); case ANONGAME_TYPE_SMALL_FFA: case ANONGAME_TYPE_TEAM_FFA: return account_get_ladder_level(conn_get_account(c), ct, ladder_id_ffa); case ANONGAME_TYPE_AT_2V2: case ANONGAME_TYPE_AT_3V3: case ANONGAME_TYPE_AT_4V4: case ANONGAME_TYPE_AT_2V2V2: return 0; case ANONGAME_TYPE_TY: /* set to ((wins * 3) + ties - losses) ie. prelim score */ return tournament_get_player_score(conn_get_account(c)); default: eventlog(eventlog_level_error, __FUNCTION__, "unknown queue: {}", queue); return -1; } } static const char * _get_map_from_prefs(int queue, std::uint32_t cur_prefs, t_clienttag clienttag) { int i, j = 0; const char *default_map, *selected; char *res_maps[32]; char clienttag_str[5]; switch (clienttag) { case CLIENTTAG_WARCRAFT3_UINT: default_map = "Maps\\(8)PlainsOfSnow.w3m"; break; case CLIENTTAG_WAR3XP_UINT: default_map = "Maps\\(8)PlainsOfSnow.w3m"; break; default: ERROR1("invalid clienttag: {}", tag_uint_to_str(clienttag_str, clienttag)); return "Maps\\(8)PlainsOfSnow.w3m"; } for (i = 0; i < 32; i++) res_maps[i] = NULL; for (i = 0; i < 32; i++) { if (cur_prefs & 1) res_maps[j++] = maplists_get_map(queue, clienttag, i + 1); cur_prefs >>= 1; } i = std::rand() % j; if (res_maps[i]) selected = res_maps[i]; else selected = default_map; eventlog(eventlog_level_trace, __FUNCTION__, "got map {} from prefs", selected); return selected; } static unsigned int _anongame_get_gametype_tab(int queue) { /* dizzy: this changed in 1.05 */ switch (queue) { case ANONGAME_TYPE_1V1: return SERVER_ANONGAME_SOLO_STR; case ANONGAME_TYPE_2V2: case ANONGAME_TYPE_3V3: case ANONGAME_TYPE_4V4: case ANONGAME_TYPE_5V5: case ANONGAME_TYPE_6V6: case ANONGAME_TYPE_2V2V2: case ANONGAME_TYPE_3V3V3: case ANONGAME_TYPE_4V4V4: case ANONGAME_TYPE_2V2V2V2: case ANONGAME_TYPE_3V3V3V3: return SERVER_ANONGAME_TEAM_STR; case ANONGAME_TYPE_SMALL_FFA: return SERVER_ANONGAME_SFFA_STR; case ANONGAME_TYPE_TEAM_FFA: return 0; /* Team FFA is no longer supported */ case ANONGAME_TYPE_AT_2V2: return SERVER_ANONGAME_AT2v2_STR; case ANONGAME_TYPE_AT_3V3: return SERVER_ANONGAME_AT3v3_STR; case ANONGAME_TYPE_AT_4V4: return SERVER_ANONGAME_AT4v4_STR; case ANONGAME_TYPE_AT_2V2V2: return SERVER_ANONGAME_AT2v2_STR; /* fixme */ case ANONGAME_TYPE_TY: return SERVER_ANONGAME_TY_STR; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid queue ({})", queue); return 0; } } static int _anongame_totalplayers(int queue) { switch (queue) { case ANONGAME_TYPE_1V1: return 2; case ANONGAME_TYPE_2V2: case ANONGAME_TYPE_AT_2V2: case ANONGAME_TYPE_SMALL_FFA: /* fixme: total players not always 4 */ return 4; case ANONGAME_TYPE_3V3: case ANONGAME_TYPE_AT_3V3: case ANONGAME_TYPE_2V2V2: case ANONGAME_TYPE_AT_2V2V2: return 6; case ANONGAME_TYPE_4V4: case ANONGAME_TYPE_AT_4V4: case ANONGAME_TYPE_TEAM_FFA: case ANONGAME_TYPE_2V2V2V2: return 8; case ANONGAME_TYPE_3V3V3: return 9; case ANONGAME_TYPE_5V5: return 10; case ANONGAME_TYPE_6V6: case ANONGAME_TYPE_4V4V4: case ANONGAME_TYPE_3V3V3V3: return 12; case ANONGAME_TYPE_TY: return tournament_get_totalplayers(); default: eventlog(eventlog_level_error, __FUNCTION__, "unknown queue: {}", queue); return 0; } } static int _anongame_totalteams(int queue) { /* dont forget to change this if you make some game type with more teams */ #define ANONGAME_MAX_TEAMS 4 switch (queue) { case ANONGAME_TYPE_1V1: case ANONGAME_TYPE_SMALL_FFA: return 0; case ANONGAME_TYPE_2V2: case ANONGAME_TYPE_3V3: case ANONGAME_TYPE_4V4: case ANONGAME_TYPE_5V5: case ANONGAME_TYPE_6V6: case ANONGAME_TYPE_AT_2V2: case ANONGAME_TYPE_AT_3V3: case ANONGAME_TYPE_AT_4V4: return 2; case ANONGAME_TYPE_2V2V2: case ANONGAME_TYPE_3V3V3: case ANONGAME_TYPE_4V4V4: case ANONGAME_TYPE_AT_2V2V2: return 3; case ANONGAME_TYPE_TEAM_FFA: /* not even used */ case ANONGAME_TYPE_2V2V2V2: case ANONGAME_TYPE_3V3V3V3: return 4; case ANONGAME_TYPE_TY: return 2; /* fixme: does not support 2v2v2 - tournament_get_totalteams() */ default: eventlog(eventlog_level_error, __FUNCTION__, "unknown queue: {}", queue); return 0; } } /**********/ static int _handle_anongame_search(t_connection * c, t_packet const *packet) { int i, j, temp, set = 1; t_packet *rpacket; t_connection *tc[6]; t_anongame *a, *ta; std::uint8_t teamsize = 0; std::uint8_t option = bn_byte_get(packet->u.client_findanongame.option); if (!(a = conn_get_anongame(c))) { if (!(a = conn_create_anongame(c))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] conn_create_anongame failed", conn_get_socket(c)); return -1; } } conn_set_anongame_search_starttime(c, now); switch (option) { case CLIENT_FINDANONGAME_AT_INVITER_SEARCH: a->count = bn_int_get(packet->u.client_findanongame_at_inv.count); a->id = bn_int_get(packet->u.client_findanongame_at_inv.id); a->tid = bn_int_get(packet->u.client_findanongame_at_inv.tid); a->race = bn_int_get(packet->u.client_findanongame_at_inv.race); a->map_prefs = bn_int_get(packet->u.client_findanongame_at_inv.map_prefs); a->type = bn_byte_get(packet->u.client_findanongame_at_inv.type); a->gametype = bn_byte_get(packet->u.client_findanongame_at_inv.gametype); teamsize = bn_byte_get(packet->u.client_findanongame_at_inv.teamsize); break; case CLIENT_FINDANONGAME_AT_SEARCH: a->count = bn_int_get(packet->u.client_findanongame_at.count); a->id = bn_int_get(packet->u.client_findanongame_at.id); a->tid = bn_int_get(packet->u.client_findanongame_at.tid); a->race = bn_int_get(packet->u.client_findanongame_at.race); teamsize = bn_byte_get(packet->u.client_findanongame_at.teamsize); break; case CLIENT_FINDANONGAME_SEARCH: a->count = bn_int_get(packet->u.client_findanongame.count); a->id = bn_int_get(packet->u.client_findanongame.id); a->race = bn_int_get(packet->u.client_findanongame.race); a->map_prefs = bn_int_get(packet->u.client_findanongame.map_prefs); a->type = bn_byte_get(packet->u.client_findanongame.type); a->gametype = bn_byte_get(packet->u.client_findanongame.gametype); break; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid search option ({})", option); return -1; } if (option != CLIENT_FINDANONGAME_AT_SEARCH) if ((a->queue = _anongame_gametype_to_queue(a->type, a->gametype)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "invalid queue: {}", a->queue); return -1; } account_set_w3pgrace(conn_get_account(c), conn_get_clienttag(c), a->race); /* send search reply to client */ if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_anongame_search_reply)); packet_set_type(rpacket, SERVER_ANONGAME_SEARCH_REPLY); bn_byte_set(&rpacket->u.server_anongame_search_reply.option, SERVER_FINDANONGAME_SEARCH); bn_int_set(&rpacket->u.server_anongame_search_reply.count, a->count); bn_int_set(&rpacket->u.server_anongame_search_reply.reply, 0); temp = (int)average_anongame_search_time; packet_append_data(rpacket, &temp, 2); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); /* end search reply */ switch (option) { case CLIENT_FINDANONGAME_AT_INVITER_SEARCH: for (i = 0; i < teamsize; i++) { /* assign player conns to tc[] array */ if (!(tc[i] = _connlist_find_connection_by_uid(bn_int_get(packet->u.client_findanongame_at_inv.info[i])))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(tc[i])); return -1; } } for (i = 0; i < teamsize; i++) { /* assign info from inviter to other team players */ if (!(ta = conn_get_anongame(tc[i]))) { if (!(ta = conn_create_anongame(tc[i]))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] conn_create_anongame failed", conn_get_socket(tc[i])); return -1; } } for (j = 0; j < teamsize; j++) /* add each players conn to each anongame struct */ ta->tc[j] = tc[j]; ta->type = a->type; ta->gametype = a->gametype; ta->queue = a->queue; ta->map_prefs = a->map_prefs; if (ta->tid != a->tid) set = 0; } if (!set) /* check if search packet has been recieved from each team member */ return 0; break; case CLIENT_FINDANONGAME_AT_SEARCH: for (i = 0; i < teamsize; i++) { /* assign player conns to tc[] array */ if (!(tc[i] = _connlist_find_connection_by_uid(bn_int_get(packet->u.client_findanongame_at.info[i])))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(tc[i])); return -1; } } for (i = 0; i < teamsize; i++) { /* check if search packet has been recieved from each team member */ if (!(ta = conn_get_anongame(tc[i]))) return 0; if (ta->tid != a->tid) return 0; } break; case CLIENT_FINDANONGAME_SEARCH: tc[0] = c; a->tc[0] = c; break; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid search option ({})", option); return -1; } if (_anongame_queue(tc[0], a->queue, a->map_prefs) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "queue failed"); return -1; } _anongame_match(c, a->queue); /* if enough players are queued send found packet */ if (players[a->queue] == _anongame_totalplayers(a->queue)) if (_anongame_search_found(a->queue) < 0) return -1; return 0; } static int _anongame_queue(t_connection * c, int queue, std::uint32_t map_prefs) { int level; t_matchdata *md; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); } if (queue >= ANONGAME_TYPES) { eventlog(eventlog_level_error, __FUNCTION__, "unknown queue: {}", queue); return -1; } level = _anongame_level_by_queue(c, queue); if (!matchlists[queue][level]) matchlists[queue][level] = list_create(); md = (t_matchdata*)xmalloc(sizeof(t_matchdata)); md->c = c; md->map_prefs = map_prefs; md->versiontag = conn_get_versioncheck(c) ? conn_get_versioncheck(c)->get_version_tag().c_str() : nullptr; list_append_data(matchlists[queue][level], md); return 0; } static int _anongame_compare_level(void const *a, void const *b) { t_connection *ca = *(t_connection * const *)a; t_connection *cb = *(t_connection * const *)b; int level_a = _anongame_level_by_queue(ca, anongame_get_queue(conn_get_anongame(ca))); int level_b = _anongame_level_by_queue(cb, anongame_get_queue(conn_get_anongame(cb))); return (level_a > level_b) ? -1 : ((level_a < level_b) ? 1 : 0); } static int _anongame_order_queue(int queue) { if (_anongame_totalteams(queue) != 0 && !anongame_arranged(queue)) { /* no need to reorder 1v1, sffa, or AT queues */ int i, j; t_connection *temp; int level[ANONGAME_MAX_TEAMS]; int teams = _anongame_totalteams(queue); /* number of teams */ int ppt = (teams > 0) ? (players[queue] / teams) : 0; /* players per team */ for (i = 0; i < ANONGAME_MAX_TEAMS; i++) level[i] = 0; for (i = 0; i < ppt - 1; i++) { /* loop through the number of players per team */ for (j = 0; j < teams; j++) { level[j] = level[j] + _anongame_level_by_queue(player[queue][i * ppt + j], queue); } if (teams == 2) { /* 1 >= 2 */ if (level[i * teams] >= level[i * teams + 1]) { temp = player[queue][(i + 1) * teams]; player[queue][(i + 1) * teams] = player[queue][(i + 1) * teams + 1]; player[queue][(i + 1) * teams + 1] = temp; } /* 2 >= 1 */ else if (level[i * teams + 1] >= level[i * teams]) { ; /* nothing to do */ } } /* end 2 teams */ else if (teams == 3) { /* 1 >= 2 >= 3 */ if (level[i * 3] >= level[i * 3 + 1] && level[i * 3 + 1] >= level[i * 3 + 2]) { temp = player[queue][(i + 1) * 3]; player[queue][(i + 1) * 3] = player[queue][(i + 1) * 3 + 2]; player[queue][(i + 1) * 3 + 2] = temp; } /* 1 >= 3 >= 2 */ else if (level[i * 3] >= level[i * 3 + 2] && level[i * 3 + 2] >= level[i * 3 + 1]) { temp = player[queue][(i + 1) * 3]; player[queue][(i + 1) * 3] = player[queue][(i + 1) * 3 + 2]; player[queue][(i + 1) * 3 + 2] = player[queue][(i + 1) * 3 + 1]; player[queue][(i + 1) * 3 + 1] = temp; } /* 2 >= 1 >= 3 */ else if (level[i * 3 + 1] >= level[i * 3] && level[i * 3] >= level[i * 3 + 2]) { temp = player[queue][(i + 1) * 3]; player[queue][(i + 1) * 3] = player[queue][(i + 1) * 3 + 1]; player[queue][(i + 1) * 3 + 1] = player[queue][(i + 1) * 3 + 2]; player[queue][(i + 1) * 3 + 2] = temp; } /* 2 >= 3 >= 1 */ else if (level[i * 3 + 1] >= level[i * 3 + 2] && level[i * 3 + 2] >= level[i * 3]) { temp = player[queue][(i + 1) * 3 + 1]; player[queue][(i + 1) * 3 + 1] = player[queue][(i + 1) * 3 + 2]; player[queue][(i + 1) * 3 + 2] = temp; } /* 3 >= 1 >= 2 */ else if (level[i * 3 + 2] >= level[i * 3] && level[i * 3] >= level[i * 3 + 1]) { temp = player[queue][(i + 1) * 3]; player[queue][(i + 1) * 3] = player[queue][(i + 1) * 3 + 1]; player[queue][(i + 1) * 3 + 1] = temp; } /* 3 >= 2 >= 1 */ else if (level[i * 3 + 2] >= level[i * 3 + 1] && level[i * 3 + 1] >= level[i * 3]) { ; /* nothing to do */ } } /* end 3 teams */ else if (teams == 4) { /* 1234 */ if (level[i * 4] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 3]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; temp = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 1243 */ else if (level[i * 4] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 2] && level[i * 4 + 3] >= level[i * 4 + 2]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 1324 */ else if (level[i * 4] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 3]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 1342 */ else if (level[i * 4] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4 + 1]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = temp; } /* 1423 */ else if (level[i * 4] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 2]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 1432 */ else if (level[i * 4] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 1]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = temp; } /* 2134 */ else if (level[i * 4 + 1] >= level[i * 4] && level[i * 4] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 3]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 2143 */ else if (level[i * 4 + 1] >= level[i * 4] && level[i * 4] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4 + 2]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; temp = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 2314 */ else if (level[i * 4 + 1] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4] && level[i * 4] >= level[i * 4 + 3]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = temp; } /* 2341 */ else if (level[i * 4 + 1] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4]) { temp = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 2413 */ else if (level[i * 4 + 1] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4] && level[i * 4] >= level[i * 4 + 2]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 2431 */ else if (level[i * 4 + 1] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4]) { temp = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 3124 */ else if (level[i * 4 + 2] >= level[i * 4] && level[i * 4] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 3]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 3142 */ else if (level[i * 4 + 2] >= level[i * 4] && level[i * 4] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4 + 1]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = temp; } /* 3214 */ else if (level[i * 4 + 2] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4] && level[i * 4] >= level[i * 4 + 3]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 3241 */ else if (level[i * 4 + 2] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4]) { temp = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 3412 */ else if (level[i * 4 + 2] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4] && level[i * 4] >= level[i * 4 + 1]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = temp; temp = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 3421 */ else if (level[i * 4 + 2] >= level[i * 4 + 3] && level[i * 4 + 3] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4]) { temp = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 3]; player[queue][(i + 1) * 4 + 3] = temp; } /* 4123 */ else if (level[i * 4 + 3] >= level[i * 4] && level[i * 4] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 2]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 4132 */ else if (level[i * 4 + 3] >= level[i * 4] && level[i * 4] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 1]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = temp; } /* 4213 */ else if (level[i * 4 + 3] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4] && level[i * 4] >= level[i * 4 + 2]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 4231 */ else if (level[i * 4 + 3] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4]) { temp = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = player[queue][(i + 1) * 4 + 2]; player[queue][(i + 1) * 4 + 2] = temp; } /* 4312 */ else if (level[i * 4 + 3] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4] && level[i * 4] >= level[i * 4 + 1]) { temp = player[queue][(i + 1) * 4]; player[queue][(i + 1) * 4] = player[queue][(i + 1) * 4 + 1]; player[queue][(i + 1) * 4 + 1] = temp; } /* 4321 */ else if (level[i * 4 + 3] >= level[i * 4 + 2] && level[i * 4 + 2] >= level[i * 4 + 1] && level[i * 4 + 1] >= level[i * 4]) { ; /* nothing to do */ } } /* end 4 teams */ } /* end ppt loop */ } /* end "if" statement */ return 0; } static int _anongame_match(t_connection * c, int queue) { int level = _anongame_level_by_queue(c, queue); int delta = 0; int i; t_matchdata *md; t_elem *curr; int diff; t_anongame *a = conn_get_anongame(c); std::uint32_t cur_prefs = a->map_prefs; t_connection *inv_c[ANONGAME_MAX_TEAMS]; int maxlevel, minlevel; int teams = 0; players[queue] = 0; eventlog(eventlog_level_trace, __FUNCTION__, "[{}] matching started for level {} player in queue {}", conn_get_socket(c), level, queue); diff = war3_get_maxleveldiff(); maxlevel = level + diff; minlevel = (level - diff < 0) ? 0 : level - diff; while (abs(delta) < (diff + 1)) { if ((level + delta <= maxlevel) && (level + delta >= minlevel)) { eventlog(eventlog_level_trace, __FUNCTION__, "Traversing level {} players", level + delta); LIST_TRAVERSE(matchlists[queue][level + delta], curr) { md = (t_matchdata*)elem_get_data(curr); if (md->versiontag && conn_get_versioncheck(c) && !std::strcmp(md->versiontag, conn_get_versioncheck(c)->get_version_tag().c_str()) && (cur_prefs & md->map_prefs)) { /* set maxlevel and minlevel to keep all players within 6 levels */ maxlevel = (level + delta + diff < maxlevel) ? level + delta + diff : maxlevel; minlevel = (level + delta - diff > minlevel) ? level + delta - diff : minlevel; cur_prefs &= md->map_prefs; /* AT match */ if (anongame_arranged(queue)) { /* set the inv_c for unqueueing later */ inv_c[teams] = md->c; a = conn_get_anongame(md->c); /* add all the players on the team to player[][] */ int totalplayers = _anongame_totalplayers(queue); int totalteams = _anongame_totalteams(queue); int totalcount = (totalteams > 0) ? totalplayers / totalteams : 0; for (i = 0; i < totalcount; i++) { player[queue][teams + i * _anongame_totalteams(queue)] = a->tc[i]; players[queue]++; } teams++; /* check for enough players */ if (players[queue] == _anongame_totalplayers(queue)) { /* unqueue just the single team entry */ for (i = 0; i < teams; i++) anongame_unqueue(inv_c[i], queue); mapname = _get_map_from_prefs(queue, cur_prefs, conn_get_clienttag(c)); return 0; } /* PG match */ } else { player[queue][players[queue]++] = md->c; if (players[queue] == _anongame_totalplayers(queue)) { /* first sort queue by level */ std::qsort(player[queue], players[queue], sizeof(t_connection *), _anongame_compare_level); /* next call reodering function */ _anongame_order_queue(queue); /* unqueue players */ for (i = 0; i < players[queue]; i++) anongame_unqueue(player[queue][i], queue); mapname = _get_map_from_prefs(queue, cur_prefs, conn_get_clienttag(c)); return 0; } } } } } if (delta <= 0 || level - delta < 0) delta = abs(delta) + 1; else delta = -delta; if (level + delta > MAX_LEVEL) delta = -delta; if (level + delta < 0) break; /* cant really happen */ } eventlog(eventlog_level_trace, __FUNCTION__, "[{}] Matching finished, not enough players (found {})", conn_get_socket(c), players[queue]); mapname = NULL; return 0; } static int w3routeip = -1; /* changed by dizzy to show the w3routeshow addr if available */ static unsigned short w3routeport = BNETD_W3ROUTE_PORT; static int _anongame_search_found(int queue) { t_packet *rpacket; t_anongame *a; int i, j; t_saf_pt2 *pt2; /* FIXME: maybe periodically lookup w3routeaddr to support dynamic ips? * (or should dns lookup be even quick enough to do it everytime?) */ if (w3routeip == -1) { t_addr *routeraddr; routeraddr = addr_create_str(prefs_get_w3route_addr(), 0, BNETD_W3ROUTE_PORT); if (!routeraddr) { eventlog(eventlog_level_error, __FUNCTION__, "error getting w3route_addr"); return -1; } w3routeip = addr_get_ip(routeraddr); w3routeport = addr_get_port(routeraddr); addr_destroy(routeraddr); } t_anongameinfo* const info = anongameinfo_create(_anongame_totalplayers(queue)); if (!info) { eventlog(eventlog_level_error, __FUNCTION__, "anongameinfo_create failed"); return -1; } /* create data to be appended to end of packet */ pt2 = (t_saf_pt2*)xmalloc(sizeof(t_saf_pt2)); bn_int_set(&pt2->unknown1, 0xFFFFFFFF); bn_int_set(&pt2->anongame_string, _anongame_get_gametype_tab(queue)); bn_byte_set(&pt2->totalplayers, _anongame_totalplayers(queue)); bn_byte_set(&pt2->totalteams, _anongame_totalteams(queue)); /* 1v1 & sffa are set to zero in _anongame_totalteams() */ bn_short_set(&pt2->unknown2, 0); bn_byte_set(&pt2->visibility, 2); /* visibility. 0x01 - dark 0x02 - default */ bn_byte_set(&pt2->unknown3, 2); /* send found packet to each of the players */ for (i = 0; i < players[queue]; i++) { if (!(a = conn_get_anongame(player[queue][i]))) { eventlog(eventlog_level_error, __FUNCTION__, "no anongame struct for queued player"); xfree(pt2); anongameinfo_destroy(info); return -1; } a->info = info; a->playernum = i + 1; for (j = 0; j < players[queue]; j++) { a->info->player[j] = player[queue][j]; a->info->account[j] = conn_get_account(player[queue][j]); } if (!(rpacket = packet_create(packet_class_bnet))) { xfree(pt2); anongameinfo_destroy(info); return -1; } packet_set_size(rpacket, sizeof(t_server_anongame_found)); packet_set_type(rpacket, SERVER_ANONGAME_FOUND); bn_byte_set(&rpacket->u.server_anongame_found.option, 1); bn_int_set(&rpacket->u.server_anongame_found.count, a->count); bn_int_set(&rpacket->u.server_anongame_found.unknown1, 0); { /* trans support */ unsigned int w3ip = w3routeip; unsigned short w3port = w3routeport; trans_net(conn_get_addr(player[queue][i]), &w3ip, &w3port); /* if ip to send is 0.0.0.0 (which will not work anyway) try * to guess the reachable IP of pvpgn by using the local * endpoing address of the bnet class connection */ if (!w3ip) w3ip = conn_get_real_local_addr(player[queue][i]); bn_int_nset(&rpacket->u.server_anongame_found.ip, w3ip); bn_short_set(&rpacket->u.server_anongame_found.port, w3port); } bn_byte_set(&rpacket->u.server_anongame_found.unknown2, i + 1); bn_byte_set(&rpacket->u.server_anongame_found.unknown3, queue); bn_short_set(&rpacket->u.server_anongame_found.unknown4, 0); bn_int_set(&rpacket->u.server_anongame_found.id, 0xdeadbeef); bn_byte_set(&rpacket->u.server_anongame_found.unknown5, 6); bn_byte_set(&rpacket->u.server_anongame_found.type, a->type); bn_byte_set(&rpacket->u.server_anongame_found.gametype, a->gametype); packet_append_string(rpacket, mapname); packet_append_data(rpacket, pt2, sizeof(t_saf_pt2)); conn_push_outqueue(player[queue][i], rpacket); packet_del_ref(rpacket); } /* clear queue */ players[queue] = 0; xfree(pt2); return 0; } /**********************************************************************************/ /* external functions */ /**********************************************************************************/ extern int anongame_matchlists_create() { int i, j; for (i = 0; i < ANONGAME_TYPES; i++) { for (j = 0; j < MAX_LEVEL; j++) { matchlists[i][j] = NULL; } } return 0; } extern int anongame_matchlists_destroy() { int i, j; for (i = 0; i < ANONGAME_TYPES; i++) { for (j = 0; j < MAX_LEVEL; j++) { if (matchlists[i][j]) { list_destroy(matchlists[i][j]); } } } return 0; } /**********/ extern int handle_anongame_search(t_connection * c, t_packet const *packet) { return _handle_anongame_search(c, packet); } extern int anongame_unqueue(t_connection * c, int queue) { int i; t_elem *curr; t_matchdata *md; if (queue < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got negative queue id ({})", queue); return -1; } if (queue >= ANONGAME_TYPES) { eventlog(eventlog_level_error, __FUNCTION__, "unknown queue: {}", queue); return -1; } if (conn_get_anongame_search_starttime(c) != ((std::time_t) 0)) { average_anongame_search_time *= anongame_search_count; average_anongame_search_time += (long)std::difftime(std::time(NULL), conn_get_anongame_search_starttime(c)); anongame_search_count++; average_anongame_search_time /= anongame_search_count; if (anongame_search_count > 20000) anongame_search_count = anongame_search_count / 2; /* to prevent an overflow of the average time */ conn_set_anongame_search_starttime(c, ((std::time_t) 0)); } for (i = 0; i < MAX_LEVEL; i++) { if (matchlists[queue][i] == NULL) continue; LIST_TRAVERSE(matchlists[queue][i], curr) { md = (t_matchdata*)elem_get_data(curr); if (md->c == c) { eventlog(eventlog_level_trace, __FUNCTION__, "unqueued player [{}] level {}", conn_get_socket(c), i); list_remove_elem(matchlists[queue][i], &curr); xfree(md); return 0; } } } /* Output error to std::log for PG queues, AT players are queued with single * entry. Because anongame_unqueue() is called for each player, only the first * time called will the team be removed, the rest are therefore not an error. * [Omega] */ if (anongame_arranged(queue) == 0) { eventlog(eventlog_level_trace, __FUNCTION__, "[{}] player not found in \"{}\" queue", conn_get_socket(c), _anongame_queue_to_string(queue)); return -1; } return 0; } /**********/ extern char anongame_arranged(int queue) { switch (queue) { case ANONGAME_TYPE_AT_2V2: case ANONGAME_TYPE_AT_3V3: case ANONGAME_TYPE_AT_4V4: case ANONGAME_TYPE_AT_2V2V2: return 1; case ANONGAME_TYPE_TY: return tournament_is_arranged(); default: return 0; } } extern int anongame_evaluate_results(t_anongame * anongame) { int i, j, number; int wins[ANONGAME_MAX_GAMECOUNT]; int losses[ANONGAME_MAX_GAMECOUNT]; int result; t_anongame_gameresult *results; t_anongameinfo *anoninfo = anongame->info; for (i = 0; i < ANONGAME_MAX_GAMECOUNT; i++) { wins[i] = 0; losses[i] = 0; } for (i = 0; i < anongame_get_totalplayers(anongame); i++) { if ((results = anoninfo->results[i])) { for (j = 0; j < gameresult_get_number_of_results(results); j++) { number = gameresult_get_player_number(results, j) - 1; result = gameresult_get_player_result(results, j); if ((result == W3_GAMERESULT_WIN)) wins[number]++; if ((result == W3_GAMERESULT_LOSS)) losses[number]++; } } } for (i = 0; i < anongame_get_totalplayers(anongame); i++) { if ((wins[i] > losses[i])) { if ((anoninfo->result[i] != W3_GAMERESULT_WIN)) { eventlog(eventlog_level_trace, __FUNCTION__, "player {} reported DISC/LOSS for self, but others agree on WIN", i + 1); anoninfo->result[i] = W3_GAMERESULT_WIN; } } else { if ((anoninfo->result[i] != W3_GAMERESULT_LOSS)) { eventlog(eventlog_level_trace, __FUNCTION__, "player {} reported DISC/WIN for self, but others agree on LOSS", i + 1); anoninfo->result[i] = W3_GAMERESULT_LOSS; } } } return 0; } extern int anongame_stats(t_connection * c) { int i; int wins = 0, losses = 0, discs = 0; t_connection *gamec = conn_get_routeconn(c); t_anongame *a = conn_get_anongame(gamec); int tp = anongame_get_totalplayers(a); int oppon_level[ANONGAME_MAX_GAMECOUNT]; std::uint8_t gametype = a->queue; std::uint8_t plnum = a->playernum; t_clienttag ct = conn_get_clienttag(c); int tt = _anongame_totalteams(gametype); /* do nothing till all other players have w3route conn closed */ for (i = 0; i < tp; i++) if (i + 1 != plnum && a->info->player[i]) if (conn_get_routeconn(a->info->player[i])) return 0; anongame_evaluate_results(a); /* count wins, losses, discs */ for (i = 0; i < tp; i++) { if (a->info->result[i] == W3_GAMERESULT_WIN) wins++; else if (a->info->result[i] == W3_GAMERESULT_LOSS) losses++; else discs++; } /* do some sanity checking (hack prevention) */ switch (gametype) { case ANONGAME_TYPE_SMALL_FFA: if (wins != 1) { eventlog(eventlog_level_info, __FUNCTION__, "bogus game result: wins != 1 in small ffa game"); return -1; } break; case ANONGAME_TYPE_TEAM_FFA: if (!discs && wins != 2) { eventlog(eventlog_level_info, __FUNCTION__, "bogus game result: wins != 2 in team ffa game"); return -1; } break; default: if (!discs && wins > losses) { eventlog(eventlog_level_info, __FUNCTION__, "bogus game result: wins > losses"); return -1; } break; } /* prevent users from getting loss if server is shutdown (does not prevent errors from crash) - [Omega] */ /* also discard games with no winners at all (i.e. games where game host disc'ed and so all players do) */ if (!wins) return -1; /* according to zap, order of players in anongame is: * for PG: t1_p1, t2_p1, t1_p2, t2_p2, ... * for AT: t1_p1, t1_p2, ..., t2_p1, t2_p2, ... * * (Not True.. follows same order as PG) * 4v4 = t1_p1, t2_p1, t1_p2, t2_p2, t1_p3, t2_p3, t1_p4, t2_p4 * 3v3v3 = t1_p1, t2_p1, t3_p1, t1_p2, t2_p2, t3_p2, t1_p3, t2_p3, t3_p3 * 2v2v2v2 = t1_p1, t2_p1, t3_p1, t4_p1, t1_p2, t2_p2, t3_p2, t4_p2 */ /* opponent level calculation has to be done here, because later on, the level of other players * may already be modified */ for (i = 0; i < tp; i++) { int j, k, l; t_account *oacc; oppon_level[i] = 0; switch (gametype) { case ANONGAME_TYPE_TY: /* FIXME-TY: ADD TOURNAMENT STATS RECORDING (this part not required?) */ break; case ANONGAME_TYPE_1V1: oppon_level[i] = account_get_ladder_level(a->info->account[(i + 1) % tp], ct, ladder_id_solo); break; case ANONGAME_TYPE_SMALL_FFA: /* oppon_level = average level of all other players */ for (j = 0; j < tp; j++) if (i != j) oppon_level[i] += account_get_ladder_level(a->info->account[j], ct, ladder_id_ffa); oppon_level[i] /= (tp - 1); break; case ANONGAME_TYPE_AT_2V2: case ANONGAME_TYPE_AT_3V3: case ANONGAME_TYPE_AT_4V4: oacc = a->info->account[(i + 1) % tp]; oppon_level[i] = team_get_level(account_find_team_by_teamid(oacc, account_get_currentatteam(oacc))); break; case ANONGAME_TYPE_AT_2V2V2: oacc = a->info->account[(i + 1) % tp]; oppon_level[i] = team_get_level(account_find_team_by_teamid(oacc, account_get_currentatteam(oacc))); oacc = a->info->account[(i + 2) % tp]; oppon_level[i] = team_get_level(account_find_team_by_teamid(oacc, account_get_currentatteam(oacc))); oppon_level[i] /= 2; break; default: /* oppon_level = average level of all opponents * this should work for all PG team games * [Omega] */ k = i + 1; int _count = (tt > 0) ? tp / tt : 0; for (j = 0; j < _count; j++) { for (l = 0; l < (tt - 1); l++) { oppon_level[i] += account_get_ladder_level(a->info->account[k % tp], ct, ladder_id_team); k++; } k++; } oppon_level[i] /= (_count * (tt - 1)); } } for (i = 0; i < tp; i++) { t_account *acc; t_team *team; unsigned int currteam; int result = a->info->result[i]; if (result == -1) result = W3_GAMERESULT_LOSS; acc = a->info->account[i]; switch (gametype) { case ANONGAME_TYPE_TY: if (result == W3_GAMERESULT_WIN) tournament_add_stat(acc, 1); if (result == W3_GAMERESULT_LOSS) tournament_add_stat(acc, 2); /* FIXME-TY: how to do ties? */ break; case ANONGAME_TYPE_AT_2V2: case ANONGAME_TYPE_AT_3V3: case ANONGAME_TYPE_AT_4V4: case ANONGAME_TYPE_AT_2V2V2: if ((currteam = account_get_currentatteam(acc))) { team = account_find_team_by_teamid(acc, currteam); team_set_saveladderstats(team, gametype, result, oppon_level[i], ct); } break; case ANONGAME_TYPE_1V1: case ANONGAME_TYPE_2V2: case ANONGAME_TYPE_3V3: case ANONGAME_TYPE_4V4: case ANONGAME_TYPE_SMALL_FFA: case ANONGAME_TYPE_5V5: case ANONGAME_TYPE_6V6: case ANONGAME_TYPE_2V2V2: case ANONGAME_TYPE_3V3V3: case ANONGAME_TYPE_4V4V4: case ANONGAME_TYPE_2V2V2V2: case ANONGAME_TYPE_3V3V3V3: if (result == W3_GAMERESULT_WIN) account_set_saveladderstats(acc, gametype, game_result_win, oppon_level[i], ct); if (result == W3_GAMERESULT_LOSS) account_set_saveladderstats(acc, gametype, game_result_loss, oppon_level[i], ct); break; default: break; } } /* aaron: now update war3 ladders */ ladders.update(); return 1; } /**********/ extern t_anongameinfo *anongameinfo_create(int totalplayers) { t_anongameinfo *temp; int i; temp = (t_anongameinfo*)xmalloc(sizeof(t_anongameinfo)); temp->totalplayers = temp->currentplayers = totalplayers; for (i = 0; i < ANONGAME_MAX_GAMECOUNT; i++) { temp->player[i] = NULL; temp->account[i] = NULL; temp->result[i] = -1; /* consider DISC default */ temp->results[i] = NULL; } return temp; } extern void anongameinfo_destroy(t_anongameinfo * i) { int j; if (!i) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongameinfo"); return; } for (j = 0; j < ANONGAME_MAX_GAMECOUNT; j++) if (i->results[j]) gameresult_destroy(i->results[j]); xfree(i); } /**********/ extern t_anongameinfo *anongame_get_info(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return NULL; } return a->info; } extern int anongame_get_currentplayers(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } if (!a->info) { eventlog(eventlog_level_error, __FUNCTION__, "NULL anongameinfo"); return 0; } return a->info->currentplayers; } extern int anongame_get_totalplayers(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } if (!a->info) { eventlog(eventlog_level_error, __FUNCTION__, "NULL anongameinfo"); return 0; } return a->info->totalplayers; } extern t_connection *anongame_get_player(t_anongame * a, int plnum) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return NULL; } if (!a->info) { eventlog(eventlog_level_error, __FUNCTION__, "NULL anongameinfo"); return NULL; } if (plnum < 0 || plnum > 7 || plnum >= a->info->totalplayers) { eventlog(eventlog_level_error, __FUNCTION__, "invalid plnum: {}", plnum); return NULL; } return a->info->player[plnum]; } extern int anongame_get_count(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->count; } extern std::uint32_t anongame_get_id(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->id; } extern t_connection *anongame_get_tc(t_anongame * a, int tpnumber) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->tc[tpnumber]; } extern std::uint32_t anongame_get_race(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->race; } extern std::uint32_t anongame_get_handle(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->handle; } extern unsigned int anongame_get_addr(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->addr; } extern char anongame_get_loaded(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->loaded; } extern char anongame_get_joined(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->joined; } extern std::uint8_t anongame_get_playernum(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->playernum; } extern std::uint8_t anongame_get_queue(t_anongame * a) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return 0; } return a->queue; } /**********/ extern void anongame_set_result(t_anongame * a, int result) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return; } if (!a->info) { eventlog(eventlog_level_error, __FUNCTION__, "NULL anongameinfo"); return; } if (a->playernum < 1 || a->playernum > ANONGAME_MAX_GAMECOUNT) { eventlog(eventlog_level_error, __FUNCTION__, "invalid playernum: {}", a->playernum); return; } a->info->result[a->playernum - 1] = result; } extern void anongame_set_gameresults(t_anongame * a, t_anongame_gameresult * results) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return; } if (!a->info) { eventlog(eventlog_level_error, __FUNCTION__, "NULL anongameinfo"); return; } if (a->playernum < 1 || a->playernum > ANONGAME_MAX_GAMECOUNT) { eventlog(eventlog_level_error, __FUNCTION__, "invalid playernum: {}", a->playernum); return; } a->info->results[a->playernum - 1] = results; } extern void anongame_set_handle(t_anongame * a, std::uint32_t h) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return; } a->handle = h; } extern void anongame_set_addr(t_anongame * a, unsigned int addr) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return; } a->addr = addr; } extern void anongame_set_loaded(t_anongame * a, char loaded) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return; } a->loaded = loaded; } extern void anongame_set_joined(t_anongame * a, char joined) { if (!a) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame"); return; } a->joined = joined; } /**********/ /* move to own .c/.h file for handling w3route connections */ extern int handle_w3route_packet(t_connection * c, t_packet const *const packet) { /* [smith] 20030427 fixed Big-Endian/Little-Endian conversion (Solaris bug) then * use packet_append_data for append platform dependent data types - like * "int", cos this code was broken for BE platforms. it's rewriten in platform * independent style whis usege bn_int and other bn_* like datatypes and * fuctions for wor with datatypes - bn_int_set(), what provide right * byteorder, not depended on LE/BE * fixed broken htonl() conversion for BE platforms - change it to * bn_int_nset(). i hope it's worked on intel too %) */ t_packet *rpacket; t_connection *gamec; char const *username; t_anongame *a = NULL; std::uint8_t gametype, plnum; int tp, i; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", conn_get_socket(c)); return -1; } if (packet_get_class(packet) != packet_class_w3route) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", conn_get_socket(c), packet_get_class(packet)); return -1; } if (conn_get_state(c) != conn_state_connected) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] not connected", conn_get_socket(c)); return -1; } /* init route connection */ if (packet_get_type(packet) == CLIENT_W3ROUTE_REQ) { t_connection *oldc; eventlog(eventlog_level_trace, __FUNCTION__, "[{}] sizeof t_client_w3route_req {}", conn_get_socket(c), sizeof(t_client_w3route_req)); username = packet_get_str_const(packet, sizeof(t_client_w3route_req), MAX_USERNAME_LEN); eventlog(eventlog_level_info, __FUNCTION__, "[{}] got username '{}'", conn_get_socket(c), username); gamec = connlist_find_connection_by_accountname(username); if (!gamec) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] no game connection found for this w3route connection; closing", conn_get_socket(c)); conn_set_state(c, conn_state_destroy); return 0; } if (!(a = conn_get_anongame(gamec))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] no anongame struct for game connection", conn_get_socket(c)); conn_set_state(c, conn_state_destroy); return 0; } if (bn_int_get((unsigned char const *)packet->u.data + sizeof(t_client_w3route_req)+std::strlen(username) + 2) != anongame_get_id(a)) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] client sent wrong id for user '{}', closing connection", conn_get_socket(c), username); conn_set_state(c, conn_state_destroy); return 0; } oldc = conn_get_routeconn(gamec); if (oldc) { conn_set_routeconn(oldc, NULL); conn_set_state(oldc, conn_state_destroy); } if (conn_set_routeconn(c, gamec) < 0 || conn_set_routeconn(gamec, c) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] conn_set_routeconn failed", conn_get_socket(c)); return -1; } /* set clienttag for w3route connections; we can do conn_get_clienttag() on them */ conn_set_clienttag(c, conn_get_clienttag(gamec)); anongame_set_addr(a, bn_int_get((unsigned char const *)packet->u.data + sizeof(t_client_w3route_req)+std::strlen(username) + 2 + 12)); anongame_set_joined(a, 0); anongame_set_loaded(a, 0); anongame_set_result(a, -1); anongame_set_gameresults(a, NULL); anongame_set_handle(a, bn_int_get(packet->u.client_w3route_req.handle)); if (!(rpacket = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_w3route_ack)); packet_set_type(rpacket, SERVER_W3ROUTE_ACK); bn_byte_set(&rpacket->u.server_w3route_ack.unknown1, 7); bn_short_set(&rpacket->u.server_w3route_ack.unknown2, 0); bn_int_set(&rpacket->u.server_w3route_ack.unknown3, SERVER_W3ROUTE_ACK_UNKNOWN3); bn_short_set(&rpacket->u.server_w3route_ack.unknown4, 0xcccc); bn_byte_set(&rpacket->u.server_w3route_ack.playernum, anongame_get_playernum(a)); bn_short_set(&rpacket->u.server_w3route_ack.unknown5, 0x0002); bn_short_set(&rpacket->u.server_w3route_ack.port, conn_get_port(c)); bn_int_nset(&rpacket->u.server_w3route_ack.ip, conn_get_addr(c)); bn_int_set(&rpacket->u.server_w3route_ack.unknown7, 0); bn_int_set(&rpacket->u.server_w3route_ack.unknown8, 0); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } else { gamec = conn_get_routeconn(c); if (gamec) a = conn_get_anongame(gamec); } if (!gamec) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] no game connection found for this w3route connection", conn_get_socket(c)); return 0; } if (!a) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] no anongame struct found for this w3route connection", conn_get_socket(c)); return 0; } gametype = anongame_get_queue(a); plnum = anongame_get_playernum(a); tp = anongame_get_totalplayers(a); /* handle these packets _before_ checking for routeconn of other players */ switch (packet_get_type(packet)) { case CLIENT_W3ROUTE_ECHOREPLY: return 0; case CLIENT_W3ROUTE_CONNECTED: return 0; case CLIENT_W3ROUTE_GAMERESULT: case CLIENT_W3ROUTE_GAMERESULT_W3XP: { /* insert reading of whole packet into t_gameresult */ t_anongame_gameresult *gameresult; int result; t_timer_data data; t_anongameinfo *inf = anongame_get_info(a); t_connection *ac; data.p = NULL; if (!(gameresult = anongame_gameresult_parse(packet))) result = -1; else /* own result is always stored as first result */ result = gameresult_get_player_result(gameresult, 0); eventlog(eventlog_level_trace, __FUNCTION__, "[{}] got W3ROUTE_GAMERESULT: {:08}", conn_get_socket(c), result); if (!inf) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] NULL anongameinfo", conn_get_socket(c)); return -1; } anongame_set_gameresults(a, gameresult); anongame_set_result(a, result); conn_set_state(c, conn_state_destroy); /* activate timers on open w3route connectons */ if (result == W3_GAMERESULT_WIN) { for (i = 0; i < tp; i++) { if (anongame_get_player(a, i)) { ac = conn_get_routeconn(anongame_get_player(a, i)); if (ac) { /* 300 seconds or 5 minute timer */ timerlist_add_timer(ac, now + (std::time_t) 300, conn_shutdown, data); eventlog(eventlog_level_trace, __FUNCTION__, "[{}] started timer to close w3route", conn_get_socket(ac)); } } } } return 0; } } for (i = 0; i < tp; i++) if (i + 1 != plnum && anongame_get_player(a, i)) if (!conn_get_routeconn(anongame_get_player(a, i)) || !conn_get_anongame(anongame_get_player(a, i))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] not all players have w3route connections up yet", conn_get_socket(c)); return 0; } /* handle these packets _after_ checking for routeconns of other players */ switch (packet_get_type(packet)) { case CLIENT_W3ROUTE_LOADINGDONE: eventlog(eventlog_level_trace, __FUNCTION__, "[{}] got LOADINGDONE, playernum: {}", conn_get_socket(c), plnum); anongame_set_loaded(a, 1); for (i = 0; i < tp; i++) { if (!anongame_get_player(a, i)) /* ignore disconnected players */ continue; if (!(rpacket = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_w3route_loadingack)); packet_set_type(rpacket, SERVER_W3ROUTE_LOADINGACK); bn_byte_set(&rpacket->u.server_w3route_loadingack.playernum, plnum); conn_push_outqueue(conn_get_routeconn(anongame_get_player(a, i)), rpacket); packet_del_ref(rpacket); } /* have all players loaded? */ for (i = 0; i < tp; i++) if (i + 1 != plnum && anongame_get_player(a, i) && !anongame_get_loaded(conn_get_anongame(anongame_get_player(a, i)))) return 0; for (i = 0; i < tp; i++) { if (!anongame_get_player(a, i)) continue; if (!(rpacket = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_w3route_ready)); packet_set_type(rpacket, SERVER_W3ROUTE_READY); bn_byte_set(&rpacket->u.server_w3route_host.unknown1, 0); conn_push_outqueue(conn_get_routeconn(anongame_get_player(a, i)), rpacket); packet_del_ref(rpacket); } break; case CLIENT_W3ROUTE_ABORT: eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got W3ROUTE_ABORT", conn_get_socket(c)); break; default: eventlog(eventlog_level_trace, __FUNCTION__, "[{}] default: got packet type: {:04}", conn_get_socket(c), packet_get_type(packet)); } return 0; } extern int handle_anongame_join(t_connection * c) { t_anongame *a, *ja, *oa; t_connection *jc, *o; t_packet *rpacket; int tp, level; char gametype; t_account *acct; t_clienttag ct = conn_get_clienttag(c); static t_server_w3route_playerinfo2 pl2; static t_server_w3route_levelinfo2 li2; static t_server_w3route_playerinfo_addr pl_addr; int i, j; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(c)); return -1; } if (!(conn_get_routeconn(c))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] no route connection", conn_get_socket(c)); return -1; } if (!(a = conn_get_anongame(c))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] no anongame struct", conn_get_socket(c)); return -1; } anongame_set_joined(a, 1); gametype = anongame_get_queue(a); tp = anongame_get_totalplayers(a); /* wait till all players have w3route conns */ for (i = 0; i < tp; i++) if (anongame_get_player(a, i) && (!conn_get_routeconn(anongame_get_player(a, i)) || !conn_get_anongame(anongame_get_player(a, i)) || !anongame_get_joined(conn_get_anongame(anongame_get_player(a, i))))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] not all players have joined game BNet yet", conn_get_socket(c)); return 0; } /* then send each player info about all others */ for (j = 0; j < tp; j++) { jc = anongame_get_player(a, j); if (!jc) /* ignore disconnected players */ continue; ja = conn_get_anongame(jc); /* send a playerinfo packet for this player to each other player */ for (i = 0; i < tp; i++) { if (i + 1 != anongame_get_playernum(ja)) { eventlog(eventlog_level_trace, __FUNCTION__, "i = {}", i); if (!(o = anongame_get_player(ja, i))) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] player {} disconnected, ignoring", conn_get_socket(c), i); continue; } if (!(rpacket = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_w3route_playerinfo)); packet_set_type(rpacket, SERVER_W3ROUTE_PLAYERINFO); if (!(oa = conn_get_anongame(o))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] no anongame struct of player {}", conn_get_socket(c), i); packet_del_ref(rpacket); return -1; } bn_int_set(&rpacket->u.server_w3route_playerinfo.handle, anongame_get_handle(oa)); bn_byte_set(&rpacket->u.server_w3route_playerinfo.playernum, anongame_get_playernum(oa)); packet_append_string(rpacket, conn_get_username(o)); /* playerinfo2 */ bn_byte_set(&pl2.unknown1, 8); bn_int_set(&pl2.id, anongame_get_id(oa)); bn_int_set(&pl2.race, anongame_get_race(oa)); packet_append_data(rpacket, &pl2, sizeof(pl2)); /* external addr */ bn_short_set(&pl_addr.unknown1, 2); { /* trans support */ unsigned short port = conn_get_game_port(o); unsigned int addr = conn_get_game_addr(o); trans_net(conn_get_game_addr(jc), &addr, &port); bn_short_nset(&pl_addr.port, port); bn_int_nset(&pl_addr.ip, addr); } bn_int_set(&pl_addr.unknown2, 0); bn_int_set(&pl_addr.unknown3, 0); packet_append_data(rpacket, &pl_addr, sizeof(pl_addr)); /* local addr */ bn_short_set(&pl_addr.unknown1, 2); bn_short_nset(&pl_addr.port, conn_get_game_port(o)); bn_int_set(&pl_addr.ip, anongame_get_addr(oa)); bn_int_set(&pl_addr.unknown2, 0); bn_int_set(&pl_addr.unknown3, 0); packet_append_data(rpacket, &pl_addr, sizeof(pl_addr)); conn_push_outqueue(conn_get_routeconn(jc), rpacket); packet_del_ref(rpacket); } } /* levelinfo */ if (!(rpacket = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_w3route_levelinfo)); packet_set_type(rpacket, SERVER_W3ROUTE_LEVELINFO); bn_byte_set(&rpacket->u.server_w3route_levelinfo.numplayers, anongame_get_currentplayers(a)); for (i = 0; i < tp; i++) { if (!anongame_get_player(ja, i)) continue; bn_byte_set(&li2.plnum, i + 1); bn_byte_set(&li2.unknown1, 3); switch (gametype) { case ANONGAME_TYPE_1V1: level = account_get_ladder_level(conn_get_account(anongame_get_player(ja, i)), ct, ladder_id_solo); break; case ANONGAME_TYPE_SMALL_FFA: case ANONGAME_TYPE_TEAM_FFA: level = account_get_ladder_level(conn_get_account(anongame_get_player(ja, i)), ct, ladder_id_ffa); break; case ANONGAME_TYPE_AT_2V2: case ANONGAME_TYPE_AT_3V3: case ANONGAME_TYPE_AT_4V4: case ANONGAME_TYPE_AT_2V2V2: acct = conn_get_account(anongame_get_player(ja, i)); level = team_get_level(account_find_team_by_teamid(acct, account_get_currentatteam(acct))); break; case ANONGAME_TYPE_TY: level = 0; /* FIXME-TY: WHAT TO DO HERE */ break; default: level = account_get_ladder_level(conn_get_account(anongame_get_player(ja, i)), ct, ladder_id_team); break; } /* first anongame shows level 0 as level 1 */ bn_byte_set(&li2.level, level ? level : 1); bn_short_set(&li2.unknown2, 0); packet_append_data(rpacket, &li2, sizeof(li2)); } conn_push_outqueue(conn_get_routeconn(jc), rpacket); packet_del_ref(rpacket); /* startgame1 */ if (!(rpacket = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_w3route_startgame1)); packet_set_type(rpacket, SERVER_W3ROUTE_STARTGAME1); conn_push_outqueue(conn_get_routeconn(jc), rpacket); packet_del_ref(rpacket); /* startgame2 */ if (!(rpacket = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_w3route_startgame2)); packet_set_type(rpacket, SERVER_W3ROUTE_STARTGAME2); conn_push_outqueue(conn_get_routeconn(jc), rpacket); packet_del_ref(rpacket); } return 0; } extern const char * anongame_get_map_from_prefs(int queue, t_clienttag clienttag) { int i, j = 0; const char *default_map, *selected; char *res_maps[32]; char clienttag_str[5]; switch (clienttag) { case CLIENTTAG_REDALERT2_UINT: default_map = "eb2.map"; break; case CLIENTTAG_YURISREV_UINT: default_map = "xgrinder.map"; break; default: ERROR1("invalid clienttag: {}", tag_uint_to_str(clienttag_str, clienttag)); return "eb2.map"; } for (i = 0; i < 32; i++) res_maps[i] = NULL; for (i = 0; i < maplists_get_totalmaps_by_queue(clienttag, queue); i++) { res_maps[j++] = maplists_get_map(queue, clienttag, i + 1); } if (j == 0) { eventlog(eventlog_level_error, __FUNCTION__, "j == 0; returning \"eb2.map\""); return "eb2.map"; } i = std::rand() % j; if (res_maps[i]) selected = res_maps[i]; else selected = default_map; eventlog(eventlog_level_trace, __FUNCTION__, "got map {} from prefs", selected); return selected; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame.h000066400000000000000000000102351462640461400200220ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ANONGAME_TYPES #define INCLUDED_ANONGAME_TYPES #include #ifdef JUST_NEED_TYPES # include "account.h" # include "connection.h" #else # define JUST_NEED_TYPES # include "account.h" # include "connection.h" # undef JUST_NEED_TYPES #endif # include "anongame_gameresult.h" namespace pvpgn { namespace bnetd { typedef struct { int currentplayers; int totalplayers; struct connection * player[ANONGAME_MAX_GAMECOUNT]; t_account * account[ANONGAME_MAX_GAMECOUNT]; int result[ANONGAME_MAX_GAMECOUNT]; t_anongame_gameresult * results[ANONGAME_MAX_GAMECOUNT]; } t_anongameinfo; typedef struct { t_anongameinfo * info; int count; std::uint32_t id; std::uint32_t tid; struct connection * tc[ANONGAME_MAX_GAMECOUNT / 2]; std::uint32_t race; std::uint32_t handle; unsigned int addr; char loaded; char joined; std::uint8_t playernum; std::uint32_t map_prefs; std::uint8_t type; std::uint8_t gametype; int queue; } t_anongame; typedef struct { struct connection * c; std::uint32_t map_prefs; char const * versiontag; } t_matchdata; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ANONGAME_PROTOS #define INCLUDED_ANONGAME_PROTOS #define JUST_NEED_TYPES #include "common/packet.h" #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int anongame_matchlists_create(void); extern int anongame_matchlists_destroy(void); extern int handle_anongame_search(t_connection * c, t_packet const * packet); extern int anongame_unqueue(t_connection * c, int queue); extern char anongame_arranged(int queue); extern int anongame_stats(t_connection * c); extern t_anongameinfo * anongameinfo_create(int totalplayers); extern void anongameinfo_destroy(t_anongameinfo * i); extern t_anongameinfo * anongame_get_info(t_anongame * a); extern int anongame_get_currentplayers(t_anongame *a); extern int anongame_get_totalplayers(t_anongame *a); extern t_connection * anongame_get_player(t_anongame * a, int plnum); extern int anongame_get_count(t_anongame *a); extern std::uint32_t anongame_get_id(t_anongame * a); extern t_connection * anongame_get_tc(t_anongame *a, int tpnumber); extern std::uint32_t anongame_get_race(t_anongame * a); extern std::uint32_t anongame_get_handle(t_anongame * a); extern unsigned int anongame_get_addr(t_anongame * a); extern char anongame_get_loaded(t_anongame * a); extern char anongame_get_joined(t_anongame * a); extern std::uint8_t anongame_get_playernum(t_anongame * a); extern std::uint8_t anongame_get_queue(t_anongame *a); extern void anongame_set_result(t_anongame * a, int result); extern void anongame_set_gameresults(t_anongame * a, t_anongame_gameresult * results); extern void anongame_set_handle(t_anongame *a, std::uint32_t h); extern void anongame_set_addr(t_anongame *a, unsigned int addr); extern void anongame_set_loaded(t_anongame * a, char loaded); extern void anongame_set_joined(t_anongame * a, char joined); extern int handle_w3route_packet(t_connection * c, t_packet const * const packet); extern int handle_anongame_join(t_connection * c); /* Currently used by WOL RAL2 and YURI clients*/ extern const char * anongame_get_map_from_prefs(int queue, t_clienttag clienttag); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_gameresult.cpp000066400000000000000000000142711462640461400226110ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define ANONGAME_GAMERESULT_INTERNAL_ACCESS #include "anongame_gameresult.h" #include #include "common/eventlog.h" #include "common/xalloc.h" #include "common/packet.h" #include "common/bn_type.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern int gameresult_destroy(t_anongame_gameresult * gameresult) { if (!(gameresult)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL gameresult"); return -1; } if ((gameresult->players)) xfree((void *)gameresult->players); if ((gameresult->heroes)) xfree((void *)gameresult->heroes); xfree((void *)gameresult); return 0; } extern t_anongame_gameresult * anongame_gameresult_parse(t_packet const * const packet) { t_anongame_gameresult * gameresult; t_client_w3route_gameresult_player * player; t_client_w3route_gameresult_part2 * part2; t_client_w3route_gameresult_hero * hero; t_client_w3route_gameresult_part3 * part3; int counter, heroes_count; unsigned expectedsize; //still without hero infos unsigned int offset = 0; int result_count = bn_byte_get(packet->u.client_w3route_gameresult.number_of_results); expectedsize = sizeof(t_client_w3route_gameresult)+ sizeof(t_client_w3route_gameresult_player)* result_count + sizeof(t_client_w3route_gameresult_part2)+ sizeof(t_client_w3route_gameresult_part3); if (packet_get_size(packet) < expectedsize) { eventlog(eventlog_level_error, __FUNCTION__, "gameresult packet is smaller than expected"); return NULL; } gameresult = (t_anongame_gameresult*)xmalloc(sizeof(t_anongame_gameresult)); gameresult->players = (t_anongame_player*)xmalloc(sizeof(t_anongame_player)*result_count); gameresult->number_of_results = result_count; offset += sizeof(t_client_w3route_gameresult); for (counter = 0; counter < result_count; counter++) { player = (t_client_w3route_gameresult_player *)packet_get_raw_data_const(packet, offset); gameresult->players[counter].number = bn_byte_get(player->number); gameresult->players[counter].result = bn_int_get(player->result); gameresult->players[counter].race = bn_int_get(player->race); offset += sizeof(t_client_w3route_gameresult_player); } part2 = (t_client_w3route_gameresult_part2 *)packet_get_raw_data_const(packet, offset); gameresult->unit_score = bn_int_get(part2->unit_score); gameresult->heroes_score = bn_int_get(part2->heroes_score); gameresult->resource_score = bn_int_get(part2->resource_score); gameresult->units_produced = bn_int_get(part2->units_produced); gameresult->units_killed = bn_int_get(part2->units_killed); gameresult->buildings_produced = bn_int_get(part2->buildings_produced); gameresult->buildings_razed = bn_int_get(part2->buildings_razed); gameresult->largest_army = bn_int_get(part2->largest_army); heroes_count = bn_int_get(part2->heroes_used_count); gameresult->heroes_used_count = heroes_count; offset += sizeof(t_client_w3route_gameresult_part2); if ((heroes_count)) { gameresult->heroes = (t_anongame_hero*)xmalloc(sizeof(t_anongame_hero)*heroes_count); if (packet_get_size(packet) < expectedsize + sizeof(t_client_w3route_gameresult_hero)*heroes_count) { eventlog(eventlog_level_error, __FUNCTION__, "gameresult packet is smaller than expected"); return NULL; } for (counter = 0; counter < heroes_count; counter++) { hero = (t_client_w3route_gameresult_hero *)packet_get_raw_data_const(packet, offset); gameresult->heroes[counter].level = bn_short_get(hero->level); gameresult->heroes[counter].race_and_name = bn_int_get(hero->race_and_name); gameresult->heroes[counter].hero_xp = bn_int_get(hero->hero_xp); offset += sizeof(t_client_w3route_gameresult_hero); } } else gameresult->heroes = NULL; part3 = (t_client_w3route_gameresult_part3 *)packet_get_raw_data_const(packet, offset); gameresult->heroes_killed = bn_int_get(part3->heroes_killed); gameresult->items_obtained = bn_int_get(part3->items_obtained); gameresult->mercenaries_hired = bn_int_get(part3->mercenaries_hired); gameresult->total_hero_xp = bn_int_get(part3->total_hero_xp); gameresult->gold_mined = bn_int_get(part3->gold_mined); gameresult->lumber_harvested = bn_int_get(part3->lumber_harvested); gameresult->resources_traded_given = bn_int_get(part3->resources_traded_given); gameresult->resources_traded_taken = bn_int_get(part3->resources_traded_taken); gameresult->tech_percentage = bn_int_get(part3->tech_percentage); gameresult->gold_lost_to_upkeep = bn_int_get(part3->gold_lost_to_upkeep); return gameresult; } extern char gameresult_get_number_of_results(t_anongame_gameresult * gameresult) { assert(gameresult); return gameresult->number_of_results; } extern int gameresult_get_player_result(t_anongame_gameresult * gameresult, int player) { if (!(gameresult)) return -1; if (player >= gameresult->number_of_results) { eventlog(eventlog_level_error, __FUNCTION__, "request for invalid player number"); return -1; } return gameresult->players[player].result; } extern int gameresult_get_player_number(t_anongame_gameresult * gameresult, int player) { if (!(gameresult)) return -1; if (player >= gameresult->number_of_results) { eventlog(eventlog_level_error, __FUNCTION__, "request for invalid player number"); return -1; } return gameresult->players[player].number; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_gameresult.h000066400000000000000000000047041462640461400222560ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ANONGAME_GAMERESULT_TYPES #define INCLUDED_ANONGAME_GAMERESULT_TYPES namespace pvpgn { namespace bnetd { #ifdef ANONGAME_GAMERESULT_INTERNAL_ACCESS typedef struct { char number; int result; int race; } t_anongame_player; typedef struct { int level; int race_and_name; int hero_xp; } t_anongame_hero; #endif typedef struct anongame_gameresult_struct #ifdef ANONGAME_GAMERESULT_INTERNAL_ACCESS { char number_of_results; t_anongame_player *players; int unit_score; int heroes_score; int resource_score; int units_produced; int units_killed; int buildings_produced; int buildings_razed; int largest_army; int heroes_used_count; t_anongame_hero *heroes; int heroes_killed; int items_obtained; int mercenaries_hired; int total_hero_xp; int gold_mined; int lumber_harvested; int resources_traded_given; int resources_traded_taken; int tech_percentage; int gold_lost_to_upkeep; } #endif t_anongame_gameresult; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ANONGAME_GAMERESULT_PROTOS #define INCLUDED_ANONGAME_GAMERESULT_PROTOS #define JUST_NEED_TYPES # include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern t_anongame_gameresult * anongame_gameresult_parse(t_packet const * const packet); extern int gameresult_destroy(t_anongame_gameresult * gameresult); extern char gameresult_get_number_of_results(t_anongame_gameresult * gameresult); extern int gameresult_get_player_result(t_anongame_gameresult * gameresult, int player); extern int gameresult_get_player_number(t_anongame_gameresult * gameresult, int player); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_infos.cpp000066400000000000000000002116221462640461400215560ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define TIMER_INTERNAL_ACCESS #include "common/setup_before.h" #include "anongame_infos.h" #include #include #include #include "common/list.h" #include "common/eventlog.h" #include "common/util.h" #include "common/packet.h" #include "common/tag.h" #include "common/bn_type.h" #include "common/xalloc.h" #include "zlib.h" #include "tournament.h" #include "anongame_maplists.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static std::FILE *fp = NULL; static t_anongame_infos *anongame_infos; static int zlib_compress(void const *src, int srclen, char **dest, int *destlen); static int anongame_infos_data_load(void); static int anongame_infos_URL_init(t_anongame_infos * anongame_infos) { char **anongame_infos_URL; int i; if (!(anongame_infos)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return -1; } anongame_infos_URL = (char**)xmalloc(sizeof(char*)*anongame_infos_URL_count); for (i = 0; i < anongame_infos_URL_count; i++) anongame_infos_URL[i] = NULL; anongame_infos->anongame_infos_URL = anongame_infos_URL; return 0; } static int anongame_infos_URL_destroy(char ** anongame_infos_URL) { int i; if (!(anongame_infos_URL)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_URL"); return -1; } for (i = 0; i < anongame_infos_URL_count; i++) { if (anongame_infos_URL[i]) { xfree((void *)anongame_infos_URL[i]); } } xfree((void *)anongame_infos_URL); return 0; } static t_anongame_infos_DESC *anongame_infos_DESC_init(void) { int i; char ** descs; t_anongame_infos_DESC *anongame_infos_DESC; anongame_infos_DESC = (t_anongame_infos_DESC*)xmalloc(sizeof(t_anongame_infos_DESC)); anongame_infos_DESC->langID = NULL; descs = (char**)xmalloc(sizeof(char *)*anongame_infos_DESC_count); for (i = 0; i < anongame_infos_DESC_count; i++) descs[i] = NULL; anongame_infos_DESC->descs = descs; return anongame_infos_DESC; } static int anongame_infos_DESC_destroy(t_anongame_infos_DESC * anongame_infos_DESC) { int i; char ** descs; if (!(anongame_infos_DESC)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_DESC"); return -1; } if (anongame_infos_DESC->langID) xfree((void *)anongame_infos_DESC->langID); if ((descs = anongame_infos_DESC->descs)) { for (i = 0; i < anongame_infos_DESC_count; i++) { if ((descs[i])) xfree((void *)descs[i]); } xfree((void *)descs); } xfree((void *)anongame_infos_DESC); return 0; } static int anongame_infos_THUMBSDOWN_init(t_anongame_infos * anongame_infos) { char * anongame_infos_THUMBSDOWN; int i; if (!(anongame_infos)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return -1; } anongame_infos_THUMBSDOWN = anongame_infos->anongame_infos_THUMBSDOWN; for (i = 0; i < anongame_infos_THUMBSDOWN_count; i++) anongame_infos_THUMBSDOWN[i] = 0; return 0; } static int anongame_infos_ICON_REQ_init(t_anongame_infos * anongame_infos) { int *anongame_infos_ICON_REQ; if (!(anongame_infos)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return -1; } anongame_infos_ICON_REQ = anongame_infos->anongame_infos_ICON_REQ; anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level1] = 25; anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level2] = 250; anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level3] = 500; anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level4] = 1500; anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level1] = 25; anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level2] = 150; anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level3] = 350; anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level4] = 750; anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level5] = 1500; anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level1] = 10; anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level2] = 75; anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level3] = 150; anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level4] = 250; anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level5] = 500; return 0; } static t_anongame_infos_data_lang *anongame_infos_data_lang_init(char *langID) { t_anongame_infos_data_lang *anongame_infos_data_lang; anongame_infos_data_lang = (t_anongame_infos_data_lang*)xmalloc(sizeof(t_anongame_infos_data_lang)); anongame_infos_data_lang->langID = xstrdup(langID); anongame_infos_data_lang->desc_data = NULL; anongame_infos_data_lang->ladr_data = NULL; anongame_infos_data_lang->desc_comp_data = NULL; anongame_infos_data_lang->ladr_comp_data = NULL; anongame_infos_data_lang->desc_len = 0; anongame_infos_data_lang->ladr_len = 0; anongame_infos_data_lang->desc_comp_len = 0; anongame_infos_data_lang->ladr_comp_len = 0; return anongame_infos_data_lang; } static int anongame_infos_data_lang_destroy(t_anongame_infos_data_lang * anongame_infos_data_lang) { if (!(anongame_infos_data_lang)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_data_lang"); return -1; } if (anongame_infos_data_lang->langID) xfree((void *)anongame_infos_data_lang->langID); if (anongame_infos_data_lang->desc_data) xfree((void *)anongame_infos_data_lang->desc_data); if (anongame_infos_data_lang->ladr_data) xfree((void *)anongame_infos_data_lang->ladr_data); if (anongame_infos_data_lang->desc_comp_data) xfree((void *)anongame_infos_data_lang->desc_comp_data); if (anongame_infos_data_lang->ladr_comp_data) xfree((void *)anongame_infos_data_lang->ladr_comp_data); xfree((void *)anongame_infos_data_lang); return 0; } static int anongame_infos_data_init(t_anongame_infos * anongame_infos) { t_anongame_infos_data *anongame_infos_data; t_list *anongame_infos_data_lang; if (!(anongame_infos)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return -1; } anongame_infos_data = (t_anongame_infos_data*)xmalloc(sizeof(t_anongame_infos_data)); anongame_infos_data_lang = list_create(); anongame_infos_data->url_comp_data = NULL; anongame_infos_data->url_comp_data_115 = NULL; anongame_infos_data->map_comp_data = NULL; anongame_infos_data->type_comp_data = NULL; anongame_infos_data->desc_comp_data = NULL; anongame_infos_data->ladr_comp_data = NULL; anongame_infos_data->url_comp_len = 0; anongame_infos_data->url_comp_len_115 = 0; anongame_infos_data->map_comp_len = 0; anongame_infos_data->type_comp_len = 0; anongame_infos_data->desc_comp_len = 0; anongame_infos_data->ladr_comp_len = 0; anongame_infos->anongame_infos_data_war3 = anongame_infos_data; anongame_infos->anongame_infos_data_lang_war3 = anongame_infos_data_lang; anongame_infos_data = (t_anongame_infos_data*)xmalloc(sizeof(t_anongame_infos_data)); anongame_infos_data_lang = list_create(); anongame_infos_data->url_comp_data = NULL; anongame_infos_data->url_comp_data_115 = NULL; anongame_infos_data->map_comp_data = NULL; anongame_infos_data->type_comp_data = NULL; anongame_infos_data->desc_comp_data = NULL; anongame_infos_data->ladr_comp_data = NULL; anongame_infos_data->url_comp_len = 0; anongame_infos_data->url_comp_len_115 = 0; anongame_infos_data->map_comp_len = 0; anongame_infos_data->type_comp_len = 0; anongame_infos_data->desc_comp_len = 0; anongame_infos_data->ladr_comp_len = 0; anongame_infos->anongame_infos_data_w3xp = anongame_infos_data; anongame_infos->anongame_infos_data_lang_w3xp = anongame_infos_data_lang; return 0; } static int anongame_infos_data_destroy(t_anongame_infos_data * anongame_infos_data, t_list * anongame_infos_data_lang) { t_elem *curr; t_anongame_infos_data_lang *entry; if (anongame_infos_data->url_comp_data) xfree((void *)anongame_infos_data->url_comp_data); if (anongame_infos_data->url_comp_data_115) xfree((void *)anongame_infos_data->url_comp_data_115); if (anongame_infos_data->map_comp_data) xfree((void *)anongame_infos_data->map_comp_data); if (anongame_infos_data->type_comp_data) xfree((void *)anongame_infos_data->type_comp_data); if (anongame_infos_data->desc_comp_data) xfree((void *)anongame_infos_data->desc_comp_data); if (anongame_infos_data->ladr_comp_data) xfree((void *)anongame_infos_data->ladr_comp_data); xfree((void *)anongame_infos_data); if (anongame_infos_data_lang) { LIST_TRAVERSE(anongame_infos_data_lang, curr) { if (!(entry = (t_anongame_infos_data_lang*)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); else { anongame_infos_data_lang_destroy(entry); } list_remove_elem(anongame_infos_data_lang, &curr); } list_destroy(anongame_infos_data_lang); } return 0; } t_anongame_infos *anongame_infos_init(void) { t_anongame_infos *anongame_infos; anongame_infos = (t_anongame_infos*)xmalloc(sizeof(t_anongame_infos)); if (anongame_infos_URL_init(anongame_infos) != 0) { xfree((void *)anongame_infos); return NULL; } if (anongame_infos_THUMBSDOWN_init(anongame_infos) != 0) { anongame_infos_URL_destroy(anongame_infos->anongame_infos_URL); xfree((void *)anongame_infos); return NULL; } if (anongame_infos_ICON_REQ_init(anongame_infos) != 0) { anongame_infos_URL_destroy(anongame_infos->anongame_infos_URL); xfree((void *)anongame_infos); return NULL; } if (anongame_infos_data_init(anongame_infos) != 0) { anongame_infos_URL_destroy(anongame_infos->anongame_infos_URL); xfree((void *)anongame_infos); return NULL; } anongame_infos->anongame_infos_DESC = NULL; anongame_infos->anongame_infos_DESC_list = list_create(); return anongame_infos; } static int anongame_infos_destroy(t_anongame_infos * anongame_infos) { t_elem *curr; t_anongame_infos_DESC *entry; if (!(anongame_infos)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return -1; } if (anongame_infos->anongame_infos_DESC_list) { LIST_TRAVERSE(anongame_infos->anongame_infos_DESC_list, curr) { if (!(entry = (t_anongame_infos_DESC*)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); else { anongame_infos_DESC_destroy(entry); } list_remove_elem(anongame_infos->anongame_infos_DESC_list, &curr); } list_destroy(anongame_infos->anongame_infos_DESC_list); anongame_infos->anongame_infos_DESC_list = NULL; } anongame_infos_DESC_destroy(anongame_infos->anongame_infos_DESC); anongame_infos_URL_destroy(anongame_infos->anongame_infos_URL); anongame_infos_data_destroy(anongame_infos->anongame_infos_data_war3, anongame_infos->anongame_infos_data_lang_war3); anongame_infos_data_destroy(anongame_infos->anongame_infos_data_w3xp, anongame_infos->anongame_infos_data_lang_w3xp); xfree((void *)anongame_infos); return 0; } static int anongame_infos_set_str(char **dst, const char *src, const char *errstr) { char *temp; if (!(src)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL {}", errstr); return -1; } temp = xstrdup(src); if (*dst) xfree((void *)*dst); *dst = temp; return 0; } static int anongame_infos_URL_set_URL(int member, const char *URL) { char **anongame_infos_URLs; if (!(anongame_infos_URLs = anongame_infos->anongame_infos_URL)) { eventlog(eventlog_level_error, __FUNCTION__, "detected NULL anongame_infos_URL"); return -1; } return anongame_infos_set_str(&anongame_infos_URLs[member], URL, "URL"); } extern char *anongame_infos_URL_get_URL(int member) { char **anongame_infos_URLs; if (!(anongame_infos_URLs = anongame_infos->anongame_infos_URL)) return NULL; else return anongame_infos_URLs[member]; } static int anongame_infos_DESC_set_DESC(t_anongame_infos_DESC * anongame_infos_DESC, int member, const char *DESC) { char ** descs; if (!(anongame_infos_DESC)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_DESC"); return -1; } if (!(descs = anongame_infos_DESC->descs)) { eventlog(eventlog_level_error, __FUNCTION__, "anongame_infos_DESC had NULL descs"); return -1; } return anongame_infos_set_str(&descs[member], DESC, "DESC"); } static t_anongame_infos_DESC *anongame_infos_get_anongame_infos_DESC_by_langID(t_anongame_infos * anongame_infos, char *langID) { t_elem *curr; t_anongame_infos_DESC *entry; if (!(anongame_infos)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return NULL; } if (!(langID)) return anongame_infos->anongame_infos_DESC; if (!(anongame_infos->anongame_infos_DESC_list)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_DESC_list - default values"); return anongame_infos->anongame_infos_DESC; } LIST_TRAVERSE(anongame_infos->anongame_infos_DESC_list, curr) { if (!(entry = (t_anongame_infos_DESC *)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); else { if ((entry->langID) && (std::strcmp(entry->langID, langID) == 0)) return entry; } } return anongame_infos->anongame_infos_DESC; } extern char *anongame_infos_DESC_get_DESC(char *langID, int member) { char *result; t_anongame_infos_DESC * DESC; if ((DESC = anongame_infos_get_anongame_infos_DESC_by_langID(anongame_infos, langID))) if ((DESC->descs) && (result = DESC->descs[member])) return result; if ((DESC = anongame_infos->anongame_infos_DESC)) if ((DESC->descs) && (result = DESC->descs[member])) return result; return NULL; } /**********/ extern char *anongame_infos_get_short_desc(char *langID, int queue) { int member = 0; switch (queue) { case ANONGAME_TYPE_1V1: member = gametype_1v1_short; break; case ANONGAME_TYPE_2V2: member = gametype_2v2_short; break; case ANONGAME_TYPE_3V3: member = gametype_3v3_short; break; case ANONGAME_TYPE_4V4: member = gametype_4v4_short; break; case ANONGAME_TYPE_5V5: member = gametype_5v5_short; break; case ANONGAME_TYPE_6V6: member = gametype_6v6_short; break; case ANONGAME_TYPE_2V2V2: member = gametype_2v2v2_short; break; case ANONGAME_TYPE_3V3V3: member = gametype_3v3v3_short; break; case ANONGAME_TYPE_4V4V4: member = gametype_4v4v4_short; break; case ANONGAME_TYPE_2V2V2V2: member = gametype_2v2v2v2_short; break; case ANONGAME_TYPE_3V3V3V3: member = gametype_3v3v3v3_short; break; case ANONGAME_TYPE_SMALL_FFA: member = gametype_sffa_short; break; case ANONGAME_TYPE_TEAM_FFA: member = gametype_tffa_short; break; case ANONGAME_TYPE_AT_2V2: member = gametype_2v2_short; break; case ANONGAME_TYPE_AT_3V3: member = gametype_3v3_short; break; case ANONGAME_TYPE_AT_4V4: member = gametype_4v4_short; break; case ANONGAME_TYPE_AT_2V2V2: member = gametype_2v2v2_short; break; case ANONGAME_TYPE_TY: return tournament_get_format(); default: eventlog(eventlog_level_error, __FUNCTION__, "invalid queue ({})", queue); return NULL; } return anongame_infos_DESC_get_DESC(langID, member); } extern char *anongame_infos_get_long_desc(char *langID, int queue) { int member = 0; switch (queue) { case ANONGAME_TYPE_1V1: member = gametype_1v1_long; break; case ANONGAME_TYPE_2V2: member = gametype_2v2_long; break; case ANONGAME_TYPE_3V3: member = gametype_3v3_long; break; case ANONGAME_TYPE_4V4: member = gametype_4v4_long; break; case ANONGAME_TYPE_5V5: member = gametype_5v5_long; break; case ANONGAME_TYPE_6V6: member = gametype_6v6_long; break; case ANONGAME_TYPE_2V2V2: member = gametype_2v2v2_long; break; case ANONGAME_TYPE_3V3V3: member = gametype_3v3v3_long; break; case ANONGAME_TYPE_4V4V4: member = gametype_4v4v4_long; break; case ANONGAME_TYPE_2V2V2V2: member = gametype_2v2v2v2_long; break; case ANONGAME_TYPE_3V3V3V3: member = gametype_3v3v3v3_long; break; case ANONGAME_TYPE_SMALL_FFA: member = gametype_sffa_long; break; case ANONGAME_TYPE_TEAM_FFA: member = gametype_tffa_long; break; case ANONGAME_TYPE_AT_2V2: member = gametype_2v2_long; break; case ANONGAME_TYPE_AT_3V3: member = gametype_3v3_long; break; case ANONGAME_TYPE_AT_4V4: member = gametype_4v4_long; break; case ANONGAME_TYPE_AT_2V2V2: member = gametype_2v2v2_long; break; case ANONGAME_TYPE_TY: return tournament_get_sponsor();; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid queue ({})", queue); return NULL; } return anongame_infos_DESC_get_DESC(langID, member); } /**********/ static int anongame_infos_THUMBSDOWN_set_THUMBSDOWN(char * anongame_infos_THUMBSDOWN, int member, char value) { if (!anongame_infos_THUMBSDOWN) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_THUMBSDOWN"); return -1; } anongame_infos_THUMBSDOWN[member] = value; return 0; } /**********/ extern char anongame_infos_get_thumbsdown(int queue) { int member = 0; switch (queue) { case ANONGAME_TYPE_1V1: member = PG_1v1; break; case ANONGAME_TYPE_2V2: member = PG_2v2; break; case ANONGAME_TYPE_3V3: member = PG_3v3; break; case ANONGAME_TYPE_4V4: member = PG_4v4; break; case ANONGAME_TYPE_5V5: member = PG_5v5; break; case ANONGAME_TYPE_6V6: member = PG_6v6; break; case ANONGAME_TYPE_2V2V2: member = PG_2v2v2; break; case ANONGAME_TYPE_3V3V3: member = PG_3v3v3; break; case ANONGAME_TYPE_4V4V4: member = PG_4v4v4; break; case ANONGAME_TYPE_2V2V2V2: member = PG_2v2v2v2; break; case ANONGAME_TYPE_3V3V3V3: member = PG_3v3v3v3; break; case ANONGAME_TYPE_SMALL_FFA: member = PG_ffa; break; case ANONGAME_TYPE_TEAM_FFA: member = AT_ffa; break; case ANONGAME_TYPE_AT_2V2: member = AT_2v2; break; case ANONGAME_TYPE_AT_3V3: member = AT_3v3; break; case ANONGAME_TYPE_AT_4V4: member = AT_4v4; break; case ANONGAME_TYPE_AT_2V2V2: member = AT_2v2v2; break; case ANONGAME_TYPE_TY: return tournament_get_thumbs_down(); default: eventlog(eventlog_level_error, __FUNCTION__, "invalid queue ({})", queue); return 1; } return anongame_infos->anongame_infos_THUMBSDOWN[member]; } /**********/ static int anongame_infos_ICON_REQ_set_REQ(t_anongame_infos * anongame_infos, int member, int value) { int * anongame_infos_ICON_REQ; if (!anongame_infos) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return -1; } anongame_infos_ICON_REQ = anongame_infos->anongame_infos_ICON_REQ; anongame_infos_ICON_REQ[member] = value; return 0; } extern short anongame_infos_get_ICON_REQ(int Level, t_clienttag clienttag) { switch (clienttag) { case CLIENTTAG_WARCRAFT3_UINT: switch (Level) { case 0: return 0; case 1: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level1]; case 2: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level2]; case 3: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level3]; case 4: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_WAR3_Level4]; default: return -1; } case CLIENTTAG_WAR3XP_UINT: switch (Level) { case 0: return 0; case 1: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level1]; case 2: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level2]; case 3: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level3]; case 4: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level4]; case 5: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_W3XP_Level5]; default: return -1; } default: eventlog(eventlog_level_error, __FUNCTION__, "invalid clienttag"); return -1; } } extern short anongame_infos_get_ICON_REQ_TOURNEY(int Level) { switch (Level) { case 0: return 0; case 1: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level1]; case 2: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level2]; case 3: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level3]; case 4: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level4]; case 5: return anongame_infos->anongame_infos_ICON_REQ[ICON_REQ_TRNY_Level5]; default: return -1; } } /**********/ extern char *anongame_infos_data_get_url(t_clienttag clienttag, int versionid, int *len) { if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { if (versionid <= 0x0000000E) { (*len) = anongame_infos->anongame_infos_data_war3->url_comp_len; return anongame_infos->anongame_infos_data_war3->url_comp_data; } else { (*len) = anongame_infos->anongame_infos_data_war3->url_comp_len_115; return anongame_infos->anongame_infos_data_war3->url_comp_data_115; } } else { if (versionid <= 0x0000000E) { (*len) = anongame_infos->anongame_infos_data_w3xp->url_comp_len; return anongame_infos->anongame_infos_data_w3xp->url_comp_data; } else { (*len) = anongame_infos->anongame_infos_data_w3xp->url_comp_len_115; return anongame_infos->anongame_infos_data_w3xp->url_comp_data_115; } } } extern char *anongame_infos_data_get_map(t_clienttag clienttag, int versionid, int *len) { if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { (*len) = anongame_infos->anongame_infos_data_war3->map_comp_len; return anongame_infos->anongame_infos_data_war3->map_comp_data; } else { (*len) = anongame_infos->anongame_infos_data_w3xp->map_comp_len; return anongame_infos->anongame_infos_data_w3xp->map_comp_data; } } extern char *anongame_infos_data_get_type(t_clienttag clienttag, int versionid, int *len) { if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { (*len) = anongame_infos->anongame_infos_data_war3->type_comp_len; return anongame_infos->anongame_infos_data_war3->type_comp_data; } else { (*len) = anongame_infos->anongame_infos_data_w3xp->type_comp_len; return anongame_infos->anongame_infos_data_w3xp->type_comp_data; } } extern char *anongame_infos_data_get_desc(char const *langID, t_clienttag clienttag, int versionid, int *len) { t_elem *curr; t_anongame_infos_data_lang *entry; if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { if (langID != NULL) { LIST_TRAVERSE(anongame_infos->anongame_infos_data_lang_war3, curr) { if ((entry = (t_anongame_infos_data_lang*)elem_get_data(curr)) && std::strcmp(entry->langID, langID) == 0) { (*len) = entry->desc_comp_len; return entry->desc_comp_data; } } } (*len) = anongame_infos->anongame_infos_data_war3->desc_comp_len; return anongame_infos->anongame_infos_data_war3->desc_comp_data; } else { if (langID != NULL) { LIST_TRAVERSE(anongame_infos->anongame_infos_data_lang_w3xp, curr) { if ((entry = (t_anongame_infos_data_lang*)elem_get_data(curr)) && std::strcmp(entry->langID, langID) == 0) { (*len) = entry->desc_comp_len; return entry->desc_comp_data; } } } (*len) = anongame_infos->anongame_infos_data_w3xp->desc_comp_len; return anongame_infos->anongame_infos_data_w3xp->desc_comp_data; } } extern char *anongame_infos_data_get_ladr(char const *langID, t_clienttag clienttag, int versionid, int *len) { t_elem *curr; t_anongame_infos_data_lang *entry; if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { if (langID != NULL) { LIST_TRAVERSE(anongame_infos->anongame_infos_data_lang_war3, curr) { if ((entry = (t_anongame_infos_data_lang*)elem_get_data(curr)) && std::strcmp(entry->langID, langID) == 0) { (*len) = entry->ladr_comp_len; return entry->ladr_comp_data; } } } (*len) = anongame_infos->anongame_infos_data_war3->ladr_comp_len; return anongame_infos->anongame_infos_data_war3->ladr_comp_data; } else { if (langID != NULL) { LIST_TRAVERSE(anongame_infos->anongame_infos_data_lang_w3xp, curr) { if ((entry = (t_anongame_infos_data_lang*)elem_get_data(curr)) && std::strcmp(entry->langID, langID) == 0) { (*len) = entry->ladr_comp_len; return entry->ladr_comp_data; } } } (*len) = anongame_infos->anongame_infos_data_w3xp->ladr_comp_len; return anongame_infos->anongame_infos_data_w3xp->ladr_comp_data; } } /**********/ static void anongame_infos_set_defaults(t_anongame_infos * anongame_infos) { char ** anongame_infos_URL; t_anongame_infos_DESC *anongame_infos_DESC; char ** anongame_infos_DESCs; if (!(anongame_infos)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos"); return; } anongame_infos_URL = anongame_infos->anongame_infos_URL; anongame_infos_DESC = anongame_infos->anongame_infos_DESC; if (!(anongame_infos_URL)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_URL, trying to init"); if (anongame_infos_URL_init(anongame_infos) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to init... PANIC!"); return; } } if (!(anongame_infos_DESC)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_infos_DESC, trying to init"); if (!(anongame_infos_DESC = anongame_infos_DESC_init())) { eventlog(eventlog_level_error, __FUNCTION__, "failed to init... PANIC!"); return; } else anongame_infos->anongame_infos_DESC = anongame_infos_DESC; } // now set default values if (!(anongame_infos_URL[URL_server])) anongame_infos_URL_set_URL(URL_server, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_player])) anongame_infos_URL_set_URL(URL_player, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_tourney])) anongame_infos_URL_set_URL(URL_tourney, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_clan])) anongame_infos_URL_set_URL(URL_clan, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_PG_1v1])) anongame_infos_URL_set_URL(URL_ladder_PG_1v1, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_PG_ffa])) anongame_infos_URL_set_URL(URL_ladder_PG_ffa, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_PG_team])) anongame_infos_URL_set_URL(URL_ladder_PG_team, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_AT_2v2])) anongame_infos_URL_set_URL(URL_ladder_AT_2v2, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_AT_3v3])) anongame_infos_URL_set_URL(URL_ladder_AT_3v3, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_AT_4v4])) anongame_infos_URL_set_URL(URL_ladder_AT_4v4, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_clan_1v1])) anongame_infos_URL_set_URL(URL_ladder_clan_1v1, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_clan_2v2])) anongame_infos_URL_set_URL(URL_ladder_clan_2v2, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_clan_3v3])) anongame_infos_URL_set_URL(URL_ladder_clan_3v3, PVPGN_DEFAULT_URL); if (!(anongame_infos_URL[URL_ladder_clan_4v4])) anongame_infos_URL_set_URL(URL_ladder_clan_4v4, PVPGN_DEFAULT_URL); if (!(anongame_infos_DESCs = anongame_infos_DESC->descs)) return; if (!(anongame_infos_DESCs[ladder_PG_1v1_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_PG_1v1_desc, PVPGN_PG_1V1_DESC); if (!(anongame_infos_DESCs[ladder_PG_ffa_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_PG_ffa_desc, PVPGN_PG_FFA_DESC); if (!(anongame_infos_DESCs[ladder_PG_team_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_PG_team_desc, PVPGN_PG_TEAM_DESC); if (!(anongame_infos_DESCs[ladder_AT_2v2_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_AT_2v2_desc, PVPGN_AT_2V2_DESC); if (!(anongame_infos_DESCs[ladder_AT_3v3_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_AT_3v3_desc, PVPGN_AT_3V3_DESC); if (!(anongame_infos_DESCs[ladder_AT_4v4_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_AT_4v4_desc, PVPGN_AT_4V4_DESC); if (!(anongame_infos_DESCs[ladder_clan_1v1_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_clan_1v1_desc, PVPGN_CLAN_1V1_DESC); if (!(anongame_infos_DESCs[ladder_clan_2v2_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_clan_2v2_desc, PVPGN_CLAN_2V2_DESC); if (!(anongame_infos_DESCs[ladder_clan_3v3_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_clan_3v3_desc, PVPGN_CLAN_3V3_DESC); if (!(anongame_infos_DESCs[ladder_clan_4v4_desc])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, ladder_clan_4v4_desc, PVPGN_CLAN_4V4_DESC); if (!(anongame_infos_DESCs[gametype_1v1_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_1v1_short, PVPGN_1V1_GT_DESC); if (!(anongame_infos_DESCs[gametype_1v1_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_1v1_long, PVPGN_1V1_GT_LONG); if (!(anongame_infos_DESCs[gametype_2v2_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_2v2_short, PVPGN_2V2_GT_DESC); if (!(anongame_infos_DESCs[gametype_2v2_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_2v2_long, PVPGN_2V2_GT_LONG); if (!(anongame_infos_DESCs[gametype_3v3_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_3v3_short, PVPGN_3V3_GT_DESC); if (!(anongame_infos_DESCs[gametype_3v3_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_3v3_long, PVPGN_3V3_GT_LONG); if (!(anongame_infos_DESCs[gametype_4v4_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_4v4_short, PVPGN_4V4_GT_DESC); if (!(anongame_infos_DESCs[gametype_4v4_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_4v4_long, PVPGN_4V4_GT_LONG); if (!(anongame_infos_DESCs[gametype_sffa_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_sffa_short, PVPGN_SFFA_GT_DESC); if (!(anongame_infos_DESCs[gametype_sffa_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_sffa_long, PVPGN_SFFA_GT_LONG); if (!(anongame_infos_DESCs[gametype_tffa_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_tffa_short, PVPGN_TFFA_GT_DESC); if (!(anongame_infos_DESCs[gametype_tffa_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_tffa_long, PVPGN_TFFA_GT_LONG); if (!(anongame_infos_DESCs[gametype_2v2v2_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_2v2v2_short, PVPGN_2V2V2_GT_DESC); if (!(anongame_infos_DESCs[gametype_2v2v2_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_2v2v2_long, PVPGN_2V2V2_GT_LONG); if (!(anongame_infos_DESCs[gametype_3v3v3_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_3v3v3_short, PVPGN_3V3V3_GT_DESC); if (!(anongame_infos_DESCs[gametype_3v3v3_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_3v3v3_long, PVPGN_3V3V3_GT_LONG); if (!(anongame_infos_DESCs[gametype_4v4v4_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_4v4v4_short, PVPGN_4V4V4_GT_DESC); if (!(anongame_infos_DESCs[gametype_4v4v4_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_4v4v4_long, PVPGN_4V4V4_GT_LONG); if (!(anongame_infos_DESCs[gametype_2v2v2v2_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_2v2v2v2_short, PVPGN_2V2V2V2_GT_DESC); if (!(anongame_infos_DESCs[gametype_2v2v2v2_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_2v2v2v2_long, PVPGN_2V2V2V2_GT_LONG); if (!(anongame_infos_DESCs[gametype_3v3v3v3_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_3v3v3v3_short, PVPGN_3V3V3V3_GT_DESC); if (!(anongame_infos_DESCs[gametype_3v3v3v3_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_3v3v3v3_long, PVPGN_3V3V3V3_GT_LONG); if (!(anongame_infos_DESCs[gametype_5v5_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_5v5_short, PVPGN_5V5_GT_DESC); if (!(anongame_infos_DESCs[gametype_5v5_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_5v5_long, PVPGN_5V5_GT_LONG); if (!(anongame_infos_DESCs[gametype_6v6_short])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_6v6_short, PVPGN_6V6_GT_DESC); if (!(anongame_infos_DESCs[gametype_6v6_long])) anongame_infos_DESC_set_DESC(anongame_infos_DESC, gametype_6v6_long, PVPGN_6V6_GT_LONG); } typedef struct { const char *anongame_infos_URL_string; int member; } t_anongame_infos_URL_table_row; typedef struct { const char *anongame_infos_DESC_string; int member; } t_anongame_infos_DESC_table_row; typedef struct { const char *anongame_infos_THUMBSDOWN_string; int member; } t_anongame_infos_THUMBSDOWN_table_row; typedef struct { const char *anongame_infos_ICON_REQ_WAR3_string; int member; } t_anongame_infos_ICON_REQ_WAR3_table_row; typedef struct { const char *anongame_infos_ICON_REQ_W3XP_string; int member; } t_anongame_infos_ICON_REQ_W3XP_table_row; typedef struct { const char *anongame_infos_ICON_REQ_TOURNEY_string; int member; } t_anongame_infos_ICON_REQ_TOURNEY_table_row; static const t_anongame_infos_URL_table_row URL_handler_table[] = { { "server_URL", URL_server }, { "player_URL", URL_player }, { "tourney_URL", URL_tourney }, { "clan_URL", URL_clan }, { "ladder_PG_1v1_URL", URL_ladder_PG_1v1 }, { "ladder_PG_ffa_URL", URL_ladder_PG_ffa }, { "ladder_PG_team_URL", URL_ladder_PG_team }, { "ladder_AT_2v2_URL", URL_ladder_AT_2v2 }, { "ladder_AT_3v3_URL", URL_ladder_AT_3v3 }, { "ladder_AT_4v4_URL", URL_ladder_AT_4v4 }, { "ladder_clan_1v1_URL", URL_ladder_clan_1v1 }, { "ladder_clan_2v2_URL", URL_ladder_clan_2v2 }, { "ladder_clan_3v3_URL", URL_ladder_clan_3v3 }, { "ladder_clan_4v4_URL", URL_ladder_clan_4v4 }, { NULL, -1 } }; static const t_anongame_infos_DESC_table_row DESC_handler_table[] = { { "ladder_PG_1v1_desc", ladder_PG_1v1_desc }, { "ladder_PG_ffa_desc", ladder_PG_ffa_desc }, { "ladder_PG_team_desc", ladder_PG_team_desc }, { "ladder_AT_2v2_desc", ladder_AT_2v2_desc }, { "ladder_AT_3v3_desc", ladder_AT_3v3_desc }, { "ladder_AT_4v4_desc", ladder_AT_4v4_desc }, { "ladder_clan_1v1_desc", ladder_clan_1v1_desc }, { "ladder_clan_2v2_desc", ladder_clan_2v2_desc }, { "ladder_clan_3v3_desc", ladder_clan_3v3_desc }, { "ladder_clan_4v4_desc", ladder_clan_4v4_desc }, { "gametype_1v1_short", gametype_1v1_short }, { "gametype_1v1_long", gametype_1v1_long }, { "gametype_2v2_short", gametype_2v2_short }, { "gametype_2v2_long", gametype_2v2_long }, { "gametype_3v3_short", gametype_3v3_short }, { "gametype_3v3_long", gametype_3v3_long }, { "gametype_4v4_short", gametype_4v4_short }, { "gametype_4v4_long", gametype_4v4_long }, { "gametype_sffa_short", gametype_sffa_short }, { "gametype_sffa_long", gametype_sffa_long }, { "gametype_tffa_short", gametype_tffa_short }, { "gametype_tffa_long", gametype_tffa_long }, { "gametype_2v2v2_short", gametype_2v2v2_short }, { "gametype_2v2v2_long", gametype_2v2v2_long }, { "gametype_3v3v3_short", gametype_3v3v3_short }, { "gametype_3v3v3_long", gametype_3v3v3_long }, { "gametype_4v4v4_short", gametype_4v4v4_short }, { "gametype_4v4v4_long", gametype_4v4v4_long }, { "gametype_2v2v2v2_short", gametype_2v2v2v2_short }, { "gametype_2v2v2v2_long", gametype_2v2v2v2_long }, { "gametype_3v3v3v3_short", gametype_3v3v3v3_short }, { "gametype_3v3v3v3_long", gametype_3v3v3v3_long }, { "gametype_5v5_short", gametype_5v5_short }, { "gametype_5v5_long", gametype_5v5_long }, { "gametype_6v6_short", gametype_6v6_short }, { "gametype_6v6_long", gametype_6v6_long }, { NULL, -1 } }; static const t_anongame_infos_THUMBSDOWN_table_row THUMBSDOWN_handler_table[] = { { "PG_1v1", PG_1v1 }, { "PG_2v2", PG_2v2 }, { "PG_3v3", PG_3v3 }, { "PG_4v4", PG_4v4 }, { "PG_ffa", PG_ffa }, { "AT_2v2", AT_2v2 }, { "AT_3v3", AT_3v3 }, { "AT_4v4", AT_4v4 }, { "AT_ffa", AT_ffa }, { "PG_5v5", PG_5v5 }, { "PG_6v6", PG_6v6 }, { "PG_2v2v2", PG_2v2v2 }, { "PG_3v3v3", PG_3v3v3 }, { "PG_4v4v4", PG_4v4v4 }, { "PG_2v2v2v2", PG_2v2v2v2 }, { "PG_3v3v3v3", PG_3v3v3v3 }, { "AT_2v2v2", AT_2v2v2 }, { NULL, -1 } }; static const t_anongame_infos_ICON_REQ_WAR3_table_row ICON_REQ_WAR3_handler_table[] = { { "Level1", ICON_REQ_WAR3_Level1 }, { "Level2", ICON_REQ_WAR3_Level2 }, { "Level3", ICON_REQ_WAR3_Level3 }, { "Level4", ICON_REQ_WAR3_Level4 }, { NULL, -1 } }; static const t_anongame_infos_ICON_REQ_W3XP_table_row ICON_REQ_W3XP_handler_table[] = { { "Level1", ICON_REQ_W3XP_Level1 }, { "Level2", ICON_REQ_W3XP_Level2 }, { "Level3", ICON_REQ_W3XP_Level3 }, { "Level4", ICON_REQ_W3XP_Level4 }, { "Level5", ICON_REQ_W3XP_Level5 }, { NULL, -1 } }; static const t_anongame_infos_ICON_REQ_TOURNEY_table_row ICON_REQ_TOURNEY_handler_table[] = { { "Level1", ICON_REQ_TRNY_Level1 }, { "Level2", ICON_REQ_TRNY_Level2 }, { "Level3", ICON_REQ_TRNY_Level3 }, { "Level4", ICON_REQ_TRNY_Level4 }, { "Level5", ICON_REQ_TRNY_Level5 }, { NULL, -1 } }; typedef enum { parse_UNKNOWN, parse_URL, parse_DESC, parse_THUMBSDOWN, parse_ICON_REQ_WAR3, parse_ICON_REQ_W3XP, parse_ICON_REQ_TOURNEY } t_parse_mode; typedef enum { changed, unchanged } t_parse_state; static t_parse_mode switch_parse_mode(char *text, char *langID) { if (!(text)) return parse_UNKNOWN; else if (std::strcmp(text, "[URL]") == 0) return parse_URL; else if (std::strcmp(text, "[THUMBS_DOWN_LIMIT]") == 0) return parse_THUMBSDOWN; else if (std::strcmp(text, "[ICON_REQUIRED_RACE_WINS_WAR3]") == 0) return parse_ICON_REQ_WAR3; else if (std::strcmp(text, "[ICON_REQUIRED_RACE_WINS_W3XP]") == 0) return parse_ICON_REQ_W3XP; else if (std::strcmp(text, "[ICON_REQUIRED_TOURNEY_WINS]") == 0) return parse_ICON_REQ_TOURNEY; else if (std::strcmp(text, "[DEFAULT_DESC]") == 0) { langID[0] = '\0'; return parse_DESC; } else if (std::strlen(text) == 6) { std::strncpy(langID, &(text[1]), 4); langID[4] = '\0'; return parse_DESC; } else eventlog(eventlog_level_error, __FUNCTION__, "got invalid section name: {}", text); return parse_UNKNOWN; } extern int anongame_infos_load(char const *filename) { unsigned int line; unsigned int pos; char *buff; char *temp; char langID[5]; t_parse_mode parse_mode = parse_UNKNOWN; t_parse_state parse_state = unchanged; t_anongame_infos_DESC *anongame_infos_DESC = NULL; char *pointer; char *variable; char *value = NULL; t_anongame_infos_DESC_table_row const *DESC_table_row; t_anongame_infos_URL_table_row const *URL_table_row; t_anongame_infos_THUMBSDOWN_table_row const *THUMBSDOWN_table_row; t_anongame_infos_ICON_REQ_WAR3_table_row const *ICON_REQ_WAR3_table_row; t_anongame_infos_ICON_REQ_W3XP_table_row const *ICON_REQ_W3XP_table_row; t_anongame_infos_ICON_REQ_TOURNEY_table_row const *ICON_REQ_TOURNEY_table_row; int int_value; char char_value; langID[0] = '\0'; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(anongame_infos = anongame_infos_init())) { eventlog(eventlog_level_error, __FUNCTION__, "could not init anongame_infos"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, "anongameinfo_load", "could not open file \"{}\" for reading (std::fopen: {}), using default values", filename, std::strerror(errno)); goto anongame_infos_loading_failure; } for (line = 1; (buff = file_get_line(fp)); line++) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if ((temp = std::strrchr(buff, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } if ((buff[0] == '[') && (buff[std::strlen(buff) - 1] == ']')) { if ((parse_state == unchanged) && (anongame_infos_DESC != NULL)) { if (langID[0] != '\0') list_append_data(anongame_infos->anongame_infos_DESC_list, anongame_infos_DESC); else { if (anongame_infos->anongame_infos_DESC == NULL) anongame_infos->anongame_infos_DESC = anongame_infos_DESC; else { eventlog(eventlog_level_error, __FUNCTION__, "found another default_DESC block, deleting previous"); anongame_infos_DESC_destroy(anongame_infos->anongame_infos_DESC); anongame_infos->anongame_infos_DESC = anongame_infos_DESC; } } anongame_infos_DESC = NULL; } parse_mode = switch_parse_mode(buff, langID); parse_state = changed; } else if (buff[0] != '\0') switch (parse_mode) { case parse_UNKNOWN: { if ((buff[0] != '[') || (buff[std::strlen(buff) - 1] != ']')) { eventlog(eventlog_level_error, __FUNCTION__, "expected [] section start, but found {} on line {}", buff, line); } else { parse_mode = switch_parse_mode(buff, langID); parse_state = changed; } break; } case parse_URL: { parse_state = unchanged; variable = buff; pointer = std::strchr(variable, '='); for (pointer--; pointer[0] == ' '; pointer--); pointer[1] = '\0'; pointer++; pointer++; pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; for (URL_table_row = URL_handler_table; URL_table_row->anongame_infos_URL_string != NULL; URL_table_row++) if (std::strcmp(URL_table_row->anongame_infos_URL_string, variable) == 0) { if (URL_table_row->member != -1) anongame_infos_URL_set_URL(URL_table_row->member, value); } break; } case parse_DESC: { if (parse_state == changed) { anongame_infos_DESC = anongame_infos_DESC_init(); parse_state = unchanged; eventlog(eventlog_level_info, __FUNCTION__, "got langID: [{}]", langID); if (langID[0] != '\0') anongame_infos_DESC->langID = xstrdup(langID); } variable = buff; pointer = std::strchr(variable, '='); for (pointer--; pointer[0] == ' '; pointer--); pointer[1] = '\0'; pointer++; pointer++; pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; for (DESC_table_row = DESC_handler_table; DESC_table_row->anongame_infos_DESC_string != NULL; DESC_table_row++) if (std::strcmp(DESC_table_row->anongame_infos_DESC_string, variable) == 0) { if (DESC_table_row->member != -1) anongame_infos_DESC_set_DESC(anongame_infos_DESC, DESC_table_row->member, value); } break; } case parse_THUMBSDOWN: { parse_state = unchanged; variable = buff; pointer = std::strchr(variable, '='); for (pointer--; pointer[0] == ' '; pointer--); pointer[1] = '\0'; pointer++; pointer++; pointer = std::strchr(pointer, '='); pointer++; int_value = std::atoi(pointer); if (int_value < 0) int_value = 0; if (int_value > 127) int_value = 127; char_value = (char)int_value; for (THUMBSDOWN_table_row = THUMBSDOWN_handler_table; THUMBSDOWN_table_row->anongame_infos_THUMBSDOWN_string != NULL; THUMBSDOWN_table_row++) if (std::strcmp(THUMBSDOWN_table_row->anongame_infos_THUMBSDOWN_string, variable) == 0) { if (THUMBSDOWN_table_row->member != -1) anongame_infos_THUMBSDOWN_set_THUMBSDOWN(anongame_infos->anongame_infos_THUMBSDOWN, THUMBSDOWN_table_row->member, char_value); } break; } case parse_ICON_REQ_WAR3: { parse_state = unchanged; variable = buff; pointer = std::strchr(variable, '='); for (pointer--; pointer[0] == ' '; pointer--); pointer[1] = '\0'; pointer++; pointer++; pointer = std::strchr(pointer, '='); pointer++; int_value = std::atoi(pointer); if (int_value < 0) int_value = 0; for (ICON_REQ_WAR3_table_row = ICON_REQ_WAR3_handler_table; ICON_REQ_WAR3_table_row->anongame_infos_ICON_REQ_WAR3_string != NULL; ICON_REQ_WAR3_table_row++) if (std::strcmp(ICON_REQ_WAR3_table_row->anongame_infos_ICON_REQ_WAR3_string, variable) == 0) { if (ICON_REQ_WAR3_table_row->member != -1) anongame_infos_ICON_REQ_set_REQ(anongame_infos, ICON_REQ_WAR3_table_row->member, int_value); } break; } case parse_ICON_REQ_W3XP: { parse_state = unchanged; variable = buff; pointer = std::strchr(variable, '='); for (pointer--; pointer[0] == ' '; pointer--); pointer[1] = '\0'; pointer++; pointer++; pointer = std::strchr(pointer, '='); pointer++; int_value = std::atoi(pointer); if (int_value < 0) int_value = 0; for (ICON_REQ_W3XP_table_row = ICON_REQ_W3XP_handler_table; ICON_REQ_W3XP_table_row->anongame_infos_ICON_REQ_W3XP_string != NULL; ICON_REQ_W3XP_table_row++) if (std::strcmp(ICON_REQ_W3XP_table_row->anongame_infos_ICON_REQ_W3XP_string, variable) == 0) { if (ICON_REQ_W3XP_table_row->member != -1) anongame_infos_ICON_REQ_set_REQ(anongame_infos, ICON_REQ_W3XP_table_row->member, int_value); } break; } case parse_ICON_REQ_TOURNEY: { parse_state = unchanged; variable = buff; pointer = std::strchr(variable, '='); for (pointer--; pointer[0] == ' '; pointer--); pointer[1] = '\0'; pointer++; pointer++; pointer = std::strchr(pointer, '='); pointer++; int_value = std::atoi(pointer); if (int_value < 0) int_value = 0; for (ICON_REQ_TOURNEY_table_row = ICON_REQ_TOURNEY_handler_table; ICON_REQ_TOURNEY_table_row->anongame_infos_ICON_REQ_TOURNEY_string != NULL; ICON_REQ_TOURNEY_table_row++) if (std::strcmp(ICON_REQ_TOURNEY_table_row->anongame_infos_ICON_REQ_TOURNEY_string, variable) == 0) { if (ICON_REQ_TOURNEY_table_row->member != -1) anongame_infos_ICON_REQ_set_REQ(anongame_infos, ICON_REQ_TOURNEY_table_row->member, int_value); } break; } } } if (anongame_infos_DESC) { if (langID[0] != '\0') { list_append_data(anongame_infos->anongame_infos_DESC_list, anongame_infos_DESC); } else { if (anongame_infos->anongame_infos_DESC == NULL) anongame_infos->anongame_infos_DESC = anongame_infos_DESC; else { eventlog(eventlog_level_error, __FUNCTION__, "found another default_DESC block, deleting previous"); anongame_infos_DESC_destroy(anongame_infos->anongame_infos_DESC); anongame_infos->anongame_infos_DESC = anongame_infos_DESC; } } } file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); anongame_infos_loading_failure: anongame_infos_set_defaults(anongame_infos); anongame_infos_data_load(); return 0; } static int anongame_infos_data_load(void) { t_elem *curr; t_packet *raw; int j, k, size; char ladr_count = 0; char desc_count; char mapscount_total; char value; char PG_gamestyles; char AT_gamestyles; char TY_gamestyles; char anongame_prefix[ANONGAME_TYPES][5] = { /* queue */ /* PG 1v1 */{ 0x00, 0x00, 0x03, 0x3F, 0x00 }, /* 0 */ /* PG 2v2 */{ 0x01, 0x00, 0x02, 0x3F, 0x00 }, /* 1 */ /* PG 3v3 */{ 0x02, 0x00, 0x01, 0x3F, 0x00 }, /* 2 */ /* PG 4v4 */{ 0x03, 0x00, 0x01, 0x3F, 0x00 }, /* 3 */ /* PG sffa */{ 0x04, 0x00, 0x02, 0x3F, 0x00 }, /* 4 */ /* AT 2v2 */{ 0x00, 0x00, 0x02, 0x3F, 0x02 }, /* 5 */ /* AT tffa */{ 0x01, 0x00, 0x02, 0x3F, 0x02 }, /* 6 */ /* AT 3v3 */{ 0x02, 0x00, 0x02, 0x3F, 0x03 }, /* 7 */ /* AT 4v4 */{ 0x03, 0x00, 0x02, 0x3F, 0x04 }, /* 8 */ /* TY */{ 0x00, 0x01, 0x00, 0x3F, 0x00 }, /* 9 */ /* PG 5v5 */{ 0x05, 0x00, 0x01, 0x3F, 0x00 }, /* 10 */ /* PG 6v6 */{ 0x06, 0x00, 0x01, 0x3F, 0x00 }, /* 11 */ /* PG 2v2v2 */{ 0x07, 0x00, 0x01, 0x3F, 0x00 }, /* 12 */ /* PG 3v3v3 */{ 0x08, 0x00, 0x01, 0x3F, 0x00 }, /* 13 */ /* PG 4v4v4 */{ 0x09, 0x00, 0x01, 0x3F, 0x00 }, /* 14 */ /* PG 2v2v2v2 */{ 0x0A, 0x00, 0x01, 0x3F, 0x00 }, /* 15 */ /* PG 3v3v3v3 */{ 0x0B, 0x00, 0x01, 0x3F, 0x00 }, /* 16 */ /* AT 2v2v2 */{ 0x04, 0x00, 0x02, 0x3F, 0x02 } /* 17 */ }; /* hack to give names for new gametypes untill there added to anongame_infos.c */ const char * anongame_gametype_names[ANONGAME_TYPES] = { "One vs. One", "Two vs. Two", "Three vs. Three", "Four vs. Four", "Small Free for All", "Two vs. Two", "Team Free for All", "Three vs. Three", "Four vs. Four", "Tournament Game", "Five vs. Five", "Six Vs. Six", "Two vs. Two vs. Two", "3 vs. 3 vs. 3", "4 vs. 4 vs. 4", "2 vs. 2 vs. 2 vs. 2", "3 vs. 3 vs. 3 vs. 3", "Two vs. Two vs. Two" }; t_clienttag game_clienttag[2] = { CLIENTTAG_WARCRAFT3_UINT, CLIENTTAG_WAR3XP_UINT }; char anongame_PG_section = 0x00; char anongame_AT_section = 0x01; char anongame_TY_section = 0x02; /* set thumbsdown from the conf file */ for (j = 0; j < ANONGAME_TYPES; j++) anongame_prefix[j][2] = anongame_infos_get_thumbsdown(j); if ((raw = packet_create(packet_class_raw)) != NULL) { // assemble URL part with 3 URLs ( <1.15 ) packet_append_string(raw, anongame_infos_URL_get_URL(URL_server)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_player)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_tourney)); size = packet_get_size(raw); // create compressed data zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_war3->url_comp_data, &anongame_infos->anongame_infos_data_war3->url_comp_len); zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_w3xp->url_comp_data, &anongame_infos->anongame_infos_data_w3xp->url_comp_len); // append 4th URL for >= 1.15 clients packet_append_string(raw, anongame_infos_URL_get_URL(URL_clan)); size = packet_get_size(raw); zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_war3->url_comp_data_115, &anongame_infos->anongame_infos_data_war3->url_comp_len_115); zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_w3xp->url_comp_data_115, &anongame_infos->anongame_infos_data_w3xp->url_comp_len_115); for (k = 0; k < 2; k++) { packet_set_size(raw, 0); mapscount_total = maplists_get_totalmaps(game_clienttag[k]); packet_append_data(raw, &mapscount_total, 1); maplists_add_maps_to_packet(raw, game_clienttag[k]); size = packet_get_size(raw); if (k == 0) zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_war3->map_comp_data, &anongame_infos->anongame_infos_data_war3->map_comp_len); else zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_w3xp->map_comp_data, &anongame_infos->anongame_infos_data_w3xp->map_comp_len); } for (k = 0; k < 2; k++) { packet_set_size(raw, 0); value = 0; PG_gamestyles = 0; AT_gamestyles = 0; TY_gamestyles = 0; /* count of gametypes (PG, AT, TY) */ for (j = 0; j < ANONGAME_TYPES; j++) if (maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { if (!anongame_prefix[j][1] && !anongame_prefix[j][4]) PG_gamestyles++; if (!anongame_prefix[j][1] && anongame_prefix[j][4]) AT_gamestyles++; if (anongame_prefix[j][1]) TY_gamestyles++; } if (PG_gamestyles) value++; if (AT_gamestyles) value++; if (TY_gamestyles) value++; packet_append_data(raw, &value, 1); /* PG */ if (PG_gamestyles) { packet_append_data(raw, &anongame_PG_section, 1); packet_append_data(raw, &PG_gamestyles, 1); for (j = 0; j < ANONGAME_TYPES; j++) if (!anongame_prefix[j][1] && !anongame_prefix[j][4] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_prefix[j], 5); maplists_add_map_info_to_packet(raw, game_clienttag[k], j); } } /* AT */ if (AT_gamestyles) { packet_append_data(raw, &anongame_AT_section, 1); packet_append_data(raw, &AT_gamestyles, 1); for (j = 0; j < ANONGAME_TYPES; j++) if (!anongame_prefix[j][1] && anongame_prefix[j][4] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_prefix[j], 5); maplists_add_map_info_to_packet(raw, game_clienttag[k], j); } } /* TY */ if (TY_gamestyles) { packet_append_data(raw, &anongame_TY_section, 1); packet_append_data(raw, &TY_gamestyles, 1); for (j = 0; j < ANONGAME_TYPES; j++) if (anongame_prefix[j][1] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { /* set tournament races available */ anongame_prefix[j][3] = tournament_get_races(); /* set tournament type (PG or AT) * PG = 0 * AT = number players per team */ if (tournament_is_arranged()) anongame_prefix[j][4] = tournament_get_game_type(); else anongame_prefix[j][4] = 0; packet_append_data(raw, &anongame_prefix[j], 5); maplists_add_map_info_to_packet(raw, game_clienttag[k], j); } } size = packet_get_size(raw); if (k == 0) zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_war3->type_comp_data, &anongame_infos->anongame_infos_data_war3->type_comp_len); else zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_w3xp->type_comp_data, &anongame_infos->anongame_infos_data_w3xp->type_comp_len); } for (k = 0; k < 2; k++) { desc_count = 0; packet_set_size(raw, 0); for (j = 0; j < ANONGAME_TYPES; j++) if (maplists_get_totalmaps_by_queue(game_clienttag[k], j)) desc_count++; packet_append_data(raw, &desc_count, 1); /* PG description section */ for (j = 0; j < ANONGAME_TYPES; j++) if (!anongame_prefix[j][1] && !anongame_prefix[j][4] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_PG_section, 1); packet_append_data(raw, &anongame_prefix[j][0], 1); if (anongame_infos_get_short_desc(NULL, j) == NULL) packet_append_string(raw, anongame_gametype_names[j]); else packet_append_string(raw, anongame_infos_get_short_desc(NULL, j)); if (anongame_infos_get_long_desc(NULL, j) == NULL) packet_append_string(raw, "No Descreption"); else packet_append_string(raw, anongame_infos_get_long_desc(NULL, j)); } /* AT description section */ for (j = 0; j < ANONGAME_TYPES; j++) if (!anongame_prefix[j][1] && anongame_prefix[j][4] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_AT_section, 1); packet_append_data(raw, &anongame_prefix[j][0], 1); packet_append_string(raw, anongame_infos_get_short_desc(NULL, j)); packet_append_string(raw, anongame_infos_get_long_desc(NULL, j)); } /* TY description section */ for (j = 0; j < ANONGAME_TYPES; j++) if (anongame_prefix[j][1] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_TY_section, 1); packet_append_data(raw, &anongame_prefix[j][0], 1); packet_append_string(raw, anongame_infos_get_short_desc(NULL, j)); packet_append_string(raw, anongame_infos_get_long_desc(NULL, j)); } size = packet_get_size(raw); if (k == 0) zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_war3->desc_comp_data, &anongame_infos->anongame_infos_data_war3->desc_comp_len); else zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_w3xp->desc_comp_data, &anongame_infos->anongame_infos_data_w3xp->desc_comp_len); } packet_set_size(raw, 0); /*FIXME: Still adding a static number (10) Also maybe need do do some checks to avoid prefs empty strings. */ ladr_count = 10; packet_append_data(raw, &ladr_count, 1); packet_append_data(raw, "OLOS", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_PG_1v1_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_PG_1v1)); packet_append_data(raw, "MAET", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_PG_team_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_PG_team)); packet_append_data(raw, " AFF", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_PG_ffa_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_PG_ffa)); packet_append_data(raw, "2SV2", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_AT_2v2_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_AT_2v2)); packet_append_data(raw, "3SV3", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_AT_3v3_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_AT_3v3)); packet_append_data(raw, "4SV4", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_AT_4v4_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_AT_4v4)); packet_append_data(raw, "SNLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_clan_1v1_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_1v1)); packet_append_data(raw, "2NLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_clan_2v2_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_2v2)); packet_append_data(raw, "3NLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_clan_3v3_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_3v3)); packet_append_data(raw, "4NLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(NULL, ladder_clan_4v4_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_4v4)); size = packet_get_size(raw); zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_war3->ladr_comp_data, &anongame_infos->anongame_infos_data_war3->ladr_comp_len); zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos->anongame_infos_data_w3xp->ladr_comp_data, &anongame_infos->anongame_infos_data_w3xp->ladr_comp_len); packet_destroy(raw); } if ((raw = packet_create(packet_class_raw)) != NULL) { t_anongame_infos_DESC *anongame_infos_DESC; t_anongame_infos_data_lang *anongame_infos_data_lang_war3; t_anongame_infos_data_lang *anongame_infos_data_lang_w3xp; char * langID; LIST_TRAVERSE(anongame_infos->anongame_infos_DESC_list, curr) { anongame_infos_DESC = (t_anongame_infos_DESC*)elem_get_data(curr); langID = anongame_infos_DESC->langID; anongame_infos_data_lang_war3 = anongame_infos_data_lang_init(langID); anongame_infos_data_lang_w3xp = anongame_infos_data_lang_init(langID); for (k = 0; k < 2; k++) { desc_count = 0; packet_set_size(raw, 0); for (j = 0; j < ANONGAME_TYPES; j++) if (maplists_get_totalmaps_by_queue(game_clienttag[k], j)) desc_count++; packet_append_data(raw, &desc_count, 1); /* PG description section */ for (j = 0; j < ANONGAME_TYPES; j++) if (!anongame_prefix[j][1] && !anongame_prefix[j][4] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_PG_section, 1); packet_append_data(raw, &anongame_prefix[j][0], 1); if (anongame_infos_get_short_desc(langID, j) == NULL) packet_append_string(raw, anongame_gametype_names[j]); else packet_append_string(raw, anongame_infos_get_short_desc(langID, j)); if (anongame_infos_get_long_desc(langID, j) == NULL) packet_append_string(raw, "No Descreption"); else packet_append_string(raw, anongame_infos_get_long_desc(langID, j)); } /* AT description section */ for (j = 0; j < ANONGAME_TYPES; j++) if (!anongame_prefix[j][1] && anongame_prefix[j][4] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_AT_section, 1); packet_append_data(raw, &anongame_prefix[j][0], 1); packet_append_string(raw, anongame_infos_get_short_desc(langID, j)); packet_append_string(raw, anongame_infos_get_long_desc(langID, j)); } /* TY description section */ for (j = 0; j < ANONGAME_TYPES; j++) if (anongame_prefix[j][1] && maplists_get_totalmaps_by_queue(game_clienttag[k], j)) { packet_append_data(raw, &anongame_TY_section, 1); packet_append_data(raw, &anongame_prefix[j][0], 1); packet_append_string(raw, anongame_infos_get_short_desc(langID, j)); packet_append_string(raw, anongame_infos_get_long_desc(langID, j)); } size = packet_get_size(raw); if (k == 0) zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos_data_lang_war3->desc_comp_data, &anongame_infos_data_lang_war3->desc_comp_len); else zlib_compress(packet_get_data_const(raw, 0, size), size, &anongame_infos_data_lang_w3xp->desc_comp_data, &anongame_infos_data_lang_w3xp->desc_comp_len); } packet_set_size(raw, 0); /*FIXME: Still adding a static number (10) Also maybe need do do some checks to avoid prefs empty strings. */ ladr_count = 10; packet_append_data(raw, &ladr_count, 1); packet_append_data(raw, "OLOS", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_PG_1v1_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_PG_1v1)); packet_append_data(raw, "MAET", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_PG_team_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_PG_team)); packet_append_data(raw, " AFF", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_PG_ffa_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_PG_ffa)); packet_append_data(raw, "2SV2", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_AT_2v2_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_AT_2v2)); packet_append_data(raw, "3SV3", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_AT_3v3_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_AT_3v3)); packet_append_data(raw, "4SV4", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_AT_4v4_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_AT_4v4)); packet_append_data(raw, "SNLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_clan_1v1_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_1v1)); packet_append_data(raw, "2NLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_clan_2v2_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_2v2)); packet_append_data(raw, "3NLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_clan_3v3_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_3v3)); packet_append_data(raw, "4NLC", 4); packet_append_string(raw, anongame_infos_DESC_get_DESC(langID, ladder_clan_4v4_desc)); packet_append_string(raw, anongame_infos_URL_get_URL(URL_ladder_clan_4v4)); anongame_infos_data_lang_war3->ladr_len = packet_get_size(raw); anongame_infos_data_lang_war3->ladr_data = (char *)xmalloc(anongame_infos_data_lang_war3->ladr_len); std::memcpy(anongame_infos_data_lang_war3->ladr_data, packet_get_data_const(raw, 0, anongame_infos_data_lang_war3->ladr_len), anongame_infos_data_lang_war3->ladr_len); zlib_compress(anongame_infos_data_lang_war3->ladr_data, anongame_infos_data_lang_war3->ladr_len, &anongame_infos_data_lang_war3->ladr_comp_data, &anongame_infos_data_lang_war3->ladr_comp_len); list_append_data(anongame_infos->anongame_infos_data_lang_war3, anongame_infos_data_lang_war3); anongame_infos_data_lang_w3xp->ladr_len = packet_get_size(raw); anongame_infos_data_lang_w3xp->ladr_data = (char *)xmalloc(anongame_infos_data_lang_w3xp->ladr_len); std::memcpy(anongame_infos_data_lang_w3xp->ladr_data, packet_get_data_const(raw, 0, anongame_infos_data_lang_w3xp->ladr_len), anongame_infos_data_lang_w3xp->ladr_len); zlib_compress(anongame_infos_data_lang_w3xp->ladr_data, anongame_infos_data_lang_w3xp->ladr_len, &anongame_infos_data_lang_w3xp->ladr_comp_data, &anongame_infos_data_lang_w3xp->ladr_comp_len); list_append_data(anongame_infos->anongame_infos_data_lang_w3xp, anongame_infos_data_lang_w3xp); } packet_destroy(raw); } return 0; } extern int anongame_infos_unload(void) { return anongame_infos_destroy(anongame_infos); } static int zlib_compress(void const *src, int srclen, char **dest, int *destlen) { char *tmpdata; z_stream zcpr; int ret; unsigned lorigtodo; int lorigdone; int all_read_before; ret = Z_OK; lorigtodo = srclen; lorigdone = 0; *dest = NULL; tmpdata = (char *)xmalloc(srclen + (srclen / 0x10) + 0x200 + 0x8000); std::memset(&zcpr, 0, sizeof(z_stream)); deflateInit(&zcpr, 9); zcpr.next_in = (Bytef*)src; zcpr.next_out = (Bytef*)tmpdata; do { all_read_before = zcpr.total_in; zcpr.avail_in = (lorigtodo < 0x8000) ? lorigtodo : 0x8000; zcpr.avail_out = 0x8000; ret = deflate(&zcpr, (zcpr.avail_in == lorigtodo) ? Z_FINISH : Z_SYNC_FLUSH); lorigdone += (zcpr.total_in - all_read_before); lorigtodo -= (zcpr.total_in - all_read_before); } while (ret == Z_OK); (*destlen) = zcpr.total_out; if ((*destlen) > 0) { (*dest) = (char*)xmalloc((*destlen) + 4); bn_short_set((bn_short *)(*dest), lorigdone); bn_short_set((bn_short *)(*dest + 2), *destlen); std::memcpy((*dest) + 4, tmpdata, (*destlen)); (*destlen) += 4; } deflateEnd(&zcpr); xfree((void *)tmpdata); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_infos.h000066400000000000000000000123711462640461400212230ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ANONGAME_INFOS_TYPES #define INCLUDED_ANONGAME_INFOS_TYPES #ifdef JUST_NEED_TYPES #include "common/list.h" #else #define JUST_NEED_TYPES #include "common/list.h" #undef JUST_NEED_TYPES #endif #define anongame_infos_URL_count 14 namespace pvpgn { namespace bnetd { typedef enum { URL_server, URL_player, URL_tourney, URL_clan, URL_ladder_PG_1v1, URL_ladder_PG_ffa, URL_ladder_PG_team, URL_ladder_AT_2v2, URL_ladder_AT_3v3, URL_ladder_AT_4v4, URL_ladder_clan_1v1, URL_ladder_clan_2v2, URL_ladder_clan_3v3, URL_ladder_clan_4v4 } t_anongame_infos_URLs; #define anongame_infos_DESC_count 38 typedef enum { ladder_PG_1v1_desc, ladder_PG_ffa_desc, ladder_PG_team_desc, ladder_AT_2v2_desc, ladder_AT_3v3_desc, ladder_AT_4v4_desc, ladder_clan_1v1_desc, ladder_clan_2v2_desc, ladder_clan_3v3_desc, ladder_clan_4v4_desc, gametype_1v1_short, gametype_1v1_long, gametype_2v2_short, gametype_2v2_long, gametype_3v3_short, gametype_3v3_long, gametype_4v4_short, gametype_4v4_long, gametype_ffa_short, gametype_ffa_long, gametype_2v2v2_short, gametype_2v2v2_long, gametype_sffa_short, gametype_sffa_long, gametype_tffa_short, gametype_tffa_long, gametype_3v3v3_short, gametype_3v3v3_long, gametype_4v4v4_short, gametype_4v4v4_long, gametype_2v2v2v2_short, gametype_2v2v2v2_long, gametype_3v3v3v3_short, gametype_3v3v3v3_long, gametype_5v5_short, gametype_5v5_long, gametype_6v6_short, gametype_6v6_long } t_anongame_infos_DESCs; typedef struct { char * langID; char ** descs; } t_anongame_infos_DESC; #define anongame_infos_THUMBSDOWN_count 17 typedef enum { PG_1v1, PG_2v2, PG_3v3, PG_4v4, PG_ffa, AT_2v2, AT_3v3, AT_4v4, PG_2v2v2, AT_ffa, PG_5v5, PG_6v6, PG_3v3v3, PG_4v4v4, PG_2v2v2v2, PG_3v3v3v3, AT_2v2v2 } t_anongame_infos_THUMBDOWNs; #define anongame_infos_ICON_REQ_count 14 typedef enum { ICON_REQ_WAR3_Level1, ICON_REQ_WAR3_Level2, ICON_REQ_WAR3_Level3, ICON_REQ_WAR3_Level4, ICON_REQ_W3XP_Level1, ICON_REQ_W3XP_Level2, ICON_REQ_W3XP_Level3, ICON_REQ_W3XP_Level4, ICON_REQ_W3XP_Level5, ICON_REQ_TRNY_Level1, ICON_REQ_TRNY_Level2, ICON_REQ_TRNY_Level3, ICON_REQ_TRNY_Level4, ICON_REQ_TRNY_Level5 } t_anongame_infos_ICON_REQs; typedef struct { char * langID; char * desc_data; char * ladr_data; char * desc_comp_data; char * ladr_comp_data; int desc_len; int ladr_len; int desc_comp_len; int ladr_comp_len; } t_anongame_infos_data_lang; typedef struct { char * langID; char * url_comp_data; char * url_comp_data_115; char * map_comp_data; char * type_comp_data; char * desc_comp_data; char * ladr_comp_data; int url_comp_len; int url_comp_len_115; int map_comp_len; int type_comp_len; int desc_comp_len; int ladr_comp_len; } t_anongame_infos_data; typedef struct { char ** anongame_infos_URL; t_anongame_infos_DESC * anongame_infos_DESC; /* for default DESC */ t_list * anongame_infos_DESC_list; /* for localized DESC's */ char anongame_infos_THUMBSDOWN[anongame_infos_THUMBSDOWN_count]; int anongame_infos_ICON_REQ[anongame_infos_ICON_REQ_count]; t_anongame_infos_data * anongame_infos_data_war3; t_anongame_infos_data * anongame_infos_data_w3xp; t_list * anongame_infos_data_lang_war3; t_list * anongame_infos_data_lang_w3xp; } t_anongame_infos; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ANONGAME_INFOS_PROTOS #define INCLUDED_ANONGAME_INFOS_PROTOS #define JUST_NEED_TYPES #include "common/tag.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int anongame_infos_load(char const * filename); extern int anongame_infos_unload(void); extern char anongame_infos_get_thumbsdown(int queue); extern short anongame_infos_get_ICON_REQ(int Level, t_clienttag clienttag); extern short anongame_infos_get_ICON_REQ_TOURNEY(int Level); extern char * anongame_infos_data_get_url(t_clienttag clienttag, int versionid, int * len); extern char * anongame_infos_data_get_map(t_clienttag clienttag, int versionid, int * len); extern char * anongame_infos_data_get_type(t_clienttag clienttag, int versionid, int * len); extern char * anongame_infos_data_get_desc(char const * langID, t_clienttag clienttag, int versionid, int * len); extern char * anongame_infos_data_get_ladr(char const * langID, t_clienttag clienttag, int versionid, int * len); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_maplists.cpp000066400000000000000000000263251462640461400223000ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "anongame_maplists.h" #include #include #include "common/packet.h" #include "common/tag.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "prefs.h" #include "common/setup_after.h" #define MAXMAPS 100 #define MAXMAPS_PER_QUEUE 32 /* cannot be changed (map_prefs only supports 32 maps) */ /**********************************************************************************/ namespace pvpgn { namespace bnetd { static char * maplist_war3[MAXMAPS]; static char * maplist_w3xp[MAXMAPS]; static char * maplist_ral2[MAXMAPS]; static char * maplist_yuri[MAXMAPS]; static int number_maps_war3 = 0; static int number_maps_w3xp = 0; static int number_maps_ral2 = 0; static int number_maps_yuri = 0; static char maplists_war3[ANONGAME_TYPES][MAXMAPS_PER_QUEUE + 1]; static char maplists_w3xp[ANONGAME_TYPES][MAXMAPS_PER_QUEUE + 1]; static char maplists_ral2[ANONGAME_TYPES][MAXMAPS_PER_QUEUE + 1]; static char maplists_yuri[ANONGAME_TYPES][MAXMAPS_PER_QUEUE + 1]; static int _maplists_type_get_queue(const char * type); static const char * _maplists_queue_get_type(int queue); static void _maplists_add_map(t_clienttag clienttag, char * mapname, int queue); /*****/ static const char * queue_names[ANONGAME_TYPES] = { "1v1", "2v2", "3v3", "4v4", "sffa", "at2v2", "tffa", "at3v3", "at4v4", "TY", "5v5", "6v6", "2v2v2", "3v3v3", "4v4v4", "2v2v2v2", "3v3v3v3", "at2v2v2" }; /**********************************************************************************/ static int _maplists_type_get_queue(const char * type) { int i; for (i = 0; i < ANONGAME_TYPES; i++) if (std::strcmp(type, queue_names[i]) == 0) return i; return -1; } static const char * _maplists_queue_get_type(int queue) { return queue_names[queue]; } static void _maplists_add_map(t_clienttag clienttag, char * mapname, int queue) { /* this function does two things * 1. adds the mapname to maplist for MAPS section * maplist[mapnumber] = mapname * number_maps = total maps held in maplist[] * 2. adds the mapnumber to maplists for TYPE section * maplists[queue][0] = number of maps for queue * maplists[queue][1..32] = mapnumber * ie. maplist[maplists[queue][1..32]] = mapname */ int in_list = 0; int j; char clienttag_str[5]; if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { for (j = 0; j < number_maps_war3; j++) { if (std::strcmp(maplist_war3[j], mapname) == 0) { /* already in list */ in_list = 1; break; } } if (!in_list) maplist_war3[number_maps_war3++] = xstrdup(mapname); if (maplists_war3[queue][0] < MAXMAPS_PER_QUEUE) { maplists_war3[queue][0]++; maplists_war3[queue][(int)maplists_war3[queue][0]] = j; } else { eventlog(eventlog_level_error, __FUNCTION__, "cannot add map \"{}\" for gametype: {} (maxmaps per qametype: {})", mapname, _maplists_queue_get_type(queue), MAXMAPS_PER_QUEUE); } } else if (clienttag == CLIENTTAG_WAR3XP_UINT) { for (j = 0; j < number_maps_w3xp; j++) { if (std::strcmp(maplist_w3xp[j], mapname) == 0) { /* already in list */ in_list = 1; break; } } if (!in_list) maplist_w3xp[number_maps_w3xp++] = xstrdup(mapname); if (maplists_w3xp[queue][0] < MAXMAPS_PER_QUEUE) { maplists_w3xp[queue][0]++; maplists_w3xp[queue][(int)maplists_w3xp[queue][0]] = j; } else { eventlog(eventlog_level_error, __FUNCTION__, "cannot add map \"{}\" for gametype: {} (maxmaps per qametype: {})", mapname, _maplists_queue_get_type(queue), MAXMAPS_PER_QUEUE); } } else if (clienttag == CLIENTTAG_REDALERT2_UINT) { for (j = 0; j < number_maps_ral2; j++) { if (std::strcmp(maplist_ral2[j], mapname) == 0) { /* already in list */ in_list = 1; break; } } if (!in_list) maplist_ral2[number_maps_ral2++] = xstrdup(mapname); if (maplists_ral2[queue][0] < MAXMAPS_PER_QUEUE) { maplists_ral2[queue][0]++; maplists_ral2[queue][(int)maplists_ral2[queue][0]] = j; } else { eventlog(eventlog_level_error, __FUNCTION__, "cannot add map \"{}\" for gametype: {} (maxmaps per qametype: {})", mapname, _maplists_queue_get_type(queue), MAXMAPS_PER_QUEUE); } } else if (clienttag == CLIENTTAG_YURISREV_UINT) { for (j = 0; j < number_maps_yuri; j++) { if (std::strcmp(maplist_yuri[j], mapname) == 0) { /* already in list */ in_list = 1; break; } } if (!in_list) maplist_yuri[number_maps_yuri++] = xstrdup(mapname); if (maplists_yuri[queue][0] < MAXMAPS_PER_QUEUE) { maplists_yuri[queue][0]++; maplists_yuri[queue][(int)maplists_yuri[queue][0]] = j; } else { eventlog(eventlog_level_error, __FUNCTION__, "cannot add map \"{}\" for gametype: {} (maxmaps per qametype: {})", mapname, _maplists_queue_get_type(queue), MAXMAPS_PER_QUEUE); } } else eventlog(eventlog_level_error, __FUNCTION__, "invalid clienttag: {}", tag_uint_to_str(clienttag_str, clienttag)); } /**********************************************************************************/ extern int anongame_maplists_create(void) { std::FILE *mapfd; char buffer[256]; int len, i, queue; char *p, *q, *r, *u; if (prefs_get_mapsfile() == NULL) { eventlog(eventlog_level_error, "anongame_maplists_create", "invalid mapsfile, check your config"); return -1; } if ((mapfd = std::fopen(prefs_get_mapsfile(), "rt")) == NULL) { eventlog(eventlog_level_error, "anongame_maplists_create", "could not open mapsfile : \"{}\"", prefs_get_mapsfile()); return -1; } /* init the maps, they say static vars are 0-ed anyway but u never know :) */ for (i = 0; i < ANONGAME_TYPES; i++) { maplists_war3[i][0] = 0; maplists_w3xp[i][0] = 0; } while (std::fgets(buffer, 256, mapfd)) { len = std::strlen(buffer); if (len < 1) continue; if (buffer[len - 1] == '\n') { buffer[len - 1] = '\0'; } /* search for comments and comment them out */ for (p = buffer; *p; p++) if (*p == '#') { *p = '\0'; break; } /* skip spaces and/or tabs */ for (p = buffer; *p && (*p == ' ' || *p == '\t'); p++); /* p = clienttag */ if (*p == '\0') continue; /* find next delimiter */ for (q = p; *q && *q != ' ' && *q != '\t'; q++); if (*q == '\0' || q - p != 4) continue; /* clienttag needs to have 4 chars */ *q = '\0'; /* end of clienttag */ /* skip spaces and/or tabs */ for (q++; *q && (*q == ' ' || *q == '\t'); q++); /* q = type */ if (*q == '\0') continue; /* find next delimiter */ for (r = q + 1; *r && *r != ' ' && *r != '\t'; r++); *r = '\0'; /* end of type */ /* skip spaces and/or tabs */ for (r++; *r && (*r == ' ' || *r == '\t'); r++); /* r = mapname */ if (*r == '\0') continue; if (*r != '\"') /* mapname without quotes */ /* find next delimiter */ for (u = r + 1; *u && *u != ' ' && *u != '\t'; u++); else /* mapname with quotes */ { r++; /* skip quote */ for (u = r + 1; *u && *u != '\"'; u++); /* find end quote or end of buffer */ if (*u != '\"') { eventlog(eventlog_level_error, __FUNCTION__, "missing \" at the end of the map name, presume it's ok anyway"); } } *u = '\0'; /* end of mapname */ if ((queue = _maplists_type_get_queue(q)) < 0) continue; /* invalid queue */ _maplists_add_map(tag_case_str_to_uint(p), r, queue); } std::fclose(mapfd); return 0; } /* used by the MAPS section */ extern int maplists_get_totalmaps(t_clienttag clienttag) { switch (clienttag) { case CLIENTTAG_WARCRAFT3_UINT: return number_maps_war3; case CLIENTTAG_WAR3XP_UINT: return number_maps_w3xp; case CLIENTTAG_REDALERT2_UINT: return number_maps_ral2; case CLIENTTAG_YURISREV_UINT: return number_maps_yuri; default: ERROR0("Unknown clienttag"); return 0; } return 0; } extern void maplists_add_maps_to_packet(t_packet * packet, t_clienttag clienttag) { int i; if (clienttag == CLIENTTAG_WARCRAFT3_UINT) for (i = 0; i < number_maps_war3; i++) packet_append_string(packet, maplist_war3[i]); if (clienttag == CLIENTTAG_WAR3XP_UINT) for (i = 0; i < number_maps_w3xp; i++) packet_append_string(packet, maplist_w3xp[i]); } /* used by TYPE section */ extern int maplists_get_totalmaps_by_queue(t_clienttag clienttag, int queue) { switch (clienttag) { case CLIENTTAG_WARCRAFT3_UINT: return maplists_war3[queue][0]; case CLIENTTAG_WAR3XP_UINT: return maplists_w3xp[queue][0]; case CLIENTTAG_REDALERT2_UINT: return maplists_ral2[queue][0]; case CLIENTTAG_YURISREV_UINT: return maplists_yuri[queue][0]; default: ERROR0("Unknown clienttag"); return 0; } return 0; } extern void maplists_add_map_info_to_packet(t_packet * rpacket, t_clienttag clienttag, int queue) { int i; if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { for (i = 0; i < maplists_war3[queue][0] + 1; i++) packet_append_data(rpacket, &maplists_war3[queue][i], 1); } if (clienttag == CLIENTTAG_WAR3XP_UINT) { for (i = 0; i < maplists_w3xp[queue][0] + 1; i++) packet_append_data(rpacket, &maplists_w3xp[queue][i], 1); } } /* used by _get_map_from_prefs() */ extern char * maplists_get_map(int queue, t_clienttag clienttag, int mapnumber) { switch (clienttag) { case CLIENTTAG_WARCRAFT3_UINT: return maplist_war3[(int)maplists_war3[queue][mapnumber]]; case CLIENTTAG_WAR3XP_UINT: return maplist_w3xp[(int)maplists_w3xp[queue][mapnumber]]; case CLIENTTAG_REDALERT2_UINT: return maplist_ral2[(int)maplists_ral2[queue][mapnumber]]; case CLIENTTAG_YURISREV_UINT: return maplist_yuri[(int)maplists_yuri[queue][mapnumber]]; default: ERROR0("Unknown clienttag"); return NULL; } } extern void anongame_maplists_destroy() { int i; for (i = 0; i < MAXMAPS; i++) { if (maplist_war3[i]) xfree((void *)maplist_war3[i]); if (maplist_w3xp[i]) xfree((void *)maplist_w3xp[i]); if (maplist_ral2[i]) xfree((void *)maplist_ral2[i]); if (maplist_yuri[i]) xfree((void *)maplist_yuri[i]); } } extern int anongame_add_tournament_map(t_clienttag clienttag, char * mapname) { _maplists_add_map(clienttag, mapname, ANONGAME_TYPE_TY); return 0; } extern void anongame_tournament_maplists_destroy(void) { return; /* nothing to destroy */ } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_maplists.h000066400000000000000000000034021462640461400217340ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ANONGAME_MAPLISTS_TYPES #define INCLUDED_ANONGAME_MAPLISTS_TYPES #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ANONGAME_MAPLISTS_PROTOS #define INCLUDED_ANONGAME_MAPLISTS_PROTOS #define JUST_NEED_TYPES # include "common/packet.h" # include "common/tag.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int anongame_maplists_create(void); extern void anongame_maplists_destroy(void); extern int maplists_get_totalmaps(t_clienttag clienttag); extern int maplists_get_totalmaps_by_queue(t_clienttag clienttag, int queue); extern void maplists_add_maps_to_packet(t_packet * packet, t_clienttag clienttag); extern void maplists_add_map_info_to_packet(t_packet * rpacket, t_clienttag clienttag, int queue); extern char * maplists_get_map(int queue, t_clienttag clienttag, int mapnumber); extern int anongame_add_tournament_map(t_clienttag clienttag, char * mapname); extern void anongame_tournament_maplists_destroy(void); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_wol.cpp000066400000000000000000000434641462640461400212500ustar00rootroot00000000000000/* * Copyright (C) 2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define ANONGAME_WOL_INTERNAL_ACCESS #include "common/setup_before.h" #include "anongame_wol.h" #include #include #include #include "compat/strcasecmp.h" #include "common/irc_protocol.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/tag.h" #include "common/list.h" #include "common/anongame_protocol.h" #include "irc.h" #include "handle_wol.h" #include "connection.h" #include "channel.h" #include "anongame.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef int(*t_anong_tag)(t_anongame_wol_player * player, char * param); typedef struct { const char * wol_anong_tag_string; t_anong_tag wol_anong_tag_handler; } t_wol_anongame_tag_table_row; static t_list * anongame_wol_matchlist_head = NULL; static int _handle_address_tag(t_anongame_wol_player * player, char * param); static int _handle_port_tag(t_anongame_wol_player * player, char * param); static int _handle_country_tag(t_anongame_wol_player * player, char * param); static int _handle_colour_tag(t_anongame_wol_player * player, char * param); static const t_wol_anongame_tag_table_row t_wol_anongame_tag_table[] = { { MATCHTAG_ADDRESS, _handle_address_tag }, { MATCHTAG_PORT, _handle_port_tag }, { MATCHTAG_COUNTRY, _handle_country_tag }, { MATCHTAG_COLOUR, _handle_colour_tag }, { NULL, NULL } }; static int anongame_wol_set_playersetting(t_anongame_wol_player * player, char const * tag, char * param) { t_wol_anongame_tag_table_row const *p; for (p = t_wol_anongame_tag_table; p->wol_anong_tag_string != NULL; p++) { if (strcasecmp(tag, p->wol_anong_tag_string) == 0) { if (p->wol_anong_tag_handler != NULL) return ((p->wol_anong_tag_handler)(player, param)); } } return -1; } /* anongame_wol player functions: */ static t_anongame_wol_player * anongame_wol_player_create(t_connection * conn) { t_anongame_wol_player * player; player = (t_anongame_wol_player*)xmalloc(sizeof(t_anongame_wol_player)); player->conn = conn; /* Used only in Red Alert 2 and Yuri's Revenge */ player->address = 0; player->port = 0; player->country = -2; /* Default values are form packet dumps - not prefered country */ player->colour = -2; /* Default values are form packet dumps - not prefered colour */ conn_wol_set_anongame_player(conn, player); list_append_data(anongame_wol_matchlist_head, player); DEBUG1("[** WOL **] annongame player created: {}", conn_get_chatname(conn)); return player; } static int anongame_wol_player_destroy(t_anongame_wol_player * player, t_elem ** curr) { if (list_remove_data(anongame_wol_matchlist_head, player, curr) < 0){ ERROR0("could not remove item from list"); return -1; } DEBUG0("[** WOL **] destroying annongame player"); xfree(player); return 0; } static t_connection * anongame_wol_player_get_conn(t_anongame_wol_player const * player) { if (!player) { ERROR0("got NULL player"); return NULL; } return player->conn; } static int anongame_wol_player_get_address(t_anongame_wol_player const * player) { if (!player) { ERROR0("got NULL player"); return -1; } return player->address; } static int _handle_address_tag(t_anongame_wol_player * player, char * param) { if (!player) { ERROR0("got NULL player"); return -1; } if (param) player->address = std::atoi(param); return 0; } static int anongame_wol_player_get_port(t_anongame_wol_player const * player) { if (!player) { ERROR0("got NULL player"); return -1; } return player->port; } static int _handle_port_tag(t_anongame_wol_player * player, char * param) { if (!player) { ERROR0("got NULL player"); return -1; } if (param) player->port = std::atoi(param); return 0; } static int anongame_wol_player_get_country(t_anongame_wol_player const * player) { if (!player) { ERROR0("got NULL player"); return -1; } return player->country; } static int _handle_country_tag(t_anongame_wol_player * player, char * param) { if (!player) { ERROR0("got NULL player"); return -1; } if (param) player->country = std::atoi(param); return 0; } static int anongame_wol_player_get_colour(t_anongame_wol_player const * player) { if (!player) { ERROR0("got NULL player"); return -1; } return player->colour; } static int _handle_colour_tag(t_anongame_wol_player * player, char * param) { if (!player) { ERROR0("got NULL player"); return -1; } if (param) player->colour = std::atoi(param); return 0; } /* Matchlist functions:*/ extern int anongame_wol_matchlist_create(void) { anongame_wol_matchlist_head = list_create(); return 0; } extern int anongame_wol_matchlist_destroy(void) { t_anongame_wol_player * player; t_elem * curr; if (anongame_wol_matchlist_head) { LIST_TRAVERSE(anongame_wol_matchlist_head, curr) { if (!(player = (t_anongame_wol_player*)elem_get_data(curr))) { /* should not happen */ ERROR0("wol_matchlist contains NULL item"); continue; } anongame_wol_player_destroy(player, &curr); } if (list_destroy(anongame_wol_matchlist_head) < 0) return -1; anongame_wol_matchlist_head = NULL; } return 0; } extern t_list * anongame_wol_matchlist(void) { return anongame_wol_matchlist_head; } static t_anongame_wol_player * anongame_wol_matchlist_find_player_by_conn(t_connection * conn) { t_elem * curr; if (!conn) { ERROR0("got NULL conn"); return NULL; } LIST_TRAVERSE(anongame_wol_matchlist(), curr) { t_anongame_wol_player * player = (t_anongame_wol_player *)elem_get_data(curr); if (conn == anongame_wol_player_get_conn(player)) { return player; } } return NULL; } static int anongame_wol_matchlist_get_length(void) { return list_get_length(anongame_wol_matchlist_head); } /* support functions */ static int _send_msg(t_connection * conn, char const * command, char const * text) { if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!command) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL command"); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text"); return -1; } t_packet * p = packet_create(packet_class_raw); if (!p) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return -1; } const char *nick = conn_get_loggeduser(conn); if (!nick) nick = "UserName"; std::string data(":matchbot!u@h " + std::string(command) + " " + std::string(nick) + " " + std::string(text)); data.erase(MAX_IRC_MESSAGE_LEN, std::string::npos); DEBUG2("[{}] sent \"{}\"", conn_get_socket(conn), data.c_str()); data.append("\r\n"); packet_set_size(p, 0); packet_append_data(p, data.c_str(), data.length()); conn_push_outqueue(conn, p); packet_del_ref(p); return 0; } static int _get_pair(int * i, int * j, int max, bool different) { max++; /* We want to use just real maximum number - no metter what function do */ if (*i == -2) *i = (max * rand() / (RAND_MAX + 1)); if (*j == -2) *j = (max * rand() / (RAND_MAX + 1)); if ((different) && (*i == *j)) { do { *j = (max * rand() / (RAND_MAX + 1)); } while (*i == *j); } return 0; } static int anongame_wol_trystart(t_anongame_wol_player const * player1) { t_elem * curr; char temp[MAX_IRC_MESSAGE_LEN]; char _temp[MAX_IRC_MESSAGE_LEN]; int random; t_anongame_wol_player * player2; t_connection * conn_pl1; t_connection * conn_pl2; char const * channelname; const char * mapname; t_clienttag ctag; std::memset(temp, 0, sizeof(temp)); std::memset(_temp, 0, sizeof(_temp)); /** * Expected start message is * * Red Alert 2: * :Start [rand_num],0,0,[credits],0,1,[super_weapon],1,1,0,1,x,2,1,[mapsize_kilobytes],[mapname],1: * [nick1_name],[nick1_country],[nick1_colour],[nick1_addr_hex],[nick1_nat],[nick1_prt_hex], * [nick2_name],[nick2_country],[nick2_colour],[nick2_addr_hex],[nick2_nat],[nick2_prt_hex] * * Yuris Revenge Quick game: * * Yuris Revenge Quick Coop: * */ if (!player1) { ERROR0("got NULL player"); return -1; } conn_pl1 = anongame_wol_player_get_conn(player1); ctag = conn_get_clienttag(conn_pl1); channelname = channel_get_name(conn_get_channel(conn_pl1)); LIST_TRAVERSE(anongame_wol_matchlist(), curr) { player2 = (t_anongame_wol_player *)elem_get_data(curr); conn_pl2 = anongame_wol_player_get_conn(player2); if ((player1 != player2) && ((conn_get_channel(conn_pl1)) == (conn_get_channel(conn_pl2)))) { switch (ctag) { case CLIENTTAG_REDALERT2_UINT: { random = rand(); if (std::strcmp(channelname, RAL2_CHANNEL_FFA) == 0) { int pl1_colour = anongame_wol_player_get_colour(player1); int pl1_country = anongame_wol_player_get_country(player1); int pl2_colour = anongame_wol_player_get_colour(player2); int pl2_country = anongame_wol_player_get_country(player2); DEBUG0("Generating SOLO game for Red Alert 2"); _get_pair(&pl1_colour, &pl2_colour, 7, true); _get_pair(&pl1_country, &pl2_country, 8, false); mapname = anongame_get_map_from_prefs(ANONGAME_TYPE_1V1, ctag); /* We have madatory of game */ std::snprintf(_temp, sizeof(_temp), ":Start %d,0,0,10000,0,1,0,1,1,0,1,x,2,1,165368,%s,1:", random, mapname); std::strcat(temp, _temp); /* GameHost informations */ std::snprintf(_temp, sizeof(_temp), "%s,%d,%d,%x,1,%x,", conn_get_chatname(conn_pl1), pl1_country, pl1_colour, anongame_wol_player_get_address(player1), anongame_wol_player_get_port(player1)); std::strcat(temp, _temp); /* GameJoinie informations */ std::snprintf(_temp, sizeof(_temp), "%s,%d,%d,%x,1,%x", conn_get_chatname(conn_pl2), pl2_country, pl2_colour, anongame_wol_player_get_address(player2), anongame_wol_player_get_port(player2)); std::strcat(temp, _temp); _send_msg(conn_pl1, "PRIVMSG", temp); _send_msg(conn_pl2, "PRIVMSG", temp); } else ERROR1("undefined channel type for {} channel", channelname); } return 0; case CLIENTTAG_YURISREV_UINT: { random = rand(); if (std::strcmp(channelname, YURI_CHANNEL_FFA) == 0) { int pl1_colour = anongame_wol_player_get_colour(player1); int pl1_country = anongame_wol_player_get_country(player1); int pl2_colour = anongame_wol_player_get_colour(player2); int pl2_country = anongame_wol_player_get_country(player2); DEBUG0("Generating SOLO game for Yuri's Revenge"); _get_pair(&pl1_colour, &pl2_colour, 7, true); _get_pair(&pl1_country, &pl2_country, 9, false); mapname = anongame_get_map_from_prefs(ANONGAME_TYPE_1V1, ctag); /* We have madatory of game */ std::snprintf(_temp, sizeof(_temp), ":Start %d,0,0,10000,0,0,1,1,1,0,3,0,x,2,1,163770,%s,1:", random, mapname); std::strcat(temp, _temp); /* GameHost informations */ std::snprintf(_temp, sizeof(_temp), "%s,%d,%d,-2,-2,", conn_get_chatname(conn_pl1), pl1_country, pl1_colour); std::strcat(temp, _temp); std::snprintf(_temp, sizeof(_temp), "%x,1,%x,", anongame_wol_player_get_address(player1), anongame_wol_player_get_port(player1)); std::strcat(temp, _temp); /* GameJoinie informations */ std::snprintf(_temp, sizeof(_temp), "%s,%d,%d,-2,-2,", conn_get_chatname(conn_pl2), pl2_country, pl2_colour); std::strcat(temp, _temp); std::snprintf(_temp, sizeof(_temp), "%x,1,%x", anongame_wol_player_get_address(player2), anongame_wol_player_get_port(player2)); std::strcat(temp, _temp); _send_msg(conn_pl1, "PRIVMSG", temp); _send_msg(conn_pl2, "PRIVMSG", temp); } else if (std::strcmp(channelname, YURI_CHANNEL_COOP) == 0) { DEBUG0("Generating COOP game for Yuri's Revenge"); /* We have madatory of game */ std::snprintf(_temp, sizeof(_temp), ":Start %d,0,0,10000,10,0,1,1,0,1,3,0,x,2,1,163770,C1A01MD.MAP,1:", random); std::strcat(temp, _temp); /* GameHost informations */ std::snprintf(_temp, sizeof(_temp), "%s,0,4,0,-2,", conn_get_chatname(conn_pl1)); std::strcat(temp, _temp); std::snprintf(_temp, sizeof(_temp), "%x,1,%x,", anongame_wol_player_get_address(player1), anongame_wol_player_get_port(player1)); std::strcat(temp, _temp); /* GameJoinie informations */ std::snprintf(_temp, sizeof(_temp), "%s,0,5,1,-2,", conn_get_chatname(anongame_wol_player_get_conn(player2))); std::strcat(temp, _temp); std::snprintf(_temp, sizeof(_temp), "%x,1,%x", anongame_wol_player_get_address(player2), anongame_wol_player_get_port(player2)); std::strcat(temp, _temp); /* Some computers for coop games */ std::snprintf(_temp, sizeof(_temp), ":@:0,-1,-1,-2,-2,0,-1,-1,-2,-2,1,8,1,-2,-2,1,8,2,-2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,"); std::strcat(temp, _temp); _send_msg(conn_pl1, "PRIVMSG", temp); _send_msg(conn_pl2, "PRIVMSG", temp); } else ERROR1("undefined channel type for {} channel", channelname); } return 0; default: DEBUG0("unsupported client for WOL Matchgame"); return 0; } } } return 0; } static int anongame_wol_tokenize_line(t_connection * conn, char const * text) { t_anongame_wol_player * player; char * command; /* Match, CINFO, SINFO, Pings */ char * tag; /* ADR, COL, COU... */ char * param; /* value of paramtag */ char * line; /* Copy of text */ char * temp; char * p; if (!conn) { ERROR0("got NULL conn"); return -1; } if (!text) { ERROR0("got NULL text"); return -1; } if (!(player = anongame_wol_player_create(conn))) { ERROR0("player was not created"); return -1; } /** * Here are expected privmsgs: * :user!EMPR@host PRIVMSG matchbot :Match COU=-1,COL=-1,SHA=-1,SHB=-1,LOC=2,RAT=0 * :user!RAL2@host PRIVMSG matchbot :Match COU=-2, COL=-2, LOC=1, RAT=3, RES=640, ADR=202680512, NAT=1, PRT=1295 * :user!RAL2@host PRIVMSG matchbot :Match COU=-2, COL=-2, LOC=1, RAT=3, RES=640, ADR=202680512, NAT=1, PRT=1340 * :user!RAL2@host PRIVMSG matchbot :Match COU=-2, COL=-2, LOC=25, RAT=1, RES=640, ADR=-501962560, NAT=1, PRT=1320 * :user!YURI@host PRIVMSG matchbot :Match COU=-2, COL=-2, MBR=1, LOC=5, RAT=2, RES=800, ADR=202680512, NAT=1, PRT=1390 * :user!RNGD@host PRIVMSG matchbot :CINFO VER=1329937315 CPU=2981 MEM=1023 TPOINTS=0 PLAYED=0 PINGS=00FFFFFFFFFFFFFF * :user!RNGD@host PRIVMSG matchbot :SINFO 4F453BA3DBAE41CB00000000000000002d# 9Dedicated Renegade Server-C&C_Field.mix07FF0656FFFF1C2D|090000000000 * :user!YURI@host PRIVMSG matchbot :Pings nickname,2; */ line = (char *)xmalloc(std::strlen(text) + 2); strcpy(line, text); command = line; if (!(temp = strchr(command, ' '))) { WARN0("got malformed line (missing command)"); xfree(line); return -1; } *temp++ = '\0'; if ((std::strcmp(command, "Match") == 0)) { strcat(temp, ","); /* FIXME: This is DUMB - without that we lost the last tag/param */ for (p = temp; *p && (*p != '\0'); p++) { if ((*p == ',') || (*p == ' ')) { *p++ = '\0'; if (temp[0] == ' ') /* Pelish: Emperor sends line without spaces but RA2/Yuri do */ *temp++; tag = temp; param = strchr(tag, '='); if (param) *param++ = '\0'; if (anongame_wol_set_playersetting(player, tag, param) == -1) WARN2("[** WOL **] got unknown tag {} param {}", tag, param); temp = p; } } _send_msg(conn, "PRIVMSG", ":Working"); anongame_wol_trystart(player); } else { DEBUG1("[** WOL **] got line /{}/", text); } if (line) xfree(line); return 0; } /* Functions for getting/sending player informations */ extern int anongame_wol_destroy(t_connection * conn) { t_elem *curr; /* Player destroying */ LIST_TRAVERSE(anongame_wol_matchlist_head, curr) { t_anongame_wol_player * player = (t_anongame_wol_player*)elem_get_data(curr); if (conn == anongame_wol_player_get_conn(player)) anongame_wol_player_destroy(player, &curr); } return 0; } extern int anongame_wol_privmsg(t_connection * conn, int numparams, char ** params, char * text) { if (!conn) { ERROR0("got NULL conn"); return -1; } anongame_wol_tokenize_line(conn, text); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/anongame_wol.h000066400000000000000000000043531462640461400207070ustar00rootroot00000000000000/* * Copyright (C) 2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ANONGAME_WOL_TYPES #define INCLUDED_ANONGAME_WOL_TYPES #ifdef ANONGAME_WOL_INTERNAL_ACCESS #ifndef JUST_NEED_TYPES # define JUST_NEED_TYPES # include "connection.h" # undef JUST_NEED_TYPES #endif #define MATCHTAG_ADDRESS "ADR" #define MATCHTAG_PORT "PRT" #define MATCHTAG_COUNTRY "COU" #define MATCHTAG_COLOUR "COL" #define MATCHTAG_MATCHRESOLUTION "MBR" #define MATCHTAG_LOCATION "LOC" #define MATCHTAG_SCREENRESOLUTION "RES" #define RAL2_CHANNEL_FFA "Lob 38 0" #define YURI_CHANNEL_FFA "Lob 40 0" #define YURI_CHANNEL_COOP "Lob 39 0" #endif namespace pvpgn { namespace bnetd { typedef struct anongame_wol_player #ifdef ANONGAME_WOL_INTERNAL_ACCESS { t_connection * conn; /* Red Alert 2 and Yuri's Revnenge */ int address; int port; int country; int colour; } #endif t_anongame_wol_player; } } #endif /*******/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ANONGAME_WOL_PROTOS #define INCLUDED_ANONGAME_WOL_PROTOS #define JUST_NEED_TYPES # include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int anongame_wol_matchlist_create(void); extern int anongame_wol_matchlist_destroy(void); extern int anongame_wol_destroy(t_connection * conn); extern int anongame_wol_privmsg(t_connection * conn, int numparams, char ** params, char * text); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/attr.h000066400000000000000000000041161462640461400172100ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __ATTR_INCLUDED__ #define __ATTR_INCLUDED__ #include "common/elist.h" #include "common/xalloc.h" namespace pvpgn { namespace bnetd { typedef struct attr_struct { const char *key; const char *val; int dirty; t_hlist link; } t_attr; static inline t_attr *attr_create(const char *key, const char *val) { t_attr *attr; attr = (t_attr*)xmalloc(sizeof(t_attr)); attr->dirty = 0; hlist_init(&attr->link); attr->key = key ? xstrdup(key) : NULL; attr->val = val ? xstrdup(val) : NULL; return attr; } static inline int attr_destroy(t_attr *attr) { if (attr->key) xfree((void*)attr->key); if (attr->val) xfree((void*)attr->val); xfree((void*)attr); return 0; } static inline int attr_get_dirty(t_attr *attr) { return attr->dirty; } static inline void attr_clear_dirty(t_attr *attr) { attr->dirty = 0; } static inline const char *attr_get_key(t_attr *attr) { return attr->key; } static inline const char *attr_get_val(t_attr *attr) { return attr->val; } static inline void attr_set_val(t_attr *attr, const char *val) { if (attr->val) xfree((void*)attr->val); if (val) attr->val = xstrdup(val); else attr->val = NULL; } static inline void attr_set_dirty(t_attr *attr) { attr->dirty = 1; } } } #endif /* __ATTR_H_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/attrgroup.cpp000066400000000000000000000334041462640461400206220ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define ATTRGROUP_INTERNAL_ACCESS #include "attrgroup.h" #include #include #include #include #include "common/eventlog.h" #include "common/flags.h" #include "common/xalloc.h" #include "compat/strcasecmp.h" #include "compat/strncasecmp.h" #include "attr.h" #include "attrlayer.h" #include "storage.h" #include "prefs.h" #include "server.h" #include "connection.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static const char * key_get_tab(const char *key); static inline void attrgroup_set_accessed(t_attrgroup *attrgroup) { FLAG_SET(&attrgroup->flags, ATTRGROUP_FLAG_ACCESSED); attrgroup->lastaccess = now; attrlayer_accessed(attrgroup); } static inline void attrgroup_clear_accessed(t_attrgroup *attrgroup) { FLAG_CLEAR(&attrgroup->flags, ATTRGROUP_FLAG_ACCESSED); } static inline void attrgroup_set_dirty(t_attrgroup *attrgroup) { if (FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_DIRTY)) return; attrgroup->dirtytime = now; FLAG_SET(&attrgroup->flags, ATTRGROUP_FLAG_DIRTY); attrlayer_add_dirtylist(&attrgroup->dirtylist); } static inline void attrgroup_clear_dirty(t_attrgroup *attrgroup) { if (!FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_DIRTY)) return; FLAG_CLEAR(&attrgroup->flags, ATTRGROUP_FLAG_DIRTY); attrlayer_del_dirtylist(&attrgroup->dirtylist); t_attr *attr; t_hlist *curr; // clear dirty flag on each attribute hlist_for_each(curr, (t_hlist*)attrgroup) { attr = hlist_entry(curr, t_attr, link); attr_clear_dirty(attr); } } static inline void attrgroup_set_loaded(t_attrgroup *attrgroup) { if (FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_LOADED)) return; FLAG_SET(&attrgroup->flags, ATTRGROUP_FLAG_LOADED); attrlayer_add_loadedlist(&attrgroup->loadedlist); } static inline void attrgroup_clear_loaded(t_attrgroup *attrgroup) { if (!FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_LOADED)) return; /* clear this because they are not valid if attrgroup is unloaded */ attrgroup_clear_dirty(attrgroup); attrgroup_clear_accessed(attrgroup); FLAG_CLEAR(&attrgroup->flags, ATTRGROUP_FLAG_LOADED); attrlayer_del_loadedlist(&attrgroup->loadedlist); #ifdef WITH_SQL for (std::vector::iterator it = attrgroup->loadedtabs->begin(); it != attrgroup->loadedtabs->end(); ++it) xfree((void*)(*it)); // clear loaded tabs attrgroup->loadedtabs->clear(); #endif } static t_attrgroup * attrgroup_create(void) { t_attrgroup *attrgroup; attrgroup = (t_attrgroup*)xmalloc(sizeof(t_attrgroup)); hlist_init(&attrgroup->list); attrgroup->storage = NULL; attrgroup->flags = ATTRGROUP_FLAG_NONE; attrgroup->lastaccess = 0; attrgroup->dirtytime = 0; elist_init(&attrgroup->loadedlist); elist_init(&attrgroup->dirtylist); #ifdef WITH_SQL attrgroup->loadedtabs = new std::vector(); #endif return attrgroup; } extern t_attrgroup * attrgroup_create_storage(t_storage_info *storage) { t_attrgroup *attrgroup; attrgroup = attrgroup_create(); attrgroup->storage = storage; return attrgroup; } extern t_attrgroup * attrgroup_create_newuser(const char *name) { t_attrgroup *attrgroup; t_storage_info *stmp; stmp = storage->create_account(name); if (!stmp) { eventlog(eventlog_level_error, __FUNCTION__, "failed to add user '{}' to storage", name); return NULL; } attrgroup = attrgroup_create_storage(stmp); /* new accounts are born loaded */ attrgroup_set_loaded(attrgroup); return attrgroup; } extern t_attrgroup * attrgroup_create_nameuid(const char *name, unsigned uid) { t_storage_info *info; t_attrgroup *attrgroup; info = storage->read_account(name, uid); if (!info) return NULL; attrgroup = attrgroup_create_storage(info); return attrgroup; } extern int attrgroup_destroy(t_attrgroup *attrgroup) { if (!attrgroup) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL attrgroup"); return -1; } attrgroup_unload(attrgroup); if (attrgroup->storage) storage->free_info(attrgroup->storage); xfree(attrgroup); return 0; } extern int attrgroup_save(t_attrgroup *attrgroup, int flags) { if (!attrgroup) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL attrgroup"); return -1; } if (!FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_LOADED)) return 0; if (!FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_DIRTY)) return 0; if (!FLAG_ISSET(flags, FS_FORCE) && now - attrgroup->dirtytime < prefs_get_user_sync_timer()) return 0; assert(attrgroup->storage); storage->write_attrs(attrgroup->storage, &attrgroup->list); attrgroup_clear_dirty(attrgroup); return 1; } extern int attrgroup_flush(t_attrgroup *attrgroup, int flags) { t_attr *attr; t_hlist *curr, *save; if (!attrgroup) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL attrgroup"); return -1; } if (!FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_LOADED)) return 0; if (!FLAG_ISSET(flags, FS_FORCE) && FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_ACCESSED) && now - attrgroup->lastaccess < prefs_get_user_flush_timer()) return 0; assert(attrgroup->storage); unsigned int uid = *((unsigned int *)attrgroup->storage); t_storage_info *defacct = storage->get_defacct(); unsigned int defuid = *((unsigned int *)defacct); // do not flush default account if (uid == defuid) { storage->free_info(defacct); return 2; } // do not flush online users (but flush if FORCE!) if (!prefs_get_user_flush_connected() && !FLAG_ISSET(flags, FS_FORCE)) { if (const char * username = attrgroup_get_attr(attrgroup, "BNET\\acct\\username")) if (t_connection * c = connlist_find_connection_by_accountname(username)) { storage->free_info(defacct); return 2; } } /* sync data to disk if dirty */ attrgroup_save(attrgroup, FS_FORCE); hlist_for_each_safe(curr, &attrgroup->list, save) { attr = hlist_entry(curr, t_attr, link); attr_destroy(attr); } hlist_init(&attrgroup->list); /* reset list */ attrgroup_clear_loaded(attrgroup); storage->free_info(defacct); return 1; } static int _cb_load_attr(const char *key, const char *val, void *data) { t_attrgroup *attrgroup = (t_attrgroup *)data; #ifdef WITH_SQL if (strcmp(prefs_get_storage_path(), "sql") == 0) { const char *tab = key_get_tab(key); bool is_found = false; for (std::vector::iterator it = attrgroup->loadedtabs->begin(); it != attrgroup->loadedtabs->end(); ++it) { if (strcmp(tab, *it) == 0) is_found = true; } // add a tab if it's not found if (!is_found) attrgroup->loadedtabs->push_back(tab); else xfree((void*)tab); } #endif // set loaded attribute without a dirty flag return attrgroup_set_attr(attrgroup, key, val, false); } extern int attrgroup_load(t_attrgroup *attrgroup, const char *tab) { assert(attrgroup); assert(attrgroup->storage); if (FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_LOADED)) { #ifdef WITH_SQL if (strcmp(prefs_get_storage_path(), "sql") == 0) { // find a tab for (std::vector::iterator it = attrgroup->loadedtabs->begin(); it != attrgroup->loadedtabs->end(); ++it) if (strcmp(tab, *it) == 0) return 0; } else #endif return 0; /* already done */ } else { if (FLAG_ISSET(attrgroup->flags, ATTRGROUP_FLAG_DIRTY)) { /* if not loaded, how dirty ? */ eventlog(eventlog_level_error, __FUNCTION__, "can't load modified account"); return -1; } } attrgroup_set_loaded(attrgroup); if (storage->read_attrs(attrgroup->storage, _cb_load_attr, attrgroup, tab)) { eventlog(eventlog_level_error, __FUNCTION__, "got error loading attributes"); return -1; } return 0; } extern int attrgroup_unload(t_attrgroup *attrgroup) { attrgroup_flush(attrgroup, FS_FORCE); return 0; } typedef struct { void *data; t_attr_cb cb; } t_attr_cb_data; static int _cb_read_accounts(t_storage_info *info, void *data) { t_attrgroup *attrgroup; t_attr_cb_data *cbdata = (t_attr_cb_data*)data; attrgroup = attrgroup_create_storage(info); return cbdata->cb(attrgroup, cbdata->data); } extern int attrgroup_read_accounts(int flag, t_attr_cb cb, void *data) { t_attr_cb_data cbdata; cbdata.cb = cb; cbdata.data = data; return storage->read_accounts(flag, _cb_read_accounts, &cbdata); } static const char *attrgroup_escape_key(const char *key) { const char *newkey, *newkey2; char *tmp; newkey = key; if (!strncasecmp(key, "DynKey", 6)) { /* OLD COMMENT, MIGHT NOT BE VALID ANYMORE * Recent Starcraft clients seems to query DynKey\*\1\rank instead of * Record\*\1\rank. So replace Dynkey with Record for key lookup. */ tmp = xstrdup(key); std::strncpy(tmp, "Record", 6); newkey = tmp; } else if (!std::strncmp(key, "Star", 4)) { /* OLD COMMENT * Starcraft clients query Star instead of STAR on logon screen. */ tmp = xstrdup(key); std::strncpy(tmp, "STAR", 4); newkey = tmp; } if (newkey != key) { newkey2 = storage->escape_key(newkey); if (newkey2 != newkey) { xfree((void*)newkey); newkey = newkey2; } } else newkey = storage->escape_key(key); return newkey; } static t_attr *attrgroup_find_attr(t_attrgroup *attrgroup, const char *pkey[], int escape) { const char *val; t_hlist *curr, *last, *last2; t_attr *attr; assert(attrgroup); assert(pkey); assert(*pkey); /* only if the callers tell us to */ if (escape) *pkey = attrgroup_escape_key(*pkey); const char * tab = key_get_tab(*pkey); /* trigger loading of attributes if not loaded already */ if (attrgroup_load(attrgroup, tab)) return NULL; /* eventlog happens earlier */ xfree((void*)tab); /* we are doing attribute lookup so we are accessing it */ attrgroup_set_accessed(attrgroup); last = &attrgroup->list; last2 = NULL; hlist_for_each(curr, &attrgroup->list) { attr = hlist_entry(curr, t_attr, link); if (!strcasecmp(attr_get_key(attr), *pkey)) { val = attr_get_val(attr); /* key found, promote it so it's found faster next time */ hlist_promote(curr, last, last2); break; } last2 = last; last = curr; } if (curr == &attrgroup->list) { /* no key found in cached list */ attr = (t_attr*)storage->read_attr(attrgroup->storage, *pkey); if (attr) hlist_add(&attrgroup->list, &attr->link); } /* "attr" here can either have a proper value found in the cached list, or * a value returned by storage->read_attr, or NULL */ return attr; } /* low-level get attr, receives a flag to tell if it needs to escape key */ static const char *attrgroup_get_attrlow(t_attrgroup *attrgroup, const char *key, int escape) { const char *val = NULL; const char *newkey = key; t_attr *attr; /* no need to check for attrgroup, key */ attr = attrgroup_find_attr(attrgroup, &newkey, escape); // if attribute found if (attr) val = attr_get_val(attr); // if attribute is null then return default attribute value if (!val && attrgroup != attrlayer_get_defattrgroup()) val = attrgroup_get_attrlow(attrlayer_get_defattrgroup(), newkey, 0); if (newkey != key) xfree((void*)newkey); return val; } extern const char *attrgroup_get_attr(t_attrgroup *attrgroup, const char *key) { if (!attrgroup) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL attrgroup"); return NULL; } if (!key) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return NULL; } return attrgroup_get_attrlow(attrgroup, key, 1); } extern int attrgroup_set_attr(t_attrgroup *attrgroup, const char *key, const char *val, bool set_dirty) { t_attr *attr; const char *newkey = key; if (!attrgroup) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL attrgroup"); return -1; } if (!key) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return -1; } attr = attrgroup_find_attr(attrgroup, &newkey, 1); if (attr) { if (attr_get_val(attr) == val || (attr_get_val(attr) && val && !std::strcmp(attr_get_val(attr), val))) goto out; /* no need to modify anything, values are the same */ /* new value for existent key, replace the old one */ attr_set_val(attr, val); } else { /* unknown key so add new attr */ attr = attr_create(newkey, val); hlist_add(&attrgroup->list, &attr->link); } /* we have modified this attr and attrgroup */ if (set_dirty) { attr_set_dirty(attr); attrgroup_set_dirty(attrgroup); } out: if (newkey != key) xfree((void*)newkey); return 0; } // extract tab name from key static const char * key_get_tab(const char *key) { std::string str = std::string(key); std::size_t pos = str.find("_"); std::string find = str.substr(0, pos); return xstrdup(find.c_str()); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/attrgroup.h000066400000000000000000000050261462640461400202660ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __ATTRGROUP_H_INCLUDED__ #define __ATTRGROUP_H_INCLUDED__ #include #include "common/elist.h" #ifndef JUST_NEED_TYPES #define JUST_NEED_TYPES #include "storage.h" #include #undef JUST_NEED_TYPES #else #include "storage.h" #endif #define ATTRGROUP_FLAG_NONE 0 #define ATTRGROUP_FLAG_LOADED 1 #define ATTRGROUP_FLAG_ACCESSED 2 #define ATTRGROUP_FLAG_DIRTY 4 namespace pvpgn { namespace bnetd { /* attrgroup represents a group of attributes which are read/saved/flush together * ex: each account stores it's data into a attrgroup */ typedef struct attrgroup_struct #ifdef ATTRGROUP_INTERNAL_ACCESS { t_hlist list; t_storage_info *storage; int flags; std::time_t lastaccess; std::time_t dirtytime; t_elist loadedlist; t_elist dirtylist; #ifdef WITH_SQL std::vector *loadedtabs; /* sql table names that were loaded */ #endif } #endif t_attrgroup; typedef int(*t_attr_cb)(t_attrgroup *, void *); extern t_attrgroup *attrgroup_create_storage(t_storage_info *storage); extern t_attrgroup *attrgroup_create_newuser(const char *name); extern t_attrgroup *attrgroup_create_nameuid(const char *name, unsigned uid); extern int attrgroup_destroy(t_attrgroup *attrgroup); extern int attrgroup_load(t_attrgroup *attrgroup, const char *tab); extern int attrgroup_unload(t_attrgroup *attrgroup); extern int attrgroup_read_accounts(int flag, t_attr_cb cb, void *data); extern const char *attrgroup_get_attr(t_attrgroup *attrgroup, const char *key); extern int attrgroup_set_attr(t_attrgroup *attrgroup, const char *key, const char *val, bool set_dirty = true); extern int attrgroup_save(t_attrgroup *attrgroup, int flags); extern int attrgroup_flush(t_attrgroup *attrgroup, int flags); } } #endif /* __ATTRGROUP_H_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/attrlayer.cpp000066400000000000000000000117711462640461400206050ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define ATTRGROUP_INTERNAL_ACCESS #include "attrlayer.h" #include "common/eventlog.h" #include "common/flags.h" #include "attr.h" #include "attrgroup.h" #include "storage.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_attrgroup *defattrs = NULL; static DECLARE_ELIST_INIT(loadedlist); static DECLARE_ELIST_INIT(dirtylist); std::vector loadedtabs; static int attrlayer_unload_default(void); extern int attrlayer_init(void) { elist_init(&loadedlist); elist_init(&dirtylist); attrlayer_load_default(); return 0; } extern int attrlayer_cleanup(void) { attrlayer_flush(FS_FORCE | FS_ALL); attrlayer_unload_default(); return 0; } extern int attrlayer_load_default(void) { if (defattrs) attrlayer_unload_default(); defattrs = attrgroup_create_storage(storage->get_defacct()); if (!defattrs) { eventlog(eventlog_level_error, __FUNCTION__, "could not create attrgroup"); return -1; } return attrgroup_load(defattrs, "BNET"); } static int attrlayer_unload_default(void) { attrgroup_destroy(defattrs); defattrs = NULL; return 0; } /* FIXME: dont copy most of the functionality, a good place for a C++ template ;) */ extern int attrlayer_flush(int flags) { static t_elist *curr = &loadedlist; static t_elist *next = NULL; t_attrgroup *attrgroup; unsigned int fcount; unsigned int tcount; fcount = tcount = 0; if (curr == &loadedlist || FLAG_ISSET(flags, FS_ALL)) { curr = elist_next(&loadedlist); next = elist_next(curr); } /* elist_for_each_safe splitted into separate startup for userstep function */ for (; curr != &loadedlist; curr = next, next = elist_next(curr)) { if (!FLAG_ISSET(flags, FS_ALL) && tcount >= prefs_get_user_step()) break; attrgroup = elist_entry(curr, t_attrgroup, loadedlist); switch (attrgroup_flush(attrgroup, flags)) { case 0: /* stop on the first account not flushed (ie accessed too early) */ goto loopout; case 1: fcount++; break; case -1: eventlog(eventlog_level_error, __FUNCTION__, "could not flush account"); break; default: break; } tcount++; } loopout: if (fcount > 0) eventlog(eventlog_level_debug, __FUNCTION__, "flushed {} user accounts", fcount); if (!FLAG_ISSET(flags, FS_ALL) && curr != &loadedlist) return 1; return 0; } extern int attrlayer_save(int flags) { static t_elist *curr = &dirtylist; static t_elist *next = NULL; t_attrgroup *attrgroup; unsigned int scount; unsigned int tcount; scount = tcount = 0; if (curr == &dirtylist || FLAG_ISSET(flags, FS_ALL)) { curr = elist_next(&dirtylist); next = elist_next(curr); } /* elist_for_each_safe splitted into separate startup for userstep function */ for (; curr != &dirtylist; curr = next, next = elist_next(curr)) { if (!FLAG_ISSET(flags, FS_ALL) && tcount >= prefs_get_user_step()) break; attrgroup = elist_entry(curr, t_attrgroup, dirtylist); switch (attrgroup_save(attrgroup, flags)) { case 0: /* stop on the first account not saved (ie dirty too early) */ goto loopout; case 1: scount++; break; case -1: eventlog(eventlog_level_error, __FUNCTION__, "could not save account"); break; default: break; } tcount++; } loopout: if (scount > 0) eventlog(eventlog_level_debug, __FUNCTION__, "saved {} user accounts", scount); if (!FLAG_ISSET(flags, FS_ALL) && curr != &dirtylist) return 1; return 0; } extern void attrlayer_add_loadedlist(t_elist *what) { elist_add_tail(&loadedlist, what); } extern void attrlayer_del_loadedlist(t_elist *what) { elist_del(what); } extern void attrlayer_add_dirtylist(t_elist *what) { elist_add_tail(&dirtylist, what); } extern void attrlayer_del_dirtylist(t_elist *what) { elist_del(what); } extern t_attrgroup * attrlayer_get_defattrgroup(void) { return defattrs; } extern void attrlayer_accessed(t_attrgroup* attrgroup) { /* move the attrgroup at the end of loaded list for the "flush" loop */ elist_del(&attrgroup->loadedlist); elist_add_tail(&loadedlist, &attrgroup->loadedlist); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/attrlayer.h000066400000000000000000000032021462640461400202400ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __ATTRLAYER_H_INCLUDED__ #define __ATTRLAYER_H_INCLUDED__ #include "common/elist.h" #include "attrgroup.h" /* flags controlling flush/save operations */ #define FS_NONE 0 #define FS_FORCE 1 /* force save/flush no matter of time */ #define FS_ALL 2 /* save/flush all, not in steps */ namespace pvpgn { namespace bnetd { extern int attrlayer_init(void); extern int attrlayer_cleanup(void); extern int attrlayer_load_default(void); extern int attrlayer_save(int flags); extern int attrlayer_flush(int flags); extern t_attrgroup * attrlayer_get_defattrgroup(void); extern void attrlayer_add_loadedlist(t_elist *what); extern void attrlayer_del_loadedlist(t_elist *what); extern void attrlayer_add_dirtylist(t_elist *what); extern void attrlayer_del_dirtylist(t_elist *what); extern void attrlayer_accessed(t_attrgroup* attrgroup); } } #endif /* __ATTRLAYER_H_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/autoupdate.cpp000066400000000000000000000170011462640461400207410ustar00rootroot00000000000000/* * Copyright (C) 2000 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 2002 Gianluigi Tiesi (sherpya@netfarm.it) * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * Copyright (C) 2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define AUTOUPDATE_INTERNAL_ACCESS #include "autoupdate.h" #include #include #include "common/eventlog.h" #include "common/list.h" #include "common/util.h" #include "common/proginfo.h" #include "common/tag.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_list * autoupdate_head = NULL; static std::FILE * fp = NULL; /* * Open the autoupdate configuration file, create a linked list of the * clienttag and the update file for it. The format of the file is: * archtagclienttagversiontagupdate file * * Comments begin with # and are ignored. * * The server assumes that the update file is in the "files" directory * so do not include "/" in the filename - it won't be sent * (because it is a security risk). */ extern int autoupdate_load(char const * filename) { unsigned int line; unsigned int pos; char * buff; char * temp; char const * archtag; char const * clienttag; char const * updatefile; char const * versiontag; char const * path; t_autoupdate * entry; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } autoupdate_head = list_create(); for (line = 1; (buff = file_get_line(fp)); line++) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if ((temp = std::strrchr(buff, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } /* FIXME: use next_token instead of std::strtok */ if (!(archtag = std::strtok(buff, " \t"))) { /* std::strtok modifies the string it is passed */ eventlog(eventlog_level_error, __FUNCTION__, "missing archtag on line {} of file \"{}\"", line, filename); continue; } if (!(clienttag = std::strtok(NULL, " \t"))) { eventlog(eventlog_level_error, __FUNCTION__, "missing clienttag on line {} of file \"{}\"", line, filename); continue; } if (!(versiontag = std::strtok(NULL, " \t"))) { eventlog(eventlog_level_error, __FUNCTION__, "missing versiontag on line {} of file \"{}\"", line, filename); continue; } if (!(updatefile = std::strtok(NULL, " \t"))) { eventlog(eventlog_level_error, __FUNCTION__, "missing updatefile on line {} of file \"{}\"", line, filename); continue; } if ((!(path = std::strtok(NULL, " \t"))) && tag_check_wolv1(tag_str_to_uint(clienttag)) && tag_check_wolv2(tag_str_to_uint(clienttag))) { /* Only in WOL is needed to have path */ eventlog(eventlog_level_error, __FUNCTION__, "missing path on line {} of file \"{}\"", line, filename); } entry = (t_autoupdate*)xmalloc(sizeof(t_autoupdate)); if (!tag_check_arch((entry->archtag = tag_str_to_uint(archtag)))) { eventlog(eventlog_level_error, __FUNCTION__, "got unknown archtag"); xfree(entry); continue; } if (!tag_check_client((entry->clienttag = tag_str_to_uint(clienttag)))) { eventlog(eventlog_level_error, __FUNCTION__, "got unknown clienttag"); xfree(entry); continue; } entry->versiontag = xstrdup(versiontag); entry->updatefile = xstrdup(updatefile); if (path) entry->path = xstrdup(path); else entry->path = NULL; eventlog(eventlog_level_debug, __FUNCTION__, "update '{}' version '{}' with file {}", clienttag, versiontag, updatefile); list_append_data(autoupdate_head, entry); } file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); return 0; } /* * Free up all of the elements in the linked list */ extern int autoupdate_unload(void) { if (autoupdate_head) { t_elem * curr; t_autoupdate * entry; LIST_TRAVERSE(autoupdate_head, curr) { if (!(entry = (t_autoupdate*)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); else { xfree((void *)entry->versiontag); /* avoid warning */ xfree((void *)entry->updatefile); /* avoid warning */ if (entry->path) xfree((void *)entry->path); /* avoid warning */ xfree(entry); } list_remove_elem(autoupdate_head, &curr); } if (list_destroy(autoupdate_head) < 0) return -1; autoupdate_head = NULL; } return 0; } /* * Check to see if an update exists for the clients version * return file name if there is one * retrun NULL if no update exists */ extern char * autoupdate_check(t_tag archtag, t_tag clienttag, t_tag gamelang, char const * versiontag, char const * sku) { if (autoupdate_head) { t_elem const * curr; t_autoupdate * entry; char * temp; LIST_TRAVERSE_CONST(autoupdate_head, curr) { if (!(entry = (t_autoupdate*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (entry->archtag != archtag) continue; if (entry->clienttag != clienttag) continue; if (std::strcmp(entry->versiontag, versiontag) != 0) continue; /* if we have a gamelang or SKU then add it to the update file name */ // so far only WAR3 uses gamelang specific MPQs! if (((gamelang) && ((clienttag == CLIENTTAG_WARCRAFT3_UINT) || (clienttag == CLIENTTAG_WAR3XP_UINT))) || ((sku) && (tag_check_wolv2(clienttag)))) { char gltag[5]; char * tempmpq; char * extention; char const * path = entry->path; tempmpq = xstrdup(entry->updatefile); extention = std::strrchr(tempmpq, '.'); *extention = '\0'; extention++; if ((clienttag == CLIENTTAG_WARCRAFT3_UINT) || (clienttag == CLIENTTAG_WAR3XP_UINT)) { tag_uint_to_str(gltag, gamelang); temp = (char*)xmalloc(std::strlen(entry->updatefile) + 6); std::sprintf(temp, "%s_%s.%s", tempmpq, gltag, extention); } else { temp = (char*)xmalloc(std::strlen(path) + std::strlen(entry->updatefile) + std::strlen(sku) + 3); std::sprintf(temp, "%s %s_%s.%s", path, tempmpq, sku, extention); } xfree((void *)tempmpq); return temp; } temp = xstrdup(entry->updatefile); return temp; } } return NULL; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/autoupdate.h000066400000000000000000000033671462640461400204200ustar00rootroot00000000000000/* * Copyright (C) 2000 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 2002 Gianluigi Tiesi (sherpya@netfarm.it) * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * Copyright (C) 2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_AUTOUPDATE_TYPES #define INCLUDED_AUTOUPDATE_TYPES #include "common/tag.h" namespace pvpgn { namespace bnetd { #ifdef AUTOUPDATE_INTERNAL_ACCESS typedef struct { t_tag archtag; t_tag clienttag; char const * versiontag; char const * updatefile; /* used for bnet *.mpq file or wol *.rtp file */ char const * path; /* Used only for WOL FTP update */ } t_autoupdate; #endif } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_AUTOUPDATE_PROTOS #define INCLUDED_AUTOUPDATE_PROTOS namespace pvpgn { namespace bnetd { extern int autoupdate_load(char const * filename); extern int autoupdate_unload(void); extern char * autoupdate_check(t_tag archtag, t_tag clienttag, t_tag gamelang, char const * versiontag, char const * sku); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/channel.cpp000066400000000000000000001334411462640461400202050ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define CHANNEL_INTERNAL_ACCESS #include "common/setup_before.h" #include "channel.h" #include #include #include #include "compat/strdup.h" #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/list.h" #include "common/util.h" #include "common/token.h" #include "common/tag.h" #include "common/xalloc.h" #include "connection.h" #include "message.h" #include "account.h" #include "account_wrap.h" #include "prefs.h" #include "irc.h" #include "i18n.h" #include "common/setup_after.h" #ifdef WITH_LUA #include "luainterface.h" #endif namespace pvpgn { namespace bnetd { static t_list * channellist_head = NULL; static t_channelmember * memberlist_curr = NULL; static int totalcount = 0; static int channellist_load_permanent(char const * filename); static t_channel * channellist_find_channel_by_fullname(char const * name); static char * channel_format_name(char const * sname, char const * country, char const * realmname, unsigned int id); extern int channel_set_userflags(t_connection * c); extern t_channel * channel_create(char const * fullname, char const * shortname, t_clienttag clienttag, int permflag, int botflag, int operflag, int logflag, char const * country, char const * realmname, int maxmembers, int moderated, int clanflag, int autoname, t_list * channellist) { t_channel * channel; if (!fullname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL fullname"); return NULL; } if (fullname[0] == '\0') { eventlog(eventlog_level_error, __FUNCTION__, "got empty fullname"); return NULL; } if (shortname && shortname[0] == '\0') { eventlog(eventlog_level_error, __FUNCTION__, "got empty shortname"); return NULL; } /* non-permanent already checks for this in conn_set_channel */ if (permflag) { if ((channel = channellist_find_channel_by_fullname(fullname))) { if ((channel_get_clienttag(channel)) && (clienttag) && (channel_get_clienttag(channel) == clienttag)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create duplicate permanent channel (fullname \"{}\")", fullname); return NULL; } else if (((channel->flags & channel_flags_allowbots) != (botflag ? channel_flags_allowbots : 0)) || ((channel->flags & channel_flags_allowopers) != (operflag ? channel_flags_allowopers : 0)) || (channel->maxmembers != maxmembers) || ((channel->flags & channel_flags_moderated) != (moderated ? channel_flags_moderated : 0)) || (channel->logname && logflag == 0) || (!(channel->logname) && logflag == 1)) { eventlog(eventlog_level_error, __FUNCTION__, "channel parameters do not match for \"{}\" and \"{}\"", fullname, channel->name); return NULL; } } } channel = (t_channel*)xmalloc(sizeof(t_channel)); if (permflag) { channel->flags = channel_flags_public; if (clienttag && maxmembers != -1) /* approximation.. we want things like "Starcraft USA-1" */ channel->flags |= channel_flags_system; } else channel->flags = channel_flags_none; if (moderated) channel->flags |= channel_flags_moderated; if (shortname && (!strcasecmp(shortname, CHANNEL_NAME_KICKED) || !strcasecmp(shortname, CHANNEL_NAME_BANNED))) channel->flags |= channel_flags_thevoid; eventlog(eventlog_level_debug, __FUNCTION__, "creating new channel \"{}\" shortname={}{}{} clienttag={}{}{} country={}{}{} realm={}{}{}", fullname, shortname ? "\"" : "(", /* all this is doing is printing the name in quotes else "none" in parens */ shortname ? shortname : "none", shortname ? "\"" : ")", clienttag ? "\"" : "(", clienttag ? clienttag_uint_to_str(clienttag) : "none", clienttag ? "\"" : ")", country ? "\"" : "(", country ? country : "none", country ? "\"" : ")", realmname ? "\"" : "(", realmname ? realmname : "none", realmname ? "\"" : ")"); channel->name = xstrdup(fullname); if (!shortname) channel->shortname = NULL; else channel->shortname = xstrdup(shortname); channel->clienttag = clienttag; if (country) channel->country = xstrdup(country); else channel->country = NULL; if (realmname) channel->realmname = xstrdup(realmname); else channel->realmname = NULL; channel->banlist = list_create(); totalcount++; if (totalcount == 0) /* if we wrap (yeah right), don't use id 0 */ totalcount = 1; channel->id = totalcount; channel->maxmembers = maxmembers; channel->currmembers = 0; channel->memberlist = NULL; if (permflag) channel->flags |= channel_flags_permanent; if (botflag) channel->flags |= channel_flags_allowbots; if (operflag) channel->flags |= channel_flags_allowopers; if (clanflag) channel->flags |= channel_flags_clan; if (autoname) channel->flags |= channel_flags_autoname; if (logflag) { std::time_t now; struct std::tm * tmnow; char dstr[64]; char timetemp[CHANLOG_TIME_MAXLEN]; now = std::time(NULL); if (!(tmnow = std::localtime(&now))) dstr[0] = '\0'; else std::sprintf(dstr, "%04d%02d%02d%02d%02d%02d", 1900 + tmnow->tm_year, tmnow->tm_mon + 1, tmnow->tm_mday, tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec); channel->logname = (char*)xmalloc(std::strlen(prefs_get_chanlogdir()) + 9 + std::strlen(dstr) + 1 + 6 + 1); /* dir + "/chanlog-" + dstr + "-" + id + NUL */ std::sprintf(channel->logname, "%s/chanlog-%s-%06u", prefs_get_chanlogdir(), dstr, channel->id); if (!(channel->log = std::fopen(channel->logname, "w"))) eventlog(eventlog_level_error, __FUNCTION__, "could not open channel log \"{}\" for writing (std::fopen: {})", channel->logname, std::strerror(errno)); else { std::fprintf(channel->log, "name=\"%s\"\n", channel->name); if (channel->shortname) std::fprintf(channel->log, "shortname=\"%s\"\n", channel->shortname); else std::fprintf(channel->log, "shortname=none\n"); std::fprintf(channel->log, "permanent=\"%s\"\n", (channel->flags & channel_flags_permanent) ? "true" : "false"); std::fprintf(channel->log, "allowbotse=\"%s\"\n", (channel->flags & channel_flags_allowbots) ? "true" : "false"); std::fprintf(channel->log, "allowopers=\"%s\"\n", (channel->flags & channel_flags_allowopers) ? "true" : "false"); if (channel->clienttag) std::fprintf(channel->log, "clienttag=\"%s\"\n", clienttag_uint_to_str(channel->clienttag)); else std::fprintf(channel->log, "clienttag=none\n"); if (tmnow) std::strftime(timetemp, sizeof(timetemp), CHANLOG_TIME_FORMAT, tmnow); else std::strcpy(timetemp, "?"); std::fprintf(channel->log, "created=\"%s\"\n\n", timetemp); std::fflush(channel->log); } } else { channel->logname = NULL; channel->log = NULL; } channel->gameType = 0; channel->gameExtension = NULL; if (channellist) list_append_data(channellist, channel); else DEBUG0("channel was not added into any channellist"); eventlog(eventlog_level_debug, __FUNCTION__, "channel created successfully"); return channel; } extern t_channel * channel_create(char const * fullname, char const * shortname, t_clienttag clienttag, int permflag, int botflag, int operflag, int logflag, char const * country, char const * realmname, int maxmembers, int moderated, int clanflag, int autoname) { return channel_create(fullname, shortname, clienttag, permflag, botflag, operflag, logflag, country, realmname, maxmembers, moderated, clanflag, autoname, channellist_head); } extern int channel_destroy(t_channel * channel, t_elem ** curr) { t_elem * ban; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } if (channel->memberlist) { eventlog(eventlog_level_debug, __FUNCTION__, "channel is not empty, deferring"); channel->flags &= ~channel_flags_permanent; /* make it go away when the last person leaves */ return -1; } if (list_remove_data(channellist_head, channel, curr) < 0) { // eventlog(eventlog_level_error,__FUNCTION__,"could not remove item from list"); eventlog(eventlog_level_info, __FUNCTION__, "channel was not removed from any list"); // return -1; } eventlog(eventlog_level_info, __FUNCTION__, "destroying channel \"{}\"", channel->name); if (channel->gameExtension) xfree(channel->gameExtension); LIST_TRAVERSE(channel->banlist, ban) { char const * banned; if (!(banned = (char*)elem_get_data(ban))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL name in banlist"); else xfree((void *)banned); /* avoid warning */ if (list_remove_elem(channel->banlist, &ban) < 0) eventlog(eventlog_level_error, __FUNCTION__, "unable to remove item from list"); } list_destroy(channel->banlist); if (channel->log) { std::time_t now; struct std::tm * tmnow; char timetemp[CHANLOG_TIME_MAXLEN]; now = std::time(NULL); if ((!(tmnow = std::localtime(&now)))) std::strcpy(timetemp, "?"); else std::strftime(timetemp, sizeof(timetemp), CHANLOG_TIME_FORMAT, tmnow); std::fprintf(channel->log, "\ndestroyed=\"%s\"\n", timetemp); if (std::fclose(channel->log) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close channel log \"{}\" after writing (std::fclose: {})", channel->logname, std::strerror(errno)); } if (channel->logname) xfree((void *)channel->logname); /* avoid warning */ if (channel->country) xfree((void *)channel->country); /* avoid warning */ if (channel->realmname) xfree((void *)channel->realmname); /* avoid warning */ if (channel->shortname) xfree((void *)channel->shortname); /* avoid warning */ xfree((void *)channel->name); /* avoid warning */ xfree(channel); return 0; } extern char const * channel_get_name(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_warn, __FUNCTION__, "got NULL channel"); return ""; } return channel->name; } extern t_clienttag channel_get_clienttag(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } return channel->clienttag; } extern unsigned channel_get_flags(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return channel_flags_none; } return channel->flags; } extern int channel_set_flags(t_channel * channel, unsigned flags) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } channel->flags = flags; return 0; } extern int channel_get_permanent(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } return (channel->flags & channel_flags_permanent); } extern unsigned int channel_get_channelid(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } return channel->id; } extern int channel_set_channelid(t_channel * channel, unsigned int channelid) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } channel->id = channelid; return 0; } extern int channel_rejoin(t_connection * conn) { t_channel const * channel; char const * temp; char const * chname; if (!(channel = conn_get_channel(conn))) return -1; if (!(temp = channel_get_name(channel))) return -1; chname = xstrdup(temp); conn_part_channel(conn); if (conn_set_channel(conn, chname) < 0) conn_set_channel(conn, CHANNEL_NAME_BANNED); xfree((void *)chname); return 0; } extern int channel_add_connection(t_channel * channel, t_connection * connection) { t_channelmember * member; t_connection * user; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } if (!connection) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (channel_check_banning(channel, connection)) { channel_message_log(channel, connection, 0, "JOIN FAILED (banned)"); return -1; } member = (t_channelmember*)xmalloc(sizeof(t_channelmember)); member->connection = connection; member->next = channel->memberlist; channel->memberlist = member; channel->currmembers++; channel_message_log(channel, connection, 0, "JOINED"); message_send_text(connection, message_type_channel, connection, channel_get_name(channel)); if ((!(channel->flags & channel_flags_permanent)) && (!(channel->flags & channel_flags_thevoid)) && (!(channel->flags & channel_flags_clan)) && (channel->currmembers == 1) && (account_is_operator_or_admin(conn_get_account(connection), channel_get_name(channel)) == 0)) { message_send_text(connection, message_type_info, connection, localize(connection, "you are now tempOP for this channel")); conn_set_tmpOP_channel(connection, (char *)channel_get_name(channel)); channel_update_userflags(connection); } if (!(channel_get_flags(channel) & channel_flags_thevoid)) for (user = channel_get_first(channel); user; user = channel_get_next()) { message_send_text(connection, message_type_adduser, user, NULL); /* In WOL gamechannels we send JOINGAME ack explicitely to self */ if (!conn_get_game(connection)) message_send_text(user, message_type_join, connection, NULL); } else { if (!conn_get_game(connection)) message_send_text(connection, message_type_join, connection, NULL); } if (conn_is_irc_variant(connection) && (!conn_get_game(connection))) { channel_set_userflags(connection); message_send_text(connection, message_type_topic, connection, NULL); message_send_text(connection, message_type_namreply, connection, NULL); } /* please don't remove this notice */ if (channel->log) message_send_text(connection, message_type_info, connection, prefs_get_log_notice()); #ifdef WITH_LUA lua_handle_channel(channel, connection, NULL, message_type_null, luaevent_channel_userjoin); #endif return 0; } extern int channel_del_connection(t_channel * channel, t_connection * connection, t_message_type type, char const * text) { t_channelmember * curr; t_channelmember * temp; t_elem * curr2; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } if (!connection) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } channel_message_send(channel, type, connection, text); channel_message_log(channel, connection, 0, "PARTED"); curr = channel->memberlist; if (curr->connection == connection) { channel->memberlist = channel->memberlist->next; xfree(curr); } else { while (curr->next && curr->next->connection != connection) curr = curr->next; if (curr->next) { temp = curr->next; curr->next = curr->next->next; xfree(temp); } else { eventlog(eventlog_level_error, __FUNCTION__, "[{}] connection not in channel member list", conn_get_socket(connection)); return -1; } } channel->currmembers--; if (conn_get_tmpOP_channel(connection) && std::strcmp(conn_get_tmpOP_channel(connection), channel_get_name(channel)) == 0) { conn_set_tmpOP_channel(connection, NULL); } #ifdef WITH_LUA lua_handle_channel(channel, connection, text, type, luaevent_channel_userleft); #endif if (!channel->memberlist && !(channel->flags & channel_flags_permanent)) /* if channel is empty, delete it unless it's a permanent channel */ { channel_destroy(channel, &curr2); } return 0; } extern void channel_update_latency(t_connection * me) { t_channel * channel; t_message * message; t_connection * c; if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!(channel = conn_get_channel(me))) { eventlog(eventlog_level_error, __FUNCTION__, "connection has no channel"); return; } if (!(message = message_create(message_type_userflags, me, NULL))) /* handles NULL text */ return; for (c = channel_get_first(channel); c; c = channel_get_next()) if (conn_get_class(c) == conn_class_bnet) message_send(message, c); message_destroy(message); } extern void channel_update_userflags(t_connection * me) { t_channel * channel; t_message * message; t_connection * c; if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!(channel = conn_get_channel(me))) { eventlog(eventlog_level_error, __FUNCTION__, "connection has no channel"); return; } if (!(message = message_create(message_type_userflags, me, NULL))) /* handles NULL text */ return; for (c = channel_get_first(channel); c; c = channel_get_next()) message_send(message, c); message_destroy(message); } extern void channel_message_log(t_channel const * channel, t_connection * me, int fromuser, char const * text) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return; } if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (channel->log) { std::time_t now; struct std::tm * tmnow; char timetemp[CHANLOG_TIME_MAXLEN]; now = std::time(NULL); if ((!(tmnow = std::localtime(&now)))) std::strcpy(timetemp, "?"); else std::strftime(timetemp, sizeof(timetemp), CHANLOGLINE_TIME_FORMAT, tmnow); if (fromuser) std::fprintf(channel->log, "%s: \"%s\" \"%s\"\n", timetemp, conn_get_username(me), text); else std::fprintf(channel->log, "%s: \"%s\" %s\n", timetemp, conn_get_username(me), text); std::fflush(channel->log); } } extern void channel_message_send(t_channel const * channel, t_message_type type, t_connection * me, char const * text) { t_connection * c; unsigned int heard; char const * tname; t_message * message1; //send to people with clienttag matching channel clienttag // or everyone when channel has no clienttag set t_message * message2; //send to people with clienttag not matching channel clienttag t_message * message_to_send; t_account * acc; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return; } if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } acc = conn_get_account(me); if (channel_get_flags(channel) & channel_flags_thevoid) // no talking in the void if (type != message_type_join && type != message_type_part) return; // if user is muted if (account_get_auth_mute(acc) == 1) { std::string msgtemp; msgtemp = localize(me, "You can't talk on the channel. Your account has been muted"); msgtemp += account_get_mutetext(me, acc, false); message_send_text(me, message_type_error, me, msgtemp); return; } if (channel_get_flags(channel) & channel_flags_moderated) // moderated channel - only admins,OPs and voices may talk { if (type == message_type_talk || type == message_type_emote) { if (!((account_is_operator_or_admin(acc, channel_get_name(channel))) || (channel_conn_has_tmpVOICE(channel, me)) || (account_get_auth_voice(acc, channel_get_name(channel)) == 1))) { message_send_text(me, message_type_error, me, localize(me, "This channel is moderated")); return; } } } if (!channel->clienttag){ if (!(message1 = message_create(type, me, text))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create message1"); return; } message2 = NULL; } else { if (!(message1 = message_create(type, me, text))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create message1"); return; } if (!(message2 = message_create(type, me, text))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create message2"); message_destroy(message1); return; } } heard = 0; tname = conn_get_chatname(me); for (c = channel_get_first(channel); c; c = channel_get_next()) { if (c == me && (type == message_type_talk || type == message_type_gameopt_talk)) continue; /* ignore ourself */ if (c == me && (!conn_is_irc_variant(c)) && type == message_type_part) continue; /* only on irc we need to inform ourself about leaving the channel */ if (c != me && (!conn_is_irc_variant(c)) && (channel_get_flags(channel) & channel_flags_thevoid) && (type == message_type_join || type == message_type_part)) continue; /* make sure we even get join part information about self in The Void */ if ((type == message_type_talk || type == message_type_whisper || type == message_type_emote || type == message_type_broadcast) && conn_check_ignoring(c, tname) == 1) continue; /* ignore squelched players */ if (!channel->clienttag || channel->clienttag == conn_get_clienttag(c)) { message_to_send = message1; } else { message_to_send = message2; } if (message_send(message_to_send, c) == 0 && c != me) heard = 1; } conn_unget_chatname(me, tname); message_destroy(message1); if (message2) message_destroy(message2); if ((conn_get_wol(me) == 0)) { if (!heard && (type == message_type_talk || type == message_type_emote)) message_send_text(me, message_type_info, me, localize(me, "No one hears you.")); } #ifdef WITH_LUA if (type != message_type_part) lua_handle_channel((t_channel*)channel, me, text, type, luaevent_channel_message); #endif } extern int channel_ban_user(t_channel * channel, char const * user) { t_elem const * curr; char * temp; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } if (!user) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL user"); return -1; } if (!channel->name) { eventlog(eventlog_level_error, __FUNCTION__, "got channel with NULL name"); return -1; } if (strcasecmp(channel->name, CHANNEL_NAME_BANNED) == 0 || strcasecmp(channel->name, CHANNEL_NAME_KICKED) == 0) return -1; LIST_TRAVERSE_CONST(channel->banlist, curr) if (strcasecmp((char*)elem_get_data(curr), user) == 0) return 0; temp = xstrdup(user); list_append_data(channel->banlist, temp); return 0; } extern int channel_unban_user(t_channel * channel, char const * user) { t_elem * curr; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } if (!user) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL user"); return -1; } LIST_TRAVERSE(channel->banlist, curr) { char const * banned; if (!(banned = (char*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL name in banlist"); continue; } if (strcasecmp(banned, user) == 0) { if (list_remove_elem(channel->banlist, &curr) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "unable to remove item from list"); return -1; } xfree((void *)banned); /* avoid warning */ return 0; } } return -1; } extern int channel_check_banning(t_channel const * channel, t_connection const * user) { t_elem const * curr; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } if (!user) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL user"); return -1; } if (!(channel->flags & channel_flags_allowbots) && conn_get_class(user) == conn_class_bot) return 1; LIST_TRAVERSE_CONST(channel->banlist, curr) if (conn_match(user, (char*)elem_get_data(curr)) == 1) return 1; return 0; } extern int channel_get_length(t_channel const * channel) { t_channelmember const * curr; int count; for (curr = channel->memberlist, count = 0; curr; curr = curr->next, count++); return count; } extern t_connection * channel_get_first(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return NULL; } memberlist_curr = channel->memberlist; return channel_get_next(); } extern t_connection * channel_get_next(void) { t_channelmember * member; if (memberlist_curr) { member = memberlist_curr; memberlist_curr = memberlist_curr->next; return member->connection; } return NULL; } extern t_list * channel_get_banlist(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_warn, __FUNCTION__, "got NULL channel"); return NULL; } return channel->banlist; } extern char const * channel_get_shortname(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_warn, __FUNCTION__, "got NULL channel"); return NULL; } return channel->shortname; } static int channellist_load_permanent(char const * filename) { std::FILE * fp; unsigned int line; unsigned int pos; int botflag; int operflag; int logflag; unsigned int modflag; char * buff; char * name; char * sname; char * tag; t_clienttag clienttag; char * bot; char * oper; char * log; char * country; char * max; char * moderated; char * newname; char * realmname; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open channel file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } for (line = 1; (buff = file_get_line(fp)); line++) { if (buff[0] == '#' || buff[0] == '\0') { continue; } pos = 0; if (!(name = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing name in line {} in file \"{}\"", line, filename); continue; } if (!(sname = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing sname in line {} in file \"{}\"", line, filename); continue; } if (!(tag = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing tag in line {} in file \"{}\"", line, filename); continue; } if (!(bot = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing bot in line {} in file \"{}\"", line, filename); continue; } if (!(oper = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing oper in line {} in file \"{}\"", line, filename); continue; } if (!(log = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing log in line {} in file \"{}\"", line, filename); continue; } if (!(country = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing country in line {} in file \"{}\"", line, filename); continue; } if (!(realmname = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing realmname in line {} in file \"{}\"", line, filename); continue; } if (!(max = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing max in line {} in file \"{}\"", line, filename); continue; } if (!(moderated = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing mod in line {} in file \"{}\"", line, filename); continue; } switch (str_get_bool(bot)) { case 1: botflag = 1; break; case 0: botflag = 0; break; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid boolean value \"{}\" for field 4 on line {} in file \"{}\"", bot, line, filename); continue; } switch (str_get_bool(oper)) { case 1: operflag = 1; break; case 0: operflag = 0; break; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid boolean value \"{}\" for field 5 on line {} in file \"{}\"", oper, line, filename); continue; } switch (str_get_bool(log)) { case 1: logflag = 1; break; case 0: logflag = 0; break; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid boolean value \"{}\" for field 5 on line {} in file \"{}\"", log, line, filename); continue; } switch (str_get_bool(moderated)) { case 1: modflag = 1; break; case 0: modflag = 0; break; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid boolean value \"{}\" for field 10 on line {} in file \"{}\"", moderated, line, filename); continue; } if (std::strcmp(sname, "NULL") == 0) sname = NULL; if (std::strcmp(tag, "NULL") == 0) clienttag = 0; else clienttag = clienttag_str_to_uint(tag); if (std::strcmp(name, "NONE") == 0) name = NULL; if (std::strcmp(country, "NULL") == 0) country = NULL; if (std::strcmp(realmname, "NULL") == 0) realmname = NULL; if (name) { channel_create(name, sname, clienttag, 1, botflag, operflag, logflag, country, realmname, std::atoi(max), modflag, 0, 0); } else { newname = channel_format_name(sname, country, realmname, 1); if (newname) { channel_create(newname, sname, clienttag, 1, botflag, operflag, logflag, country, realmname, std::atoi(max), modflag, 0, 1); xfree(newname); } else { eventlog(eventlog_level_error, __FUNCTION__, "cannot format channel name"); } } /* FIXME: call channel_delete() on current perm channels and do a channellist_find_channel() and set the long name, perm flag, etc, otherwise call channel_create(). This will make HUPing the server handle re-reading this file correctly. */ } file_get_line(NULL); // clear file_get_line buffer if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close channel file \"{}\" after reading (std::fclose: {})", filename, std::strerror(errno)); return 0; } static char * channel_format_name(char const * sname, char const * country, char const * realmname, unsigned int id) { char * fullname; unsigned int len; if (!sname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL sname"); return NULL; } len = std::strlen(sname) + 1; /* FIXME: check lengths and format */ if (country) len = len + std::strlen(country) + 1; if (realmname) len = len + std::strlen(realmname) + 1; len = len + 32 + 1; fullname = (char*)xmalloc(len); std::sprintf(fullname, "%s%s%s%s%s-%u", realmname ? realmname : "", realmname ? " " : "", sname, country ? " " : "", country ? country : "", id); return fullname; } extern int channellist_reload(void) { t_elem * curr; t_channel * channel, *old_channel; t_channelmember * memberlist, *member, *old_member; t_list * channellist_old; if (channellist_head) { channellist_old = list_create(); /* First pass - get members */ LIST_TRAVERSE(channellist_head, curr) { if (!(channel = (t_channel*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "channel list contains NULL item"); continue; } /* Trick to avoid automatic channel destruction */ channel->flags |= channel_flags_permanent; if (channel->memberlist) { /* we need only channel name and memberlist */ old_channel = (t_channel *)xmalloc(sizeof(t_channel)); old_channel->shortname = xstrdup(channel->shortname); old_channel->memberlist = NULL; member = channel->memberlist; /* First pass */ while (member) { old_member = (t_channelmember*)xmalloc(sizeof(t_channelmember)); old_member->connection = member->connection; if (old_channel->memberlist) old_member->next = old_channel->memberlist; else old_member->next = NULL; old_channel->memberlist = old_member; member = member->next; } /* Second pass - remove connections from channel */ member = old_channel->memberlist; while (member) { channel_del_connection(channel, member->connection, message_type_quit, NULL); conn_set_channel_var(member->connection, NULL); member = member->next; } list_prepend_data(channellist_old, old_channel); } /* Channel is empty - Destroying it */ channel->flags &= ~channel_flags_permanent; if (channel_destroy(channel, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not destroy channel"); } /* Cleanup and reload */ if (list_destroy(channellist_head) < 0) return -1; channellist_head = NULL; channellist_create(); /* Now put all users on their previous channel */ LIST_TRAVERSE(channellist_old, curr) { if (!(channel = (t_channel*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "old channel list contains NULL item"); continue; } memberlist = channel->memberlist; while (memberlist) { member = memberlist; memberlist = memberlist->next; conn_set_channel(member->connection, channel->shortname); } } /* Ross don't blame me for this but this way the code is cleaner */ LIST_TRAVERSE(channellist_old, curr) { if (!(channel = (t_channel*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "old channel list contains NULL item"); continue; } memberlist = channel->memberlist; while (memberlist) { member = memberlist; memberlist = memberlist->next; xfree((void*)member); } if (channel->shortname) xfree((void*)channel->shortname); if (list_remove_data(channellist_old, channel, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); xfree((void*)channel); } if (list_destroy(channellist_old) < 0) return -1; } return 0; } extern int channellist_create(void) { channellist_head = list_create(); return channellist_load_permanent(prefs_get_channelfile()); } extern int channellist_destroy(void) { t_channel * channel; t_elem * curr; if (channellist_head) { LIST_TRAVERSE(channellist_head, curr) { if (!(channel = (t_channel*)elem_get_data(curr))) /* should not happen */ { eventlog(eventlog_level_error, __FUNCTION__, "channel list contains NULL item"); continue; } channel_destroy(channel, &curr); } if (list_destroy(channellist_head) < 0) return -1; channellist_head = NULL; } return 0; } extern t_list * channellist(void) { return channellist_head; } extern int channellist_get_length(void) { return list_get_length(channellist_head); } extern int channel_get_max(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } return channel->maxmembers; } extern int channel_set_max(t_channel * channel, int maxmembers) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } if (maxmembers) channel->maxmembers = maxmembers; return 1; } extern int channel_get_curr(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } return channel->currmembers; } extern int channel_conn_is_tmpOP(t_channel const * channel, t_connection * c) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return 0; } if (!conn_get_tmpOP_channel(c)) return 0; if (std::strcmp(conn_get_tmpOP_channel(c), channel_get_name(channel)) == 0) return 1; return 0; } extern int channel_conn_has_tmpVOICE(t_channel const * channel, t_connection * c) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return 0; } if (!conn_get_tmpVOICE_channel(c)) return 0; if (std::strcmp(conn_get_tmpVOICE_channel(c), channel_get_name(channel)) == 0) return 1; return 0; } static t_channel * channellist_find_channel_by_fullname(char const * name) { t_channel * channel; t_elem const * curr; if (channellist_head) { LIST_TRAVERSE(channellist_head, curr) { channel = (t_channel*)elem_get_data(curr); if (!channel->name) { eventlog(eventlog_level_error, __FUNCTION__, "found channel with NULL name"); continue; } if (strcasecmp(channel->name, name) == 0) return channel; } } return NULL; } /* Find a channel based on the name. * Create a new channel if it is a permanent-type channel and all others * are full. */ extern t_channel * channellist_find_channel_by_name(char const * name, char const * country, char const * realmname) { t_channel * channel; t_elem const * curr; int foundperm; int foundlang; int maxchannel; /* the number of "rollover" channels that exist */ char const * saveshortname; char const * savespecialname; t_clienttag savetag; int savebotflag; int saveoperflag; int savelogflag; unsigned int savemoderated; char const * savecountry; char const * saverealmname; int savemaxmembers; t_channel * special_channel; // try to make gcc happy and initialize all variables saveshortname = savespecialname = savecountry = saverealmname = NULL; savetag = savebotflag = saveoperflag = savelogflag = savemaxmembers = savemoderated = 0; maxchannel = 0; foundperm = 0; foundlang = 0; if (channellist_head) { LIST_TRAVERSE(channellist_head, curr) { channel = (t_channel*)elem_get_data(curr); if (!channel->name) { eventlog(eventlog_level_error, __FUNCTION__, "found channel with NULL name"); continue; } if (strcasecmp(channel->name, name) == 0) { // eventlog(eventlog_level_debug,__FUNCTION__,"found exact match for \"%s\"",name); return channel; } if (channel->shortname && strcasecmp(channel->shortname, name) == 0) { special_channel = channellist_find_channel_by_name(channel->name, country, realmname); if (special_channel) channel = special_channel; /* FIXME: what should we do if the client doesn't have a country? For now, just take the first * channel that would otherwise match. */ if (((!channel->country && !foundlang) || !country || (channel->country && country && (std::strcmp(channel->country, country) == 0))) && (!channel->realmname || !realmname || !std::strcmp(channel->realmname, realmname))) { if (channel->maxmembers == -1 || channel->currmembers < channel->maxmembers) { eventlog(eventlog_level_debug, __FUNCTION__, "found permanent channel \"{}\" for \"{}\"", channel->name, name); return channel; } if (!foundlang && (channel->country)) //remember we had found a language specific channel but it was full { foundlang = 1; if (!(channel->flags & channel_flags_autoname)) savespecialname = channel->name; maxchannel = 0; } maxchannel++; } // eventlog(eventlog_level_debug,__FUNCTION__,"countries didn't match"); foundperm = 1; /* save off some info in case we need to create a new copy */ saveshortname = channel->shortname; savetag = channel->clienttag; savebotflag = channel->flags & channel_flags_allowbots; saveoperflag = channel->flags & channel_flags_allowopers; if (channel->logname) savelogflag = 1; else savelogflag = 0; if (country) savecountry = country; else savecountry = channel->country; if (realmname) saverealmname = realmname; else saverealmname = channel->realmname; savemaxmembers = channel->maxmembers; savemoderated = channel->flags & channel_flags_moderated; } } } /* we've gone thru the whole list and either there was no match or the * channels are all full. */ if (foundperm) /* All the channels were full, create a new one */ { char * channelname; if (!foundlang || !savespecialname) { if (!(channelname = channel_format_name(saveshortname, savecountry, saverealmname, maxchannel + 1))) return NULL; } else { if (!(channelname = channel_format_name(savespecialname, NULL, saverealmname, maxchannel + 1))) return NULL; } channel = channel_create(channelname, saveshortname, savetag, 1, savebotflag, saveoperflag, savelogflag, savecountry, saverealmname, savemaxmembers, savemoderated, 0, 1); xfree(channelname); eventlog(eventlog_level_debug, __FUNCTION__, "created copy \"{}\" of channel \"{}\"", (channel) ? (channel->name) : (""), name); return channel; } /* no match */ eventlog(eventlog_level_debug, __FUNCTION__, "could not find channel \"{}\"", name); return NULL; } extern t_channel * channellist_find_channel_bychannelid(unsigned int channelid) { t_channel * channel; t_elem const * curr; if (channellist_head) { LIST_TRAVERSE(channellist_head, curr) { channel = (t_channel*)elem_get_data(curr); if (!channel->name) { eventlog(eventlog_level_error, __FUNCTION__, "found channel with NULL name"); continue; } if (channel->id == channelid) return channel; } } return NULL; } extern int channel_set_userflags(t_connection * c) { unsigned int newflags; char const * channel; t_account * acc; if (!c) return -1; // user not connected, no need to update his flags acc = conn_get_account(c); /* well... unfortunatly channel_get_name never returns NULL but "" instead so we first have to check if user is in a channel at all */ if ((!conn_get_channel(c)) || (!(channel = channel_get_name(conn_get_channel(c))))) return -1; if (account_get_auth_admin(acc, channel) == 1 || account_get_auth_admin(acc, NULL) == 1) newflags = MF_BLIZZARD; else if (account_get_auth_operator(acc, channel) == 1 || account_get_auth_operator(acc, NULL) == 1) newflags = MF_BNET; else if (channel_conn_is_tmpOP(conn_get_channel(c), c)) newflags = MF_GAVEL; else if ((account_get_auth_voice(acc, channel) == 1) || (channel_conn_has_tmpVOICE(conn_get_channel(c), c))) newflags = MF_VOICE; else if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) newflags = W3_ICON_SET; else newflags = 0; if (conn_get_flags(c) != newflags) { conn_set_flags(c, newflags); channel_update_userflags(c); } return 0; } /** * Westwood Online Extensions */ extern int channel_get_min(t_channel const * channel) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } return channel->minmembers; } extern int channel_set_min(t_channel * channel, int minmembers) { if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return 0; } if (minmembers) channel->minmembers = minmembers; return 1; } extern int channel_wol_get_game_type(t_channel const * channel) { if (!channel) { ERROR0("got NULL channel"); return -1; } return channel->gameType; } extern int channel_wol_set_game_type(t_channel * channel, int gameType) { if (!channel) { ERROR0("got NULL channel"); return -1; } if (gameType) channel->gameType = gameType; return 0; } extern char const * channel_wol_get_game_extension(t_channel const * channel) { if (!channel) { ERROR0("got NULL channel"); return 0; } return channel->gameExtension; } extern int channel_wol_set_game_extension(t_channel * channel, char const * gameExtension) { if (!channel) { ERROR0("got NULL channel"); return -1; } if (!gameExtension) { ERROR0("got NULL gameExtension"); return -1; } if (channel->gameExtension) xfree(channel->gameExtension); channel->gameExtension = xstrdup(gameExtension); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/channel.h000066400000000000000000000152101462640461400176430ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000 Marco Ziech (mmz@gmx.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_CHANNEL_TYPES #define INCLUDED_CHANNEL_TYPES #ifdef JUST_NEED_TYPES # include "common/bn_type.h" #else # define JUST_NEED_TYPES # include "common/bn_type.h" # undef JUST_NEED_TYPES #endif # include "account.h" #ifdef CHANNEL_INTERNAL_ACCESS #include #ifdef JUST_NEED_TYPES # include "connection.h" # include "common/list.h" #else # define JUST_NEED_TYPES # include "connection.h" # include "common/list.h" # undef JUST_NEED_TYPES #endif #endif namespace pvpgn { namespace bnetd { #ifdef CHANNEL_INTERNAL_ACCESS typedef struct channelmember { /* standalone mode */ t_connection * connection; struct channelmember * next; } t_channelmember; #endif typedef enum { channel_flags_none = 0x00, channel_flags_public = 0x01, channel_flags_moderated = 0x02, channel_flags_restricted = 0x04, channel_flags_thevoid = 0x08, channel_flags_system = 0x10, channel_flags_official = 0x20, channel_flags_permanent = 0x40, channel_flags_allowbots = 0x80, channel_flags_allowopers = 0x100, channel_flags_clan = 0x200, channel_flags_autoname = 0x400 } t_channel_flags; typedef struct channel #ifdef CHANNEL_INTERNAL_ACCESS { char const * name; char const * shortname; /* short "alias" for permanent channels, NULL if none */ char const * country; char const * realmname; unsigned int flags; int maxmembers; int currmembers; t_clienttag clienttag; unsigned int id; t_channelmember * memberlist; t_list * banlist; /* of char * */ char * logname; /* NULL if not logged */ std::FILE * log; /* NULL if not logging */ /** * Westwood Online Extensions */ int minmembers; int gameType; char * gameExtension; } #endif t_channel; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_CHANNEL_PROTOS #define INCLUDED_CHANNEL_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "message.h" #include "common/list.h" #include "common/tag.h" #undef JUST_NEED_TYPES #define CHANNEL_NAME_BANNED "THE VOID" #define CHANNEL_NAME_KICKED "THE VOID" #define CHANNEL_NAME_CHAT "Chat" namespace pvpgn { namespace bnetd { extern int channel_set_userflags(t_connection * c); extern t_channel * channel_create(char const * fullname, char const * shortname, t_clienttag clienttag, int permflag, int botflag, int operflag, int logflag, char const * country, char const * realmname, int maxmembers, int moderated, int clanflag, int autoname, t_list * channellist); extern t_channel * channel_create(char const * fullname, char const * shortname, t_clienttag clienttag, int permflag, int botflag, int operflag, int logflag, char const * country, char const * realmname, int maxmembers, int moderated, int clanflag, int autoname); extern int channel_destroy(t_channel * channel, t_elem ** elem); extern char const * channel_get_name(t_channel const * channel); extern char const * channel_get_shortname(t_channel const * channel); extern t_clienttag channel_get_clienttag(t_channel const * channel); extern unsigned channel_get_flags(t_channel const * channel); extern int channel_set_flags(t_channel * channel, unsigned flags); extern int channel_get_permanent(t_channel const * channel); extern unsigned int channel_get_channelid(t_channel const * channel); extern int channel_set_channelid(t_channel * channel, unsigned int channelid); extern int channel_add_connection(t_channel * channel, t_connection * connection); extern int channel_del_connection(t_channel * channel, t_connection * connection, t_message_type mess, char const * text); extern void channel_update_latency(t_connection * conn); extern void channel_update_userflags(t_connection * conn); extern void channel_message_log(t_channel const * channel, t_connection * me, int fromuser, char const * text); extern void channel_message_send(t_channel const * channel, t_message_type type, t_connection * conn, char const * text); extern int channel_ban_user(t_channel * channel, char const * user); extern int channel_unban_user(t_channel * channel, char const * user); extern int channel_check_banning(t_channel const * channel, t_connection const * user); extern int channel_rejoin(t_connection * conn); extern t_list * channel_get_banlist(t_channel const * channel); extern int channel_get_length(t_channel const * channel); extern int channel_get_max(t_channel const * channel); extern int channel_set_max(t_channel * channel, int maxmembers); extern int channel_get_curr(t_channel const * channel); extern int channel_conn_is_tmpOP(t_channel const * channel, t_connection * c); extern int channel_conn_has_tmpVOICE(t_channel const * channel, t_connection * c); extern t_connection * channel_get_first(t_channel const * channel); extern t_connection * channel_get_next(void); extern int channellist_create(void); extern int channellist_destroy(void); extern int channellist_reload(void); extern t_list * channellist(void); extern t_channel * channellist_find_channel_by_name(char const * name, char const * locale, char const * realmname); extern t_channel * channellist_find_channel_bychannelid(unsigned int channelid); extern int channellist_get_length(void); /** * Westwood Online Extensions */ extern int channel_get_min(t_channel const * channel); extern int channel_set_min(t_channel * channel, int minmembers); extern int channel_wol_get_game_type(t_channel const * channel); extern int channel_wol_set_game_type(t_channel * channel, int gameType); extern char const * channel_wol_get_game_extension(t_channel const * channel); extern int channel_wol_set_game_extension(t_channel * channel, char const * gameExtension); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/channel_conv.cpp000066400000000000000000000026541462640461400212330ustar00rootroot00000000000000/* * Copyright (C) 2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "channel_conv.h" #include "common/bnet_protocol.h" #include "channel.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern unsigned int cflags_to_bncflags(unsigned flags) { unsigned int res; res = 0; if (flags&channel_flags_public) res |= CF_PUBLIC; if (flags&channel_flags_moderated) res |= CF_MODERATED; if (flags&channel_flags_restricted) res |= CF_RESTRICTED; if (flags&channel_flags_thevoid) res |= CF_THEVOID; if (flags&channel_flags_system) res |= CF_SYSTEM; if (flags&channel_flags_official) res |= CF_OFFICIAL; return res; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/channel_conv.h000066400000000000000000000021761462640461400206770ustar00rootroot00000000000000/* * Copyright (C) 2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_CHANNEL_CONV_TYPES #define INCLUDED_CHANNEL_CONV_TYPES #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_CHANNEL_CONV_PROTOS #define INCLUDED_CHANNEL_CONV_PROTOS #define JUST_NEED_TYPES #include "channel.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern unsigned int cflags_to_bncflags(unsigned flags); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/character.cpp000066400000000000000000000373771462640461400205440ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define CHARACTER_INTERNAL_ACCESS #include "common/setup_before.h" #include "character.h" #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/list.h" #include "common/bnet_protocol.h" #include "account.h" #include "account_wrap.h" #include "common/bn_type.h" #include "common/util.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_list * characterlist_head = NULL; static t_character_class bncharacter_class_to_character_class(std::uint8_t cclass) { switch (cclass) { case D2CHAR_INFO_CLASS_AMAZON: return character_class_amazon; case D2CHAR_INFO_CLASS_SORCERESS: return character_class_sorceress; case D2CHAR_INFO_CLASS_NECROMANCER: return character_class_necromancer; case D2CHAR_INFO_CLASS_PALADIN: return character_class_paladin; case D2CHAR_INFO_CLASS_BARBARIAN: return character_class_barbarian; case D2CHAR_INFO_CLASS_DRUID: return character_class_druid; case D2CHAR_INFO_CLASS_ASSASSIN: return character_class_assassin; default: return character_class_none; } } /* Function unused static t_uint8 character_class_to_bncharacter_class(t_character_class class) { switch (class) { case character_class_amazon: return D2CHAR_INFO_CLASS_AMAZON; case character_class_sorceress: return D2CHAR_INFO_CLASS_SORCERESS; case character_class_necromancer: return D2CHAR_INFO_CLASS_NECROMANCER; case character_class_paladin: return D2CHAR_INFO_CLASS_PALADIN; case character_class_barbarian: return D2CHAR_INFO_CLASS_BARBARIAN; case character_class_druid: return D2CHAR_INFO_CLASS_DRUID; case character_class_assassin: return D2CHAR_INFO_CLASS_ASSASSIN; default: eventlog(eventlog_level_error,__FUNCTION__,"got unknown class %d",(int)class); case character_class_none: return D2CHAR_INFO_FILLER; } } */ static const char * character_class_to_classname(t_character_class chclass) { switch (chclass) { case character_class_amazon: return "Amazon"; case character_class_sorceress: return "Sorceress"; case character_class_necromancer: return "Necromancer"; case character_class_paladin: return "Paladin"; case character_class_barbarian: return "Barbarian"; case character_class_druid: return "Druid"; case character_class_assassin: return "Assassin"; default: return "Unknown"; } } static const char * character_expansion_to_expansionname(t_character_expansion expansion) { switch (expansion) { case character_expansion_classic: return "Classic"; case character_expansion_lod: return "LordOfDestruction"; default: return "Unknown"; } } static void decode_character_data(t_character * ch) { ch->unknownb1 = D2CHAR_INFO_UNKNOWNB1; ch->unknownb2 = D2CHAR_INFO_UNKNOWNB2; ch->helmgfx = D2CHAR_INFO_FILLER; ch->bodygfx = D2CHAR_INFO_FILLER; ch->leggfx = D2CHAR_INFO_FILLER; ch->lhandweapon = D2CHAR_INFO_FILLER; ch->lhandgfx = D2CHAR_INFO_FILLER; ch->rhandweapon = D2CHAR_INFO_FILLER; ch->rhandgfx = D2CHAR_INFO_FILLER; ch->unknownb3 = D2CHAR_INFO_FILLER; ch->unknownb4 = D2CHAR_INFO_FILLER; ch->unknownb5 = D2CHAR_INFO_FILLER; ch->unknownb6 = D2CHAR_INFO_FILLER; ch->unknownb7 = D2CHAR_INFO_FILLER; ch->unknownb8 = D2CHAR_INFO_FILLER; ch->unknownb9 = D2CHAR_INFO_FILLER; ch->unknownb10 = D2CHAR_INFO_FILLER; ch->unknownb11 = D2CHAR_INFO_FILLER; ch->unknown1 = 0xffffffff; ch->unknown2 = 0xffffffff; ch->unknown3 = 0xffffffff; ch->unknown4 = 0xffffffff; ch->level = 0x01; ch->status = 0x80; ch->title = 0x80; ch->unknownb13 = 0x80; ch->emblembgc = 0x80; ch->emblemfgc = 0xff; ch->emblemnum = 0xff; ch->unknownb14 = D2CHAR_INFO_FILLER; /* b1 b2 hg bg lg lw lg rw rg b3 b4 b5 b6 b7 b8 b9 bA bB cl u1 u1 u1 u1 u2 u2 u2 u2 u3 u3 u3 u3 u4 u4 u4 u4 lv st ti bC eb ef en bD \0 amazon_qwer.std::log: 83 80 ff ff ff ff ff 43 ff 1b ff ff ff ff ff ff ff ff 01 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 01 80 80 80 80 ff ff ff 00 sor_Bent.std::log: 83 80 ff ff ff ff ff 53 ff ff ff ff ff ff ff ff ff ff 02 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 01 80 80 80 80 ff ff ff 00 necro_Thorsen.std::log: 83 80 ff ff ff ff ff 2b ff ff ff ff ff ff ff ff ff ff 03 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 01 80 80 80 80 ff ff ff 00 pal_QlexTEST.std::log: 87 80 01 01 01 01 01 ff ff ff 01 01 ff ff ff ff ff ff 04 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 01 80 80 ff ff ff 80 80 00 barb_Qlex.std::log: 83 80 ff ff ff ff ff 2f ff 1b ff ff ff ff ff ff ff ff 05 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 01 80 80 80 80 ff ff ff 00 */ } static int load_initial_data(t_character * character, t_character_class chclass, t_character_expansion expansion) { char const * data_in_hex; eventlog(eventlog_level_debug, __FUNCTION__, "Initial Data for {}, {} {}", character->name, character_expansion_to_expansionname(expansion), character_class_to_classname(chclass)); /* Ideally, this would be loaded from bnetd_default_user, but I don't want to hack account.c just now */ /* The "default" character info if everything else messes up; */ data_in_hex = NULL; /* FIXME: what should we do if expansion or class isn't known... */ switch (expansion) { case character_expansion_classic: switch (chclass) { case character_class_amazon: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 01 FF FF FF FF FF FF FF FF FF FF FF 01 81 80 80 80 FF FF FF"; break; case character_class_sorceress: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 02 FF FF FF FF FF FF FF FF FF FF FF 01 81 80 80 80 FF FF FF"; break; case character_class_necromancer: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 03 FF FF FF FF FF FF FF FF FF FF FF 01 81 80 80 80 FF FF FF"; break; case character_class_paladin: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 04 FF FF FF FF FF FF FF FF FF FF FF 01 81 80 80 80 FF FF FF"; break; case character_class_barbarian: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 05 FF FF FF FF FF FF FF FF FF FF FF 01 81 80 80 80 FF FF FF"; break; default: break; //should never reach that part ot the code... but to make compiler happy... } break; case character_expansion_lod: switch (chclass) { case character_class_amazon: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 01 FF FF FF FF FF FF FF FF FF FF FF 01 A1 80 80 80 FF FF FF"; break; case character_class_sorceress: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 02 FF FF FF FF FF FF FF FF FF FF FF 01 A1 80 80 80 FF FF FF"; break; case character_class_necromancer: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 03 FF FF FF FF FF FF FF FF FF FF FF 01 A1 80 80 80 FF FF FF"; break; case character_class_paladin: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 04 FF FF FF FF FF FF FF FF FF FF FF 01 A1 80 80 80 FF FF FF"; break; case character_class_barbarian: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 05 FF FF FF FF FF FF FF FF FF FF FF 01 A1 80 80 80 FF FF FF"; break; case character_class_druid: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 06 FF FF FF FF FF FF FF FF FF FF FF 01 A1 80 80 80 FF FF FF"; break; case character_class_assassin: data_in_hex = "84 80 FF FF FF FF FF FF FF FF FF FF FF 07 FF FF FF FF FF FF FF FF FF FF FF 01 A1 80 80 80 FF FF FF"; break; default: break; // again we will never get here... but how can compiler know that?!? } default: break; // well... like I said 2 times before.... } character->datalen = hex_to_str(data_in_hex, (char*)character->data, 33); decode_character_data(character); return 0; } extern int character_create(t_account * account, t_clienttag clienttag, char const * realmname, char const * name, t_character_class chclass, t_character_expansion expansion) { if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } if (!realmname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realmname"); return -1; } if (!name) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL name"); return -1; } if (account_check_closed_character(account, clienttag, realmname, name)) { eventlog(eventlog_level_error, __FUNCTION__, "a character with the name \"{}\" does already exist in realm \"{}\"", name, realmname); return -1; } t_character* ch = (t_character*)xmalloc(sizeof(t_character)); ch->name = xstrdup(name); ch->realmname = xstrdup(realmname); ch->guildname = xstrdup(""); /* FIXME: how does this work on Battle.net? */ load_initial_data(ch, chclass, expansion); account_add_closed_character(account, clienttag, ch); xfree((void *)ch->name); xfree((void *)ch->realmname); xfree((void *)ch->guildname); xfree(ch); return 0; } extern char const * character_get_name(t_character const * ch) { if (!ch) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return NULL; } return ch->name; } extern char const * character_get_realmname(t_character const * ch) { if (!ch) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return NULL; } return ch->realmname; } extern t_character_class character_get_class(t_character const * ch) { if (!ch) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return character_class_none; } return bncharacter_class_to_character_class(ch->chclass); } extern char const * character_get_playerinfo(t_character const * ch) { t_d2char_info d2char_info; static char playerinfo[sizeof(t_d2char_info)+4]; if (!ch) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return NULL; } /* ff 0f 68 00 ..h. 0x0040: 01 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................ 0x0050: d8 94 f6 08 b1 65 77 02 65 76 69 6c 67 72 75 73 .....ew.evilgrus 0x0060: 73 6c 65 72 00 56 44 32 44 42 65 74 61 57 65 73 sler.VD2DBetaWes 0x0070: 74 2c 74 61 72 61 6e 2c 83 80 ff ff ff ff ff 2f t,taran,......./ 0x0080: ff ff ff ff ff ff ff ff ff ff 03 ff ff ff ff ff ................ 0x0090: ff ff ff ff ff ff ff ff ff ff ff 07 80 80 80 80 ................ 0x00a0: ff ff ff 00 */ bn_byte_set(&d2char_info.unknownb1, ch->unknownb1); bn_byte_set(&d2char_info.unknownb2, ch->unknownb2); bn_byte_set(&d2char_info.helmgfx, ch->helmgfx); bn_byte_set(&d2char_info.bodygfx, ch->bodygfx); bn_byte_set(&d2char_info.leggfx, ch->leggfx); bn_byte_set(&d2char_info.lhandweapon, ch->lhandweapon); bn_byte_set(&d2char_info.lhandgfx, ch->lhandgfx); bn_byte_set(&d2char_info.rhandweapon, ch->rhandweapon); bn_byte_set(&d2char_info.rhandgfx, ch->rhandgfx); bn_byte_set(&d2char_info.unknownb3, ch->unknownb3); bn_byte_set(&d2char_info.unknownb4, ch->unknownb4); bn_byte_set(&d2char_info.unknownb5, ch->unknownb5); bn_byte_set(&d2char_info.unknownb6, ch->unknownb6); bn_byte_set(&d2char_info.unknownb7, ch->unknownb7); bn_byte_set(&d2char_info.unknownb8, ch->unknownb8); bn_byte_set(&d2char_info.unknownb9, ch->unknownb9); bn_byte_set(&d2char_info.unknownb10, ch->unknownb10); bn_byte_set(&d2char_info.unknownb11, ch->unknownb11); bn_byte_set(&d2char_info.chclass, ch->chclass); bn_int_set(&d2char_info.unknown1, ch->unknown1); bn_int_set(&d2char_info.unknown2, ch->unknown2); bn_int_set(&d2char_info.unknown3, ch->unknown3); bn_int_set(&d2char_info.unknown4, ch->unknown4); bn_byte_set(&d2char_info.level, ch->level); bn_byte_set(&d2char_info.status, ch->status); bn_byte_set(&d2char_info.title, ch->title); bn_byte_set(&d2char_info.unknownb13, ch->unknownb13); bn_byte_set(&d2char_info.emblembgc, ch->emblembgc); bn_byte_set(&d2char_info.emblemfgc, ch->emblemfgc); bn_byte_set(&d2char_info.emblemnum, ch->emblemnum); bn_byte_set(&d2char_info.unknownb14, ch->unknownb14); std::memcpy(playerinfo, &d2char_info, sizeof(d2char_info)); std::strcpy(&playerinfo[sizeof(d2char_info)], ch->guildname); return playerinfo; } extern char const * character_get_guildname(t_character const * ch) { if (!ch) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return NULL; } return ch->guildname; } extern int character_verify_charlist(t_character const * ch, char const * charlist) { char * temp; char const * tok1; char const * tok2; if (!ch) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return -1; } if (!charlist) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return -1; } temp = xstrdup(charlist); tok1 = (char const *)std::strtok(temp, ","); /* std::strtok modifies the string it is passed */ tok2 = std::strtok(NULL, ","); while (tok1) { if (!tok2) { eventlog(eventlog_level_error, __FUNCTION__, "bad character list \"{}\"", temp); break; } if (strcasecmp(tok1, ch->realmname) == 0 && strcasecmp(tok2, ch->name) == 0) { xfree(temp); return 0; } tok1 = std::strtok(NULL, ","); tok2 = std::strtok(NULL, ","); } xfree(temp); return -1; } extern int characterlist_create(char const * dirname) { characterlist_head = list_create(); return 0; } extern int characterlist_destroy(void) { t_elem * curr; t_character * ch; if (characterlist_head) { LIST_TRAVERSE(characterlist_head, curr) { ch = (t_character*)elem_get_data(curr); if (!ch) /* should not happen */ { eventlog(eventlog_level_error, __FUNCTION__, "characterlist contains NULL item"); continue; } if (list_remove_elem(characterlist_head, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); xfree(ch); } if (list_destroy(characterlist_head) < 0) return -1; characterlist_head = NULL; } return 0; } extern t_character * characterlist_find_character(char const * realmname, char const * charname) { t_elem * curr; t_character * ch; if (!realmname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realmname"); return NULL; } if (!charname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL charname"); return NULL; } LIST_TRAVERSE(characterlist_head, curr) { ch = (t_character*)elem_get_data(curr); if (strcasecmp(ch->name, charname) == 0 && strcasecmp(ch->realmname, realmname) == 0) return ch; } return NULL; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/character.h000066400000000000000000000073411462640461400201750ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_CHARACTER_TYPES #define INCLUDED_CHARACTER_TYPES #include namespace pvpgn { namespace bnetd { typedef enum { character_class_none, character_class_amazon, character_class_sorceress, character_class_necromancer, character_class_paladin, character_class_barbarian, character_class_druid, character_class_assassin } t_character_class; typedef enum { character_expansion_none, character_expansion_classic, character_expansion_lod } t_character_expansion; typedef struct character #ifdef CHARACTER_INTERNAL_ACCESS { char const * name; /* max 15 chars */ char const * realmname; char const * guildname; /* max 3 chars */ /* stored in Battle.net format for now */ std::uint8_t helmgfx; std::uint8_t bodygfx; std::uint8_t leggfx; std::uint8_t lhandweapon; std::uint8_t lhandgfx; std::uint8_t rhandweapon; std::uint8_t rhandgfx; std::uint8_t chclass; std::uint8_t level; std::uint8_t status; std::uint8_t title; std::uint8_t emblembgc; std::uint8_t emblemfgc; std::uint8_t emblemnum; /* not sure what these represent */ std::uint32_t unknown1; std::uint32_t unknown2; std::uint32_t unknown3; std::uint32_t unknown4; std::uint8_t unknownb1; std::uint8_t unknownb2; std::uint8_t unknownb3; std::uint8_t unknownb4; std::uint8_t unknownb5; std::uint8_t unknownb6; std::uint8_t unknownb7; std::uint8_t unknownb8; std::uint8_t unknownb9; std::uint8_t unknownb10; std::uint8_t unknownb11; std::uint8_t unknownb13; std::uint8_t unknownb14; /* Keep some generic "data", basically the blob that is sent between client and server, in an array */ std::uint8_t data[64]; std::uint8_t datalen; } #endif t_character; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_CHARACTER_PROTOS #define INCLUDED_CHARACTER_PROTOS #define JUST_NEED_TYPES #include "account.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int character_create(t_account * account, t_clienttag clienttag, char const * realmname, char const * name, t_character_class chclass, t_character_expansion expansion); extern char const * character_get_name(t_character const * ch); extern char const * character_get_realmname(t_character const * ch); extern char const * character_get_playerinfo(t_character const * ch); extern char const * character_get_guildname(t_character const * ch); extern t_character_class character_get_class(t_character const * ch); extern int character_verify_charlist(t_character const * ch, char const * charlist); extern int characterlist_create(char const * dirname); extern int characterlist_destroy(void); extern t_character * characterlist_find_character(char const * realmname, char const * charname); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/clan.cpp000066400000000000000000001061051462640461400175070ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define CLAN_INTERNAL_ACCESS #include "common/setup_before.h" #include "clan.h" #include #include "compat/strcasecmp.h" #include "compat/strncasecmp.h" #include "compat/pdir.h" #ifdef HAVE_SYS_TYPES_H # include #endif #include "common/packet.h" #include "common/bnet_protocol.h" #include "common/util.h" #include "common/bnettime.h" #include "common/eventlog.h" #include "common/list.h" #include "common/proginfo.h" #include "common/bn_type.h" #include "common/xalloc.h" #include "connection.h" #include "anongame.h" #include "prefs.h" #include "friends.h" #include "game.h" #include "message.h" #include "account.h" #include "channel.h" #include "anongame.h" #include "storage.h" #include "server.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_list *clanlist_head = NULL; unsigned max_clanid = 0; /* callback function for storage use */ static int _cb_load_clans(void *clan) { if (clanlist_add_clan((t_clan*)clan) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to add clan to clanlist"); return -1; } if (((t_clan *)clan)->clanid > max_clanid) max_clanid = ((t_clan *)clan)->clanid; return 0; } /* ** Message functions */ extern int clan_send_message_to_online_members(t_clan * clan, t_message_type type, t_connection * me, char const * text) { /* PELISH: Send message to online clan_members returns: an error == -1, done but no one heard == 0, done with message sended == 1 */ t_list * cl_member_list; t_elem * curr; t_clanmember * dest_member; t_connection * dest_conn; bool heard = false; if (!clan) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connecion"); return -1; } cl_member_list = clan_get_members(clan); LIST_TRAVERSE(cl_member_list, curr) { if (!(dest_member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if ((dest_conn = clanmember_get_conn(dest_member)) && (dest_conn != me) && (clanmember_get_fullmember(dest_member) == 1)) { message_send_text(dest_conn, type, me, text); heard = true; } } if (heard) return 1; else return 0; } /* ** Packet Management */ extern int clan_send_packet_to_online_members(t_clan * clan, t_packet * packet) { t_elem *curr; if (!clan) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } LIST_TRAVERSE(clan->members, curr) { t_clanmember * member; t_clienttag clienttag; t_connection * conn; if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); continue; } if (!(conn = clanmember_get_conn(member))) continue; // not online if (!(clienttag = conn_get_clienttag(conn))) { eventlog(eventlog_level_error, __FUNCTION__, "conn has NULL clienttag"); continue; } if ((clienttag != CLIENTTAG_WARCRAFT3_UINT) && (clienttag != CLIENTTAG_WAR3XP_UINT)) continue; // online but wrong client conn_push_outqueue(conn, packet); } return 0; } extern int clan_send_status_window_on_create(t_clan * clan) { t_packet * rpacket; t_elem *curr; if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { char channelname[10]; if (clan->tag) std::sprintf(channelname, "Clan %s", clantag_to_str(clan->tag)); else { std::sprintf(channelname, "Clans"); eventlog(eventlog_level_error, __FUNCTION__, "clan has NULL clantag"); } packet_set_size(rpacket, sizeof(t_server_clan_clanack)); packet_set_type(rpacket, SERVER_CLAN_CLANACK); bn_byte_set(&rpacket->u.server_clan_clanack.unknow1, 0); bn_int_set(&rpacket->u.server_clan_clanack.clantag, clan->tag); LIST_TRAVERSE(clan->members, curr) { t_clanmember * member; t_clienttag clienttag; t_connection * conn; if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); continue; } if (!(conn = clanmember_get_conn(member))) continue; // not online; if (!(clienttag = conn_get_clienttag(conn))) { eventlog(eventlog_level_error, __FUNCTION__, "conn has NULL clienttag"); continue; } if ((clienttag != CLIENTTAG_WARCRAFT3_UINT) && (clienttag != CLIENTTAG_WAR3XP_UINT)) continue; // online but wrong client if (conn_get_channel(conn)) { conn_update_w3_playerinfo(conn); channel_set_userflags(conn); if (conn_set_channel(conn, channelname) < 0) conn_set_channel(conn, CHANNEL_NAME_BANNED); /* should not fail */ } bn_byte_set(&rpacket->u.server_clan_clanack.status, member->status); conn_push_outqueue(conn, rpacket); } packet_del_ref(rpacket); } return 0; } extern int clan_close_status_window_on_disband(t_clan * clan) { t_packet * rpacket; t_elem *curr; if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_clanquitnotify)); packet_set_type(rpacket, SERVER_CLANQUITNOTIFY); bn_byte_set(&rpacket->u.server_clan_clanack.status, SERVER_CLANQUITNOTIFY_STATUS_REMOVED_FROM_CLAN); LIST_TRAVERSE(clan->members, curr) { t_clanmember * member; t_clienttag clienttag; t_connection * conn; if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); continue; } if (!(conn = clanmember_get_conn(member))) continue; // not online; if (!(clienttag = conn_get_clienttag(conn))) { eventlog(eventlog_level_error, __FUNCTION__, "conn has NULL clienttag"); continue; } if ((clienttag != CLIENTTAG_WARCRAFT3_UINT) && (clienttag != CLIENTTAG_WAR3XP_UINT)) continue; // online but wrong client conn_push_outqueue(conn, rpacket); conn_update_w3_playerinfo(conn); } packet_del_ref(rpacket); } return 0; } extern int clan_send_status_window(t_connection * c) { t_packet * rpacket; t_account *acc; t_clanmember *member; t_clienttag clienttag; t_clan * clan; if (!(acc = conn_get_account(c))) return 0; if (!(member = account_get_clanmember(acc))) return 0; if (!(clan = member->clan)) { eventlog(eventlog_level_error, __FUNCTION__, "member has NULL clan"); return -1; } if (!(clan->tag)) { eventlog(eventlog_level_error, __FUNCTION__, "clan has NULL clantag"); return -1; } if (!(clienttag = conn_get_clienttag(c))) { eventlog(eventlog_level_error, __FUNCTION__, "conn has NULL clienttag"); return -1; } if ((clienttag != CLIENTTAG_WARCRAFT3_UINT) && (clienttag != CLIENTTAG_WAR3XP_UINT)) return 0; if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_clan_clanack)); packet_set_type(rpacket, SERVER_CLAN_CLANACK); bn_byte_set(&rpacket->u.server_clan_clanack.unknow1, 0); bn_int_set(&rpacket->u.server_clan_clanack.clantag, member->clan->tag); bn_byte_set(&rpacket->u.server_clan_clanack.status, member->status); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } extern int clan_close_status_window(t_connection * c) { t_packet * rpacket; t_clienttag clienttag; if (!(clienttag = conn_get_clienttag(c))) { eventlog(eventlog_level_error, __FUNCTION__, "conn has NULL clienttag"); return -1; } if ((clienttag != CLIENTTAG_WARCRAFT3_UINT) && (clienttag != CLIENTTAG_WAR3XP_UINT)) return 0; if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_clanquitnotify)); packet_set_type(rpacket, SERVER_CLANQUITNOTIFY); bn_byte_set(&rpacket->u.server_clanquitnotify.status, SERVER_CLANQUITNOTIFY_STATUS_REMOVED_FROM_CLAN); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } extern int clan_send_memberlist(t_connection * c, t_packet const *const packet) { t_packet * rpacket; t_elem *curr; char const *username; t_clanmember *member; t_clan *clan; t_account *account; int count = 0; char tmpstr[2]; const char *append_str; if (!(account = conn_get_account(c))) return -1; if (!(clan = account_get_clan(account))) return -1; if ((rpacket = packet_create(packet_class_bnet))) { t_account * memberacc; packet_set_size(rpacket, sizeof(t_server_clanmemberlist_reply)); packet_set_type(rpacket, SERVER_CLANMEMBERLIST_REPLY); bn_int_set(&rpacket->u.server_clanmemberlist_reply.count, bn_int_get(packet->u.client_clanmemberlist_req.count)); LIST_TRAVERSE(clan->members, curr) { if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL element in list"); continue; } if (!(memberacc = (t_account*)member->memberacc)) { eventlog(eventlog_level_error, __FUNCTION__, "member has NULL account"); continue; } username = account_get_name(memberacc); packet_append_string(rpacket, username); tmpstr[0] = member->status; append_str = clanmember_get_online_status(member, &tmpstr[1]); packet_append_data(rpacket, tmpstr, 2); if (append_str) packet_append_string(rpacket, append_str); else packet_append_string(rpacket, ""); count++; } bn_byte_set(&rpacket->u.server_clanmemberlist_reply.member_count, count); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } return -1; } extern int clan_save_motd_chg(t_connection * c, t_packet const *const packet) { t_account *account; char const *motd; int offset; t_clan *clan; if ((account = conn_get_account(c)) == NULL) return -1; if ((clan = account_get_clan(account)) == NULL) return -1; offset = sizeof(packet->u.client_clan_motdchg); motd = packet_get_str_const(packet, offset, CLAN_MOTD_MAX); eventlog(eventlog_level_trace, __FUNCTION__, "[{}] got W3XP_CLAN_MOTDCHG packet : {}", conn_get_socket(c), motd); if (clan_set_motd(clan, motd) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "Failed to set clan motd."); return -1; } clan->modified = 1; return 0; } extern int clan_send_motd_reply(t_connection * c, t_packet const *const packet) { t_packet * rpacket; t_account *account; t_clan *clan; if ((account = conn_get_account(c)) == NULL) return -1; if ((clan = account_get_clan(account)) == NULL) return -1; if (clan->clan_motd == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "Failed to get clan motd."); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_clan_motdreply)); packet_set_type(rpacket, SERVER_CLAN_MOTDREPLY); bn_int_set(&rpacket->u.server_clan_motdreply.count, bn_int_get(packet->u.client_clan_motdreq.count)); bn_int_set(&rpacket->u.server_clan_motdreply.unknow1, SERVER_CLAN_MOTDREPLY_UNKNOW1); packet_append_string(rpacket, clan->clan_motd); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } /* ** String / Function Management */ extern int clan_get_possible_member(t_connection * c, t_packet const *const packet) { t_packet * rpacket; t_channel *channel; t_connection *conn; char const *username; t_account * account; int friend_count = 0; t_clantag clantag; clantag = bn_int_get(packet->u.client_clan_createreq.clantag); if ((rpacket = packet_create(packet_class_bnet)) == NULL) { return -1; } packet_set_size(rpacket, sizeof(t_server_clan_createreply)); packet_set_type(rpacket, SERVER_CLAN_CREATEREPLY); bn_int_set(&rpacket->u.server_clan_createreply.count, bn_int_get(packet->u.client_clan_createreq.count)); if (clanlist_find_clan_by_clantag(clantag) != NULL) { bn_byte_set(&rpacket->u.server_clan_createreply.check_result, SERVER_CLAN_CREATEREPLY_CHECK_ALLREADY_IN_USE); bn_byte_set(&rpacket->u.server_clan_createreply.friend_count, 0); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } if ((account = conn_get_account(c)) && (account_get_clan(account) != NULL || account_get_creating_clan(account) != NULL)) { bn_byte_set(&rpacket->u.server_clan_createreply.check_result, SERVER_CLAN_CREATEREPLY_CHECK_EXCEPTION); bn_byte_set(&rpacket->u.server_clan_createreply.friend_count, 0); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } bn_byte_set(&rpacket->u.server_clan_createreply.check_result, SERVER_CLAN_CREATEREPLY_CHECK_OK); channel = conn_get_channel(c); if (channel_get_permanent(channel)) { /* If not in a private channel, retreive number of mutual friend connected */ t_list *flist = account_get_friends(conn_get_account(c)); t_elem const *curr; t_friend *fr; LIST_TRAVERSE_CONST(flist, curr) { if ((fr = (t_friend*)elem_get_data(curr)) != NULL) { t_account *fr_acc = friend_get_account(fr); t_clienttag clienttag; if (fr->mutual && ((conn = connlist_find_connection_by_account(fr_acc)) != NULL) && (conn_get_channel(conn) == channel) && (!account_get_clan(fr_acc)) && (!account_get_creating_clan(fr_acc)) && (clienttag = conn_get_clienttag(conn)) && ((clienttag == CLIENTTAG_WAR3XP_UINT) || (clienttag == CLIENTTAG_WARCRAFT3_UINT)) && (username = account_get_name(fr_acc))) { friend_count++; packet_append_string(rpacket, username); } } } } else { /* If in a private channel, retreive all non-clan war3/w3xp users in the channel */ for (conn = channel_get_first(channel); conn; conn = channel_get_next()) { t_account * acc; t_clienttag clienttag; if ((conn != c) && (acc = conn_get_account(conn)) && (!account_get_clan(acc)) && (!account_get_creating_clan(acc)) && (clienttag = conn_get_clienttag(conn)) && ((clienttag == CLIENTTAG_WAR3XP_UINT) || (clienttag == CLIENTTAG_WARCRAFT3_UINT)) && (username = conn_get_username(conn))) { friend_count++; packet_append_string(rpacket, username); } } } bn_byte_set(&rpacket->u.server_clan_createreply.friend_count, friend_count); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } extern int clanmember_on_change_status(t_clanmember * member) { t_packet * rpacket; if (member == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return -1; } if (member->clan == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } if ((rpacket = packet_create(packet_class_bnet)) != NULL) { char tmpstr[2]; const char *append_str; packet_set_size(rpacket, sizeof(t_server_clanmemberupdate)); packet_set_type(rpacket, SERVER_CLANMEMBERUPDATE); packet_append_string(rpacket, account_get_name((t_account*)member->memberacc)); tmpstr[0] = member->status; append_str = clanmember_get_online_status(member, &tmpstr[1]); packet_append_data(rpacket, tmpstr, 2); if (append_str) packet_append_string(rpacket, append_str); else packet_append_string(rpacket, ""); clan_send_packet_to_online_members(member->clan, rpacket); packet_del_ref(rpacket); } return 0; } extern int clanmember_on_change_status_by_connection(t_connection * conn) { t_packet * rpacket; t_account *acc; t_clanmember *member; if (!(conn)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } if ((acc = conn_get_account(conn)) == NULL) return -1; if ((member = account_get_clanmember(acc)) == NULL) return -1; if (member->clan == NULL) return -1; if ((rpacket = packet_create(packet_class_bnet)) != NULL) { char tmpstr[2]; const char *append_str; packet_set_size(rpacket, sizeof(t_server_clanmemberupdate)); packet_set_type(rpacket, SERVER_CLANMEMBERUPDATE); packet_append_string(rpacket, account_get_name(acc)); tmpstr[0] = member->status; append_str = clanmember_get_online_status_by_connection(conn, &tmpstr[1]); packet_append_data(rpacket, tmpstr, 2); if (append_str) packet_append_string(rpacket, append_str); else packet_append_string(rpacket, ""); clan_send_packet_to_online_members(member->clan, rpacket); packet_del_ref(rpacket); } return 0; } extern int clan_unload_members(t_clan * clan) { t_elem *curr; t_clanmember *member; if (clan->members) { LIST_TRAVERSE(clan->members, curr) { if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } list_remove_elem(clan->members, &curr); xfree((void *)member); } if (list_destroy(clan->members) < 0) return -1; clan->members = NULL; } return 0; } extern int clan_remove_all_members(t_clan * clan) { t_elem *curr; t_clanmember *member; if (clan->members) { LIST_TRAVERSE(clan->members, curr) { if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (member->memberacc != NULL) account_set_clanmember((t_account*)member->memberacc, NULL); list_remove_elem(clan->members, &curr); xfree((void *)member); } if (list_destroy(clan->members) < 0) return -1; clan->members = NULL; } return 0; } extern int clanlist_remove_clan(t_clan * clan) { t_elem * elem; if (clan == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL clan"); return -1; } if (list_remove_data(clanlist_head, clan, &elem) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not delete clan entry"); return -1; } return 0; } extern int clan_remove(t_clantag clantag) { return storage->remove_clan(clantag); } extern int clan_save(t_clan * clan) { if (clan->created <= 0) { if (now - clan->creation_time > 120) { clanlist_remove_clan(clan); clan_destroy(clan); } return 0; } storage->write_clan(clan); clan->modified = 0; return 0; } extern t_list *clanlist(void) { return clanlist_head; } extern int clanlist_add_clan(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } if (!(clan->clanid)) clan->clanid = ++max_clanid; list_append_data(clanlist_head, clan); return clan->clanid; } int clanlist_load(void) { // make sure to unload previous clanlist before loading again if (clanlist_head) clanlist_unload(); clanlist_head = list_create(); storage->load_clans(_cb_load_clans); return 0; } extern int clanlist_save(void) { t_elem *curr; t_clan *clan; if (clanlist_head) { LIST_TRAVERSE(clanlist_head, curr) { if (!(clan = (t_clan*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (clan->modified) clan_save(clan); } } return 0; } extern int clanlist_unload(void) { t_elem *curr; t_clan *clan; if (clanlist_head) { LIST_TRAVERSE(clanlist_head, curr) { if (!(clan = (t_clan*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (clan->clanname) xfree((void *)clan->clanname); if (clan->clan_motd) xfree((void *)clan->clan_motd); clan_unload_members(clan); xfree((void *)clan); list_remove_elem(clanlist_head, &curr); } if (list_destroy(clanlist_head) < 0) return -1; clanlist_head = NULL; } return 0; } extern t_clan *clanlist_find_clan_by_clanid(unsigned cid) { t_elem *curr; t_clan *clan; if (clanlist_head) { LIST_TRAVERSE(clanlist_head, curr) { if (!(clan = (t_clan*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } eventlog(eventlog_level_trace, __FUNCTION__, "trace {}", clan->clanid); if (clan->created && (clan->clanid == cid)) return clan; } } return NULL; } extern t_clan *clanlist_find_clan_by_clantag(t_clantag clantag) { t_elem *curr; t_clan *clan; char * needle; if (clantag == 0) return NULL; needle = xstrdup(clantag_to_str(clantag)); if (clanlist_head) { LIST_TRAVERSE(clanlist_head, curr) { if (!(clan = (t_clan*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (clan->created && !strcasecmp(needle, clantag_to_str(clan->tag))) { xfree(needle); return clan; } } } xfree(needle); return NULL; } extern t_clanmember *clan_find_member(t_clan * clan, t_account * memberacc) { t_clanmember *member; t_elem *curr; if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return NULL; } if (!(clan->members)) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL clan->members"); return NULL; } LIST_TRAVERSE(clan->members, curr) { if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL element in list"); return NULL; } if (member->memberacc == memberacc) return member; } return NULL; } extern t_clanmember *clan_find_member_by_name(t_clan * clan, char const *membername) { t_clanmember *member; t_elem *curr; if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return NULL; } if (!(clan->members)) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL clan->members"); return NULL; } LIST_TRAVERSE(clan->members, curr) { if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL element in list"); return NULL; } if (strcasecmp(account_get_name((t_account*)member->memberacc), membername) == 0) return member; } return NULL; } extern t_clanmember *clan_find_member_by_uid(t_clan * clan, unsigned int memberuid) { t_clanmember *member; t_elem *curr; if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return NULL; } if (!(clan->members)) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL clan->members"); return NULL; } LIST_TRAVERSE(clan->members, curr) { if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL element in list"); return NULL; } if (account_get_uid((t_account*)member->memberacc) == memberuid) return member; } return NULL; } extern t_account *clanmember_get_account(t_clanmember * member) { if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return NULL; } return (t_account *)member->memberacc; } extern int clanmember_set_account(t_clanmember * member, t_account * memberacc) { if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return -1; } member->memberacc = memberacc; return 0; } extern t_connection *clanmember_get_conn(t_clanmember * member) { t_account * account; if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return NULL; } if (!(account = (t_account*)member->memberacc)) { eventlog(eventlog_level_error, __FUNCTION__, "member as NULL account"); return NULL; } return account_get_conn(account); } extern char clanmember_get_status(t_clanmember * member) { if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return 0; } if ((member->status == CLAN_NEW) && (now - member->join_time > prefs_get_clan_newer_time() * 3600)) { member->status = CLAN_PEON; member->clan->modified = 1; #ifdef WITH_SQL member->modified = 1; #endif } return member->status; } extern int clanmember_set_status(t_clanmember * member, char status) { if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return -1; } if (member->status != status) { member->status = status; member->clan->modified = 1; #ifdef WITH_SQL member->modified = 1; #endif } return 0; } extern int clanmember_set_join_time(t_clanmember * member, std::time_t join_time) { if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return 0; } member->join_time = join_time; return 0; } extern std::time_t clanmember_get_join_time(t_clanmember * member) { if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return 0; } return member->join_time; } extern t_clan *clanmember_get_clan(t_clanmember * member) { if (!(member)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanmember"); return 0; } return member->clan; } extern int clanmember_get_fullmember(t_clanmember * member) { if (!(member)) { ERROR0("got NULL clanmember"); return -1; } return member->fullmember; } extern int clanmember_set_fullmember(t_clanmember * member, int fullmember) { if (!(member)) { ERROR0("got NULL clanmember"); return -1; } if (!(fullmember)) { ERROR0("got NULL fullmember"); return -1; } member->fullmember = fullmember; return 0; } extern const char *clanmember_get_online_status(t_clanmember * member, char *status) { return clanmember_get_online_status_by_connection(clanmember_get_conn(member), status); } extern const char *clanmember_get_online_status_by_connection(t_connection * conn, char *status) { if (conn && (conn_get_state(conn) != conn_state_empty)) { t_game *game; t_channel *channel; if ((game = conn_get_game(conn)) != NULL) { if (game_get_flag(game) == game_flag_private) (*status) = SERVER_CLAN_MEMBER_PRIVATE_GAME; else (*status) = SERVER_CLAN_MEMBER_GAME; return game_get_name(game); } if ((channel = conn_get_channel(conn)) != NULL) { (*status) = SERVER_CLAN_MEMBER_CHANNEL; return channel_get_name(channel); } (*status) = SERVER_CLAN_MEMBER_ONLINE; } else (*status) = SERVER_CLAN_MEMBER_OFFLINE; return NULL; } extern int clanmember_set_online(t_connection * c) { t_clanmember *member; t_account *acc; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if ((acc = conn_get_account(c)) && (member = account_get_clanmember(acc))) { clanmember_on_change_status(member); } return 0; } extern int clanmember_set_offline(t_connection * c) { t_clanmember *member; t_account *acc; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if ((acc = conn_get_account(c)) && (member = account_get_clanmember_forced(acc))) { clanmember_on_change_status(member); } return 0; } extern int clan_get_created(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } return clan->created; } extern int clan_set_created(t_clan * clan, int created) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } clan->created = created; return 0; } extern char clan_get_modified(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } return clan->modified; } extern int clan_set_modified(t_clan * clan, char modified) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } clan->modified = modified; return 0; } extern char clan_get_channel_type(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } return clan->channel_type; } extern int clan_set_channel_type(t_clan * clan, char channel_type) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } clan->channel_type = channel_type; return 0; } extern t_list *clan_get_members(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return NULL; } return clan->members; } extern char const *clan_get_name(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return NULL; } return clan->clanname; } extern t_clantag clan_get_clantag(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return 0; } return clan->tag; } extern char const *clan_get_motd(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return NULL; } return clan->clan_motd; } extern int clan_set_motd(t_clan * clan, const char *motd) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return -1; } if (!(motd)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL motd"); return -1; } else { if (clan->clan_motd) xfree((void *)clan->clan_motd); clan->clan_motd = xstrdup(motd); } return 0; } extern unsigned int clan_get_clanid(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return 0; } return clan->clanid; } extern int clan_set_creation_time(t_clan * clan, std::time_t c_time) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return 0; } clan->creation_time = c_time; return 0; } extern std::time_t clan_get_creation_time(t_clan * clan) { if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return 0; } return clan->creation_time; } extern t_clanmember *clan_add_member(t_clan * clan, t_account * memberacc, char status) { t_clanmember *member; if (!(clan)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clan"); return NULL; } if (!(clan->members)) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL clan->members"); return NULL; } member = (t_clanmember*)xmalloc(sizeof(t_clanmember)); member->memberacc = memberacc; member->status = status; member->join_time = now; member->clan = clan; member->fullmember = 0; /* clanmember is invited */ #ifdef WITH_SQL member->modified = 1; #endif list_append_data(clan->members, member); account_set_clanmember(memberacc, member); clan->modified = 1; return member; } extern int clan_remove_member(t_clan * clan, t_clanmember * member) { t_elem * elem; if (!member) return -1; if (list_remove_data(clan->members, member, &elem) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove member"); return -1; } if (member->memberacc != NULL) { account_set_clanmember((t_account*)member->memberacc, NULL); if (member->fullmember == 1) storage->remove_clanmember(account_get_uid((t_account*)member->memberacc)); } xfree((void *)member); clan->modified = 1; return 0; } extern t_clan *clan_create(t_account * chieftain_acc, t_clantag clantag, const char *clanname, const char *motd) { t_clan *clan; t_clanmember *member; clan = (t_clan*)xmalloc(sizeof(t_clan)); member = (t_clanmember*)xmalloc(sizeof(t_clanmember)); if (!(clanname)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clanname"); xfree((void *)clan); xfree((void *)member); return NULL; } clan->clanname = xstrdup(clanname); if (!(motd)) clan->clan_motd = xstrdup("This is a newly created clan"); else clan->clan_motd = xstrdup(motd); clan->creation_time = now; clan->tag = clantag; clan->clanid = ++max_clanid; clan->created = 0; clan->modified = 1; clan->channel_type = prefs_get_clan_channel_default_private(); clan->members = list_create(); member->memberacc = chieftain_acc; member->status = CLAN_CHIEFTAIN; member->join_time = clan->creation_time; member->fullmember = 1; /* chief should be considered invited */ member->clan = clan; #ifdef WITH_SQL member->modified = 1; #endif list_append_data(clan->members, member); account_set_clanmember(chieftain_acc, member); return clan; } extern int clan_destroy(t_clan * clan) { if (!clan) return 0; if (clan->clanname) xfree((void *)clan->clanname); if (clan->clan_motd) xfree((void *)clan->clan_motd); clan_remove_all_members(clan); xfree((void *)clan); return 0; } extern unsigned clan_get_member_count(t_clan * clan) { t_elem *curr; unsigned count = 0; LIST_TRAVERSE(clan->members, curr) { if ((elem_get_data(curr)) != NULL) count++; } return count; } extern t_clantag str_to_clantag(const char *str) { t_clantag tag = 0; if (str[0]) { tag |= str[0] << 24; if (str[1]) { tag |= str[1] << 16; if (str[2]) { tag |= str[2] << 8; if (str[3]) tag |= str[3]; } } } return tag; } extern const char * clantag_to_str(t_clantag tag) { static char tagstr[sizeof(tag)+1]; std::sprintf(tagstr, "%c%c%c%c", tag >> 24, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff); return tagstr; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/clan.h000066400000000000000000000151561462640461400171610ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_CLAN_TYPES #define INCLUDED_CLAN_TYPES #include "common/tag.h" #ifdef JUST_NEED_TYPES # include "common/bn_type.h" # include "connection.h" # include "message.h" # include "common/packet.h" #else # define JUST_NEED_TYPES # include "common/bn_type.h" # include "connection.h" # include "message.h" # include "common/packet.h" # include "common/tag.h" # undef JUST_NEED_TYPES #endif #ifdef CLAN_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES # include "common/list.h" # include "account.h" #else # define JUST_NEED_TYPES # include "common/list.h" # include "account.h" # undef JUST_NEED_TYPES #endif #endif namespace pvpgn { namespace bnetd { typedef t_tag t_clantag; typedef struct clan #ifdef CLAN_INTERNAL_ACCESS { unsigned int clanid; t_clantag tag; char const *clanname; std::time_t creation_time; char const *clan_motd; t_list *members; int created; /* --by Soar on create, set it to -count of invited members, each accept packet will inc it by 1, when it is increased to 0, means that all invited members have accepted, then clan will be created and set this value to 1 */ char modified; char channel_type; /* 0--public 1--private */ } #endif t_clan; typedef struct _clanmember #ifdef CLAN_INTERNAL_ACCESS { t_account * memberacc; char status; std::time_t join_time; t_clan * clan; int fullmember; /* PELISH: 0 -- clanmember is only invited, 1 -- clanmember is fullmember */ #ifdef WITH_SQL char modified; #endif } #endif t_clanmember; } } #define CLAN_CHIEFTAIN 0x04 #define CLAN_SHAMAN 0x03 #define CLAN_GRUNT 0x02 #define CLAN_PEON 0x01 #define CLAN_NEW 0x00 #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_CLAN_PROTOS #define INCLUDED_CLAN_PROTOS #define JUST_NEED_TYPES #include "common/list.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern t_list *clanlist(void); extern int clanlist_load(void); extern int clanlist_save(void); extern int clanlist_unload(void); extern int clanlist_remove_clan(t_clan * clan); extern int clanlist_add_clan(t_clan * clan); extern t_clan *clanlist_find_clan_by_clanid(unsigned cid); extern t_clan *clanlist_find_clan_by_clantag(t_clantag clantag); extern t_account *clanmember_get_account(t_clanmember * member); extern int clanmember_set_account(t_clanmember * member, t_account * memberacc); extern t_connection *clanmember_get_conn(t_clanmember * member); extern char clanmember_get_status(t_clanmember * member); extern int clanmember_set_status(t_clanmember * member, char status); extern int clanmember_set_join_time(t_clanmember * member, std::time_t join_time); extern std::time_t clanmember_get_join_time(t_clanmember * member); extern t_clan * clanmember_get_clan(t_clanmember * member); extern int clanmember_set_online(t_connection * c); extern int clanmember_set_offline(t_connection * c); extern int clanmember_get_fullmember(t_clanmember * member); extern int clanmember_set_fullmember(t_clanmember * member, int fullmember); extern const char *clanmember_get_online_status(t_clanmember * member, char *status); extern int clanmember_on_change_status(t_clanmember * member); extern const char *clanmember_get_online_status_by_connection(t_connection * conn, char *status); extern int clanmember_on_change_status_by_connection(t_connection * conn); extern t_clan *clan_create(t_account * chieftain_acc, t_clantag clantag, const char *clanname, const char *motd); extern int clan_destroy(t_clan * clan); extern int clan_unload_members(t_clan * clan); extern int clan_remove_all_members(t_clan * clan); extern int clan_save(t_clan * clan); extern int clan_remove(t_clantag clantag); extern int clan_get_created(t_clan * clan); extern int clan_set_created(t_clan * clan, int created); extern char clan_get_modified(t_clan * clan); extern int clan_set_modified(t_clan * clan, char modified); extern char clan_get_channel_type(t_clan * clan); extern int clan_set_channel_type(t_clan * clan, char channel_type); extern t_list *clan_get_members(t_clan * clan); extern char const *clan_get_name(t_clan * clan); extern t_clantag clan_get_clantag(t_clan * clan); extern char const *clan_get_motd(t_clan * clan); extern int clan_set_motd(t_clan * clan, const char *motd); extern unsigned int clan_get_clanid(t_clan * clan); extern int clan_set_creation_time(t_clan * clan, std::time_t c_time); extern std::time_t clan_get_creation_time(t_clan * clan); extern unsigned clan_get_member_count(t_clan * clan); extern t_clanmember *clan_add_member(t_clan * clan, t_account * memberacc, char status); extern int clan_remove_member(t_clan * clan, t_clanmember * member); extern t_clanmember *clan_find_member(t_clan * clan, t_account * memberacc); extern t_clanmember *clan_find_member_by_name(t_clan * clan, char const *membername); extern t_clanmember *clan_find_member_by_uid(t_clan * clan, unsigned int memberuid); extern int clan_send_message_to_online_members(t_clan * clan, t_message_type type, t_connection * me, char const * text); extern int clan_send_packet_to_online_members(t_clan * clan, t_packet * packet); extern int clan_get_possible_member(t_connection * c, t_packet const *const packet); extern int clan_send_status_window(t_connection * c); extern int clan_send_status_window_on_create(t_clan * clan); extern int clan_close_status_window(t_connection * c); extern int clan_close_status_window_on_disband(t_clan * clan); extern int clan_send_memberlist(t_connection * c, t_packet const *const packet); extern int clan_change_member_status(t_connection * c, t_packet const *const packet); extern int clan_send_motd_reply(t_connection * c, t_packet const *const packet); extern int clan_save_motd_chg(t_connection * c, t_packet const *const packet); extern t_clantag str_to_clantag(const char *str); extern const char* clantag_to_str(t_clantag tag); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/cmdline.cpp000066400000000000000000000206241462640461400202060ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "cmdline.h" #include #include "common/xalloc.h" #ifdef WIN32 # include "win32/service.h" #endif #ifdef WIN32_GUI # include "common/gui_printf.h" #endif #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/conf.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static struct { #ifdef DO_DAEMONIZE unsigned foreground; #endif const char *preffile; const char *hexfile; unsigned debug; #ifdef WIN32_GUI unsigned console; unsigned gui; #endif } cmdline_config; static unsigned exitflag; static const char *progname; #ifdef DO_DAEMONIZE static int conf_set_foreground(const char *valstr); static int conf_setdef_foreground(void); #endif static int conf_set_preffile(const char *valstr); static int conf_setdef_preffile(void); static int conf_set_hexfile(const char *valstr); static int conf_setdef_hexfile(void); static int conf_set_debug(const char *valstr); static int conf_setdef_debug(void); static int conf_set_help(const char *valstr); static int conf_setdef_help(void); static int conf_set_version(const char *valstr); static int conf_setdef_version(void); #ifdef WIN32 static int conf_set_service(const char *valstr); static int conf_setdef_service(void); static int conf_set_servaction(const char *valstr); static int conf_setdef_servaction(void); #endif #ifdef WIN32_GUI static int conf_set_console(char const * valstr); static int conf_setdef_console(void); static int conf_set_gui(char const * valstr); static int conf_setdef_gui(void); #endif static t_conf_entry conftab[] = { { "c", conf_set_preffile, NULL, conf_setdef_preffile }, { "config", conf_set_preffile, NULL, conf_setdef_preffile }, { "d", conf_set_hexfile, NULL, conf_setdef_hexfile }, { "hexdump", conf_set_hexfile, NULL, conf_setdef_hexfile }, #ifdef DO_DAEMONIZE { "f", conf_set_foreground, NULL, conf_setdef_foreground }, { "foreground", conf_set_foreground, NULL, conf_setdef_foreground }, #endif { "D", conf_set_debug, NULL, conf_setdef_debug }, { "debug", conf_set_debug, NULL, conf_setdef_debug }, { "h", conf_set_help, NULL, conf_setdef_help }, { "help", conf_set_help, NULL, conf_setdef_help }, { "usage", conf_set_help, NULL, conf_setdef_help }, { "v", conf_set_version, NULL, conf_setdef_version }, { "version", conf_set_version, NULL, conf_setdef_version }, #ifdef WIN32 { "service", conf_set_service, NULL, conf_setdef_service }, { "s", conf_set_servaction, NULL, conf_setdef_servaction }, #endif #ifdef WIN32_GUI { "console", conf_set_console, NULL, conf_setdef_console }, { "gui", conf_set_gui, NULL, conf_setdef_gui }, #endif { NULL, NULL, NULL, NULL } }; static void usage(void) { std::fprintf(stderr, "usage: %s []\n" " -c FILE, --config=FILE use FILE as configuration file (default is " BNETD_DEFAULT_CONF_FILE ")\n" " -d FILE, --hexdump=FILE do hex dump of packets into FILE\n" #ifdef DO_DAEMONIZE " -f, --foreground don't daemonize\n" #endif " -D, --debug run in debug mode (run in foreground and log to stdout)\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n" #ifdef WIN32 " Running as service functions:\n" " --service run as service\n" " -s install install service\n" " -s uninstall uninstall service\n" #endif #ifdef WIN32GUI " -console run in console mode\n" #endif , progname); } extern int cmdline_load(int argc, char** argv) { int res; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return -1; } exitflag = 0; progname = argv[0]; res = conf_load_cmdline(argc, argv, conftab); if (res < 0) return -1; return exitflag ? 0 : 1; } extern void cmdline_unload(void) { conf_unload(conftab); } #ifdef DO_DAEMONIZE extern int cmdline_get_foreground(void) { return cmdline_config.foreground; } static int conf_set_foreground(const char *valstr) { return conf_set_bool(&cmdline_config.foreground, valstr, 0); } static int conf_setdef_foreground(void) { return conf_set_bool(&cmdline_config.foreground, NULL, 0); } #endif extern const char* cmdline_get_preffile(void) { return cmdline_config.preffile; } static int conf_set_preffile(const char *valstr) { return conf_set_str(&cmdline_config.preffile, valstr, NULL); } static int conf_setdef_preffile(void) { return conf_set_str(&cmdline_config.preffile, NULL, BNETD_DEFAULT_CONF_FILE); } extern const char* cmdline_get_hexfile(void) { return cmdline_config.hexfile; } static int conf_set_hexfile(const char *valstr) { return conf_set_str(&cmdline_config.hexfile, valstr, NULL); } static int conf_setdef_hexfile(void) { return conf_set_str(&cmdline_config.hexfile, NULL, NULL); } static int conf_set_debug(const char *valstr) { conf_set_bool(&cmdline_config.debug, valstr, 0); if (cmdline_config.debug) eventlog_set_debugmode(1); #ifdef DO_DAEMONIZE cmdline_config.foreground = 1; #endif return 0; } static int conf_setdef_debug(void) { return conf_set_bool(&cmdline_config.debug, NULL, 0); } static int conf_set_help(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { usage(); exitflag = 1; } return 0; } static int conf_setdef_help(void) { return 0; } static int conf_set_version(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { #ifdef WIN32_GUI gui_lvprintf(eventlog_level_info, PVPGN_SOFTWARE" version " PVPGN_VERSION "\n"); #else std::printf(PVPGN_SOFTWARE" version " PVPGN_VERSION "\n"); #endif exitflag = 1; } return 0; } static int conf_setdef_version(void) { return 0; } #ifdef WIN32 static int conf_set_service(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { Win32_ServiceRun(); exitflag = 1; } return 0; } static int conf_setdef_service(void) { return 0; } static int conf_set_servaction(const char *valstr) { const char* tmp = NULL; conf_set_str(&tmp, valstr, NULL); if (tmp) { if (!strcasecmp(tmp, "install")) { std::fprintf(stderr, "Installing service"); Win32_ServiceInstall(); } else if (!strcasecmp(tmp, "uninstall")) { std::fprintf(stderr, "Uninstalling service"); Win32_ServiceUninstall(); } else { std::fprintf(stderr, "Unknown service action '%s'\n", tmp); } exitflag = 1; xfree((void *)tmp); } return 0; } static int conf_setdef_servaction(void) { return 0; } #endif #ifdef WIN32_GUI static int conf_set_console(const char *valstr) { conf_set_bool(&cmdline_config.console, valstr, 0); if (cmdline_config.console){ conf_set_bool(&cmdline_config.gui, NULL, 0); } return 0; } static int conf_setdef_console(void) { return conf_set_bool(&cmdline_config.console, NULL, 0); } extern unsigned cmdline_get_console(void){ return cmdline_config.console; } static int conf_set_gui(const char *valstr) { conf_set_bool(&cmdline_config.gui, valstr, 0); if (cmdline_config.gui){ } return 0; } static int conf_setdef_gui(void) { return conf_set_bool(&cmdline_config.gui, NULL, 1); } extern unsigned cmdline_get_gui(void){ return cmdline_config.gui; } #endif } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/cmdline.h000066400000000000000000000024221462640461400176470ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __CMDLINE_H_PROTOS__ #define __CMDLINE_H_PROTOS__ namespace pvpgn { namespace bnetd { extern int cmdline_load(int argc, char * * argv); extern void cmdline_unload(void); /* options exported by cmdline */ #ifdef DO_DAEMONIZE extern int cmdline_get_foreground(void); #endif extern const char* cmdline_get_preffile(void); extern const char* cmdline_get_hexfile(void); #ifdef WIN32_GUI extern unsigned cmdline_get_console(void); extern unsigned cmdline_get_gui(void); #endif } } #endif /* __CMDLINE_H_PROTOS__ */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/command.cpp000066400000000000000000005376151462640461400202260ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Gediminas (gediminas_lt@mailexcite.com) * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2000 Dizzy * Copyright (C) 2000 Onlyer (onlyer@263.net) * Copyright (C) 2003,2004 Aaron * Copyright (C) 2004 Donny Redmond (dredmond@linuxmail.org) * Copyright (C) 2008 Pelish (pelish@gmail.com) * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "command.h" #include #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/tag.h" #include "common/util.h" #include "common/version.h" #include "common/eventlog.h" #include "common/bnettime.h" #include "common/addr.h" #include "common/packet.h" #include "common/bnethash.h" #include "common/list.h" #include "common/proginfo.h" #include "common/queue.h" #include "common/bn_type.h" #include "common/xalloc.h" #include "common/xstr.h" #include "common/trans.h" #include "common/lstr.h" #include "common/hashtable.h" #include "common/xstring.h" #include "connection.h" #include "message.h" #include "channel.h" #include "game.h" #include "team.h" #include "account.h" #include "account_wrap.h" #include "server.h" #include "prefs.h" #include "ladder.h" #include "timer.h" #include "helpfile.h" #include "mail.h" #include "runprog.h" #include "alias_command.h" #include "realm.h" #include "ipban.h" #include "command_groups.h" #include "news.h" #include "topic.h" #include "friends.h" #include "clan.h" #include "common/flags.h" #include "icons.h" #include "userlog.h" #include "i18n.h" #include "attrlayer.h" #ifdef WITH_LUA #include "luainterface.h" #endif #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static char const * bnclass_get_str(unsigned int cclass); static void do_whisper(t_connection * user_c, char const * dest, char const * text); static void do_whois(t_connection * c, char const * dest); static void user_timer_cb(t_connection * c, std::time_t now, t_timer_data str); std::string msgtemp, msgtemp2; char msgtemp0[MAX_MESSAGE_LEN]; static char const * bnclass_get_str(unsigned int cclass) { switch (cclass) { case PLAYERINFO_DRTL_CLASS_WARRIOR: return "warrior"; case PLAYERINFO_DRTL_CLASS_ROGUE: return "rogue"; case PLAYERINFO_DRTL_CLASS_SORCERER: return "sorcerer"; default: return "unknown"; } } /* * Split text by spaces and return array of arguments. * First text argument is a command name (index = 0) * Last text argument always reads to end */ extern std::vector split_command(char const * text, int args_count) { std::vector result(args_count + 1); std::string s(text); // remove slash from the command if (!s.empty()) s.erase(0, 1); std::istringstream iss(s); int i = 0; std::string tmp = std::string(); // to end do { std::string sub; iss >> sub; if (sub.empty()) continue; if (i < args_count) { result[i] = sub; i++; } else { if (!tmp.empty()) tmp += " "; tmp += sub; } } while (iss); // push remaining text at the end if (tmp.length() > 0) result[args_count] = tmp; return result; } std::string msgt; static void do_whisper(t_connection * user_c, char const * dest, char const * text) { t_connection * dest_c; char const * tname; if (account_get_auth_mute(conn_get_account(user_c)) == 1) { message_send_text(user_c, message_type_error, user_c, localize(user_c, "Your account has been muted, you can't whisper to other users.")); return; } if (!(dest_c = connlist_find_connection_by_name(dest, conn_get_realm(user_c)))) { message_send_text(user_c, message_type_error, user_c, localize(user_c, "That user is not logged on.")); return; } #ifdef WITH_LUA if (lua_handle_user(user_c, dest_c, text, luaevent_user_whisper) == 1) return; #endif if (conn_get_dndstr(dest_c)) { msgtemp = localize(user_c, "{} is unavailable ({})", conn_get_username(dest_c), conn_get_dndstr(dest_c)); message_send_text(user_c, message_type_info, user_c, msgtemp); return; } message_send_text(user_c, message_type_whisperack, dest_c, text); if (conn_get_awaystr(dest_c)) { msgtemp = localize(user_c, "{} is away ({})", conn_get_username(dest_c), conn_get_awaystr(dest_c)); message_send_text(user_c, message_type_info, user_c, msgtemp); } message_send_text(dest_c, message_type_whisper, user_c, text); if ((tname = conn_get_username(user_c))) { char username[1 + MAX_USERNAME_LEN]; /* '*' + username (including NUL) */ if (std::strlen(tname) < MAX_USERNAME_LEN) { std::sprintf(username, "*%s", tname); conn_set_lastsender(dest_c, username); } } } static void do_whois(t_connection * c, char const * dest) { t_connection * dest_c; std::string namepart, verb; /* 64 + " (" + 64 + ")" + NUL */ t_game const * game; t_channel const * channel; if ((!(dest_c = connlist_find_connection_by_accountname(dest))) && (!(dest_c = connlist_find_connection_by_name(dest, conn_get_realm(c))))) { t_account * dest_a; t_bnettime btlogin; std::time_t ulogin; struct std::tm * tmlogin; if (!(dest_a = accountlist_find_account(dest))) { message_send_text(c, message_type_error, c, localize(c, "Unknown user.")); return; } if (conn_get_class(c) == conn_class_bnet) { btlogin = time_to_bnettime((std::time_t)account_get_ll_time(dest_a), 0); btlogin = bnettime_add_tzbias(btlogin, conn_get_tzbias(c)); ulogin = bnettime_to_time(btlogin); if (!(tmlogin = std::gmtime(&ulogin))) std::strcpy(msgtemp0, "?"); else std::strftime(msgtemp0, sizeof(msgtemp0), "%a %b %d %H:%M:%S", tmlogin); msgtemp = localize(c, "User was last seen on: {}", msgtemp0); } else msgtemp = localize(c, "User is offline"); message_send_text(c, message_type_info, c, msgtemp); return; } if (c == dest_c) { namepart = localize(c, "You"); verb = localize(c, "are"); } else { char const * tname; namepart = (tname = conn_get_chatcharname(dest_c, c)); conn_unget_chatcharname(dest_c, tname); verb = localize(c, "is"); } if ((game = conn_get_game(dest_c))) { msgtemp = localize(c, "{} {} using {} and {} currently in {} game \"{}\".", namepart, verb, clienttag_get_title(conn_get_clienttag(dest_c)), verb, game_get_flag(game) == game_flag_private ? localize(c, "private") : "", game_get_name(game)); } else if ((channel = conn_get_channel(dest_c))) { msgtemp = localize(c, "{} {} using {} and {} currently in channel \"{}\".", namepart, verb, clienttag_get_title(conn_get_clienttag(dest_c)), verb, channel_get_name(channel)); } else msgtemp = localize(c, "{} {} using {}.", namepart, verb, clienttag_get_title(conn_get_clienttag(dest_c))); message_send_text(c, message_type_info, c, msgtemp); if (conn_get_dndstr(dest_c)) { msgtemp = localize(c, "{} {} refusing messages ({})", namepart, verb, conn_get_dndstr(dest_c)); message_send_text(c, message_type_info, c, msgtemp); } else if (conn_get_awaystr(dest_c)) { msgtemp = localize(c, "{} away ({})", namepart, conn_get_awaystr(dest_c)); message_send_text(c, message_type_info, c, msgtemp); } } static void user_timer_cb(t_connection * c, std::time_t now, t_timer_data str) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!str.p) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return; } if (now != (std::time_t)0) /* zero means user logged out before expiration */ message_send_text(c, message_type_info, c, (char*)str.p); xfree(str.p); } typedef int(*t_command)(t_connection * c, char const * text); typedef struct { const char * command_string; t_command command_handler; } t_command_table_row; static int command_set_flags(t_connection * c); // [Omega] // command handler prototypes static int _handle_clan_command(t_connection * c, char const * text); static int _handle_admin_command(t_connection * c, char const * text); static int _handle_aop_command(t_connection * c, char const * text); static int _handle_op_command(t_connection * c, char const * text); static int _handle_tmpop_command(t_connection * c, char const * text); static int _handle_deop_command(t_connection * c, char const * text); static int _handle_voice_command(t_connection * c, char const * text); static int _handle_devoice_command(t_connection * c, char const * text); static int _handle_vop_command(t_connection * c, char const * text); static int _handle_friends_command(t_connection * c, char const * text); static int _handle_me_command(t_connection * c, char const * text); static int _handle_whisper_command(t_connection * c, char const * text); static int _handle_status_command(t_connection * c, char const * text); static int _handle_who_command(t_connection * c, char const * text); static int _handle_whois_command(t_connection * c, char const * text); static int _handle_whoami_command(t_connection * c, char const * text); static int _handle_announce_command(t_connection * c, char const * text); static int _handle_beep_command(t_connection * c, char const * text); static int _handle_nobeep_command(t_connection * c, char const * text); static int _handle_version_command(t_connection * c, char const * text); static int _handle_copyright_command(t_connection * c, char const * text); static int _handle_uptime_command(t_connection * c, char const * text); static int _handle_stats_command(t_connection * c, char const * text); static int _handle_time_command(t_connection * c, char const * text); static int _handle_channel_command(t_connection * c, char const * text); static int _handle_rejoin_command(t_connection * c, char const * text); static int _handle_away_command(t_connection * c, char const * text); static int _handle_dnd_command(t_connection * c, char const * text); static int _handle_squelch_command(t_connection * c, char const * text); static int _handle_unsquelch_command(t_connection * c, char const * text); static int _handle_kick_command(t_connection * c, char const * text); static int _handle_ban_command(t_connection * c, char const * text); static int _handle_unban_command(t_connection * c, char const * text); static int _handle_reply_command(t_connection * c, char const * text); static int _handle_realmann_command(t_connection * c, char const * text); static int _handle_watch_command(t_connection * c, char const * text); static int _handle_unwatch_command(t_connection * c, char const * text); static int _handle_watchall_command(t_connection * c, char const * text); static int _handle_unwatchall_command(t_connection * c, char const * text); static int _handle_lusers_command(t_connection * c, char const * text); static int _handle_news_command(t_connection * c, char const * text); static int _handle_games_command(t_connection * c, char const * text); static int _handle_channels_command(t_connection * c, char const * text); static int _handle_addacct_command(t_connection * c, char const * text); static int _handle_chpass_command(t_connection * c, char const * text); static int _handle_connections_command(t_connection * c, char const * text); static int _handle_finger_command(t_connection * c, char const * text); static int _handle_operator_command(t_connection * c, char const * text); static int _handle_admins_command(t_connection * c, char const * text); static int _handle_quit_command(t_connection * c, char const * text); static int _handle_kill_command(t_connection * c, char const * text); static int _handle_killsession_command(t_connection * c, char const * text); static int _handle_gameinfo_command(t_connection * c, char const * text); static int _handle_ladderactivate_command(t_connection * c, char const * text); static int _handle_rehash_command(t_connection * c, char const * text); static int _handle_find_command(t_connection * c, char const *text); static int _handle_save_command(t_connection * c, char const * text); static int _handle_shutdown_command(t_connection * c, char const * text); static int _handle_ladderinfo_command(t_connection * c, char const * text); static int _handle_timer_command(t_connection * c, char const * text); static int _handle_serverban_command(t_connection * c, char const * text); static int _handle_netinfo_command(t_connection * c, char const * text); static int _handle_quota_command(t_connection * c, char const * text); static int _handle_lockacct_command(t_connection * c, char const * text); static int _handle_unlockacct_command(t_connection * c, char const * text); static int _handle_muteacct_command(t_connection * c, char const * text); static int _handle_unmuteacct_command(t_connection * c, char const * text); static int _handle_flag_command(t_connection * c, char const * text); static int _handle_tag_command(t_connection * c, char const * text); static int _handle_ipscan_command(t_connection * c, char const * text); static int _handle_set_command(t_connection * c, char const * text); static int _handle_motd_command(t_connection * c, char const * text); static int _handle_ping_command(t_connection * c, char const * text); static int _handle_commandgroups_command(t_connection * c, char const * text); static int _handle_topic_command(t_connection * c, char const * text); static int _handle_moderate_command(t_connection * c, char const * text); static int _handle_clearstats_command(t_connection * c, char const * text); static int _handle_tos_command(t_connection * c, char const * text); static int _handle_alert_command(t_connection * c, char const * text); static const t_command_table_row standard_command_table[] = { { "/clan", _handle_clan_command }, { "/c", _handle_clan_command }, { "/admin", _handle_admin_command }, { "/f", _handle_friends_command }, { "/friends", _handle_friends_command }, { "/me", _handle_me_command }, { "/emote", _handle_me_command }, { "/msg", _handle_whisper_command }, { "/whisper", _handle_whisper_command }, { "/w", _handle_whisper_command }, { "/m", _handle_whisper_command }, { "/status", _handle_status_command }, { "/users", _handle_status_command }, { "/who", _handle_who_command }, { "/whois", _handle_whois_command }, { "/whereis", _handle_whois_command }, { "/where", _handle_whois_command }, { "/whoami", _handle_whoami_command }, { "/announce", _handle_announce_command }, { "/beep", _handle_beep_command }, { "/nobeep", _handle_nobeep_command }, { "/version", _handle_version_command }, { "/copyright", _handle_copyright_command }, { "/warranty", _handle_copyright_command }, { "/license", _handle_copyright_command }, { "/uptime", _handle_uptime_command }, { "/stats", _handle_stats_command }, { "/astat", _handle_stats_command }, { "/time", _handle_time_command }, { "/channel", _handle_channel_command }, { "/join", _handle_channel_command }, { "/j", _handle_channel_command }, { "/rejoin", _handle_rejoin_command }, { "/away", _handle_away_command }, { "/dnd", _handle_dnd_command }, { "/ignore", _handle_squelch_command }, { "/squelch", _handle_squelch_command }, { "/unignore", _handle_unsquelch_command }, { "/unsquelch", _handle_unsquelch_command }, { "/kick", _handle_kick_command }, { "/ban", _handle_ban_command }, { "/unban", _handle_unban_command }, { "/tos", _handle_tos_command }, { "/ann", _handle_announce_command }, { "/r", _handle_reply_command }, { "/reply", _handle_reply_command }, { "/realmann", _handle_realmann_command }, { "/watch", _handle_watch_command }, { "/unwatch", _handle_unwatch_command }, { "/watchall", _handle_watchall_command }, { "/unwatchall", _handle_unwatchall_command }, { "/lusers", _handle_lusers_command }, { "/news", _handle_news_command }, { "/games", _handle_games_command }, { "/channels", _handle_channels_command }, { "/chs", _handle_channels_command }, { "/addacct", _handle_addacct_command }, { "/chpass", _handle_chpass_command }, { "/connections", _handle_connections_command }, { "/con", _handle_connections_command }, { "/finger", _handle_finger_command }, { "/operator", _handle_operator_command }, { "/aop", _handle_aop_command }, { "/op", _handle_op_command }, { "/tmpop", _handle_tmpop_command }, { "/deop", _handle_deop_command }, { "/voice", _handle_voice_command }, { "/devoice", _handle_devoice_command }, { "/vop", _handle_vop_command }, { "/admins", _handle_admins_command }, { "/logout", _handle_quit_command }, { "/quit", _handle_quit_command }, { "/exit", _handle_quit_command }, { "/kill", _handle_kill_command }, { "/killsession", _handle_killsession_command }, { "/gameinfo", _handle_gameinfo_command }, { "/ladderactivate", _handle_ladderactivate_command }, { "/rehash", _handle_rehash_command }, { "/find", _handle_find_command }, { "/save", _handle_save_command }, { "/shutdown", _handle_shutdown_command }, { "/ladderinfo", _handle_ladderinfo_command }, { "/timer", _handle_timer_command }, { "/serverban", _handle_serverban_command }, { "/netinfo", _handle_netinfo_command }, { "/quota", _handle_quota_command }, { "/lockacct", _handle_lockacct_command }, { "/lock", _handle_lockacct_command }, { "/unlockacct", _handle_unlockacct_command }, { "/unlock", _handle_unlockacct_command }, { "/muteacct", _handle_muteacct_command }, { "/mute", _handle_muteacct_command }, { "/unmuteacct", _handle_unmuteacct_command }, { "/unmute", _handle_unmuteacct_command }, { "/flag", _handle_flag_command }, { "/tag", _handle_tag_command }, { "/help", handle_help_command }, { "/?", handle_help_command }, { "/mail", handle_mail_command }, { "/ipban", handle_ipban_command }, // in ipban.c { "/ipscan", _handle_ipscan_command }, { "/set", _handle_set_command }, { "/motd", _handle_motd_command }, { "/latency", _handle_ping_command }, { "/ping", _handle_ping_command }, { "/p", _handle_ping_command }, { "/commandgroups", _handle_commandgroups_command }, { "/cg", _handle_commandgroups_command }, { "/topic", _handle_topic_command }, { "/moderate", _handle_moderate_command }, { "/clearstats", _handle_clearstats_command }, { "/icon", handle_icon_command }, { "/alert", _handle_alert_command }, { "/language", handle_language_command }, { "/lang", handle_language_command }, { "/log", handle_log_command }, { NULL, NULL } }; extern int handle_command(t_connection * c, char const * text) { int result = 0; t_command_table_row const *p; #ifdef WITH_LUA // feature to ignore flood protection result = lua_handle_command(c, text, luaevent_command_before); #endif if (result == -1) return result; if (result == 0) if ((text[0] != '\0') && (conn_quota_exceeded(c, text))) { msgtemp = localize(c, "You are sending commands to {} too quickly and risk being disconnected for flooding. Please slow down.", prefs_get_servername()); message_send_text(c, message_type_error, c, msgtemp); return 0; } #ifdef WITH_LUA result = lua_handle_command(c, text, luaevent_command); // -1 = unsuccess, 0 = success, 1 = execute next c++ code if (result == 0) { // log command if (t_account * account = conn_get_account(c)) userlog_append(account, text); } if (result == 0 || result == -1) return result; #endif for (p = standard_command_table; p->command_string != NULL; p++) { if (strstart(text, p->command_string) == 0) { if (!(command_get_group(p->command_string))) { message_send_text(c, message_type_error, c, localize(c, "This command has been deactivated")); return 0; } if (!((command_get_group(p->command_string) & account_get_command_groups(conn_get_account(c))))) { message_send_text(c, message_type_error, c, localize(c, "This command is reserved for admins.")); return 0; } if (p->command_handler != NULL) { result = ((p->command_handler)(c, text)); // -1 = unsuccess, 0 = success if (result == 0) { // log command if (t_account * account = conn_get_account(c)) userlog_append(account, text); } return result; } } } if (std::strlen(text) >= 2 && std::strncmp(text, "//", 2) == 0) { handle_alias_command(c, text); return 0; } message_send_text(c, message_type_error, c, localize(c, "Unknown command.")); eventlog(eventlog_level_debug, __FUNCTION__, "got unknown command \"{}\"", text); return -1; } // +++++++++++++++++++++++++++++++++ command implementations +++++++++++++++++++++++++++++++++++++++ static int _handle_clan_command(t_connection * c, char const * text) { t_account * acc; t_clanmember * member; t_clan * clan; if (!(acc = conn_get_account(c))){ ERROR0("got NULL account"); } std::vector args = split_command(text, 2); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } /* FIXME: can get clan as is in creating process */ // user in clan if ((member = account_get_clanmember_forced(acc)) && (clan = clanmember_get_clan(member))) { // user is full member of clan if (clanmember_get_fullmember(member) == 1) { if (args[1] == "msg" || args[1] == "m" || args[1] == "w" || args[1] == "whisper") { if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } char const *msg = args[2].c_str(); // message if (clan_send_message_to_online_members(clan, message_type_whisper, c, msg) >= 1) message_send_text(c, message_type_info, c, localize(c, "Message was sent to all currently available clan members.")); else message_send_text(c, message_type_info, c, localize(c, "All fellow members of your clan are currently offline.")); return 0; } if (clanmember_get_status(member) >= CLAN_SHAMAN) { if (args[1] == "public" || args[1] == "pub") { if (clan_get_channel_type(clan) != 0) { clan_set_channel_type(clan, 0); message_send_text(c, message_type_info, c, localize(c, "Clan channel is opened up!")); } else message_send_text(c, message_type_error, c, localize(c, "Clan channel has already been opened up!")); return 0; } else if (args[1] == "private" || args[1] == "priv") { if (clan_get_channel_type(clan) != 1) { clan_set_channel_type(clan, 1); message_send_text(c, message_type_info, c, localize(c, "Clan channel is closed!")); } else message_send_text(c, message_type_error, c, localize(c, "Clan channel has already been closed!")); return 0; } else if (args[1] == "motd") { if (args[2].empty()) { describe_command(c, args[0].c_str()); return 0; } const char * msg = args[2].c_str(); // message clan_set_motd(clan, msg); message_send_text(c, message_type_info, c, localize(c, "Clan message of day is updated!")); return 0; } else if (args[1] == "invite" || args[1] == "inv") { t_account * dest_account; t_connection * dest_conn; if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } const char * username = args[2].c_str(); if ((dest_account = accountlist_find_account(username)) && (dest_conn = account_get_conn(dest_account)) && (account_get_clan(dest_account) == NULL) && (account_get_creating_clan(dest_account) == NULL)) { if (prefs_get_clan_newer_time() > 0) clan_add_member(clan, dest_account, CLAN_NEW); else clan_add_member(clan, dest_account, CLAN_PEON); msgtemp = localize(c, "User {} was invited to your clan!", username); message_send_text(c, message_type_error, c, msgtemp); msgtemp = localize(c, "You are invited to {} by {}!", clan_get_name(clan), conn_get_chatname(c)); message_send_text(dest_conn, message_type_error, c, msgtemp); } else { msgtemp = localize(c, "User {} is not online or is already member of clan!", username); message_send_text(c, message_type_error, c, msgtemp); } return 0; } else if (args[1] == "disband" || args[1] == "dis") { if (args[2] != "yes") { message_send_text(c, message_type_info, c, localize(c, "This is one-way action! If you really want")); message_send_text(c, message_type_info, c, localize(c, "to disband your clan, type /clan disband yes")); } /* PELISH: fixme - Find out better solution! */ if (clanlist_remove_clan(clan) == 0) { if (clan_get_created(clan) == 1) clan_remove(clan_get_clantag(clan)); clan_destroy(clan); message_send_text(c, message_type_info, c, localize(c, "Your clan was disbanded.")); } return 0; } } } // user is not full member (invitation was not accepted yet) else if (clanmember_get_fullmember(member) == 0) { /* User is not in clan, but he can accept invitation to someone */ if (args[1] != "invite" && args[1] != "inv") { describe_command(c, args[0].c_str()); return -1; } if (args[2] == "get") { msgtemp = localize(c, "You have been invited to {}", clan_get_name(clan)); message_send_text(c, message_type_info, c, msgtemp); return 0; } else if (args[2] == "accept" || args[2] == "acc") { int created = clan_get_created(clan); clanmember_set_fullmember(member, 1); clanmember_set_join_time(member, std::time(NULL)); msgtemp = localize(c, "You are now a clan member of {}", clan_get_name(clan)); message_send_text(c, message_type_info, c, msgtemp); if (created > 0) { DEBUG1("clan {} has already been created", clan_get_name(clan)); return -1; } created++; if (created >= 0) { clan_set_created(clan, 1); clan_set_creation_time(clan, std::time(NULL)); /* FIXME: send message "CLAN was be created" to members */ msgtemp = localize(c, "Clan {} was be created", clan_get_name(clan)); clan_send_message_to_online_members(clan, message_type_whisper, c, msgtemp.c_str()); /* Send message to all members */ message_send_text(c, message_type_whisper, c, msgtemp); /* also to self */ clan_save(clan); } else clan_set_created(clan, created); return 0; } else if (args[2] == "decline" || args[2] == "dec") { clan_remove_member(clan, member); msgtemp = localize(c, "You are no longer ivited to {}", clan_get_name(clan)); message_send_text(c, message_type_info, c, msgtemp); return 0; } } if ((args[1] == "create" || args[1] == "cre")) { message_send_text(c, message_type_error, c, localize(c, "You are already in clan \"{}\"", clan_get_name(clan))); return -1; } } // user not in clan else { if ((args[1] == "create" || args[1] == "cre")) { char const *clantag, *clanname; std::vector args = split_command(text, 3); if (args[3].empty()) { describe_command(c, args[0].c_str()); return -1; } clantag = args[2].c_str(); // clan tag clanname = args[3].c_str(); // clan name if (clan = clanlist_find_clan_by_clantag(str_to_clantag(clantag))) { message_send_text(c, message_type_error, c, localize(c, "Clan with your specified already exist!")); message_send_text(c, message_type_error, c, localize(c, "Please choice another one.")); return -1; } if ((clan = clan_create(conn_get_account(c), str_to_clantag(clantag), clanname, NULL)) && clanlist_add_clan(clan)) { member = account_get_clanmember_forced(acc); if (prefs_get_clan_min_invites() == 0) { clan_set_created(clan, 1); clan_set_creation_time(clan, std::time(NULL)); msgtemp = localize(c, "Clan {} is created!", clan_get_name(clan)); message_send_text(c, message_type_info, c, msgtemp); clan_save(clan); } else { clan_set_created(clan, -prefs_get_clan_min_invites() + 1); //Pelish: +1 means that creator of clan is already invited msgtemp = localize(c, "Clan {} is pre-created, please invite", clan_get_name(clan)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "at least {} players to your clan by using", prefs_get_clan_min_invites()); message_send_text(c, message_type_info, c, msgtemp); message_send_text(c, message_type_info, c, localize(c, "/clan invite command.")); } } return 0; } } describe_command(c, args[0].c_str()); return -1; } static int command_set_flags(t_connection * c) { return channel_set_userflags(c); } static int _handle_admin_command(t_connection * c, char const * text) { char const * username; char command; t_account * acc; t_connection * dst_c; int changed = 0; std::vector args = split_command(text, 1); if (args[1].empty() || (args[1][0] != '+' && args[1][0] != '-')) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); command = text[0]; // command type (+/-) username = &text[1]; // username if (!*username) { message_send_text(c, message_type_info, c, localize(c, "You must supply a username.")); return -1; } if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (command == '+') { if (account_get_auth_admin(acc, NULL) == 1) { msgtemp = localize(c, "{} is already a Server Admin", username); } else { account_set_auth_admin(acc, NULL, 1); msgtemp = localize(c, "{} has been promoted to a Server Admin", username); msgtemp2 = localize(c, "{} has promoted you to a Server Admin", conn_get_loggeduser(c)); changed = 1; } } else { if (account_get_auth_admin(acc, NULL) != 1) msgtemp = localize(c, "{} is not a Server Admin.", username); else { account_set_auth_admin(acc, NULL, 0); msgtemp = localize(c, "{} has been demoted from a Server Admin", username); msgtemp2 = localize(c, "{} has demoted you from a Server Admin", conn_get_loggeduser(c)); changed = 1; } } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); command_set_flags(dst_c); return 0; } static int _handle_operator_command(t_connection * c, char const * text) { char const * username; char command; t_account * acc; t_connection * dst_c; int changed = 0; std::vector args = split_command(text, 1); if (args[1].empty() || (args[1][0] != '+' && args[1][0] != '-')) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); command = text[0]; // command type (+/-) username = &text[1]; // username if (!*username) { message_send_text(c, message_type_info, c, localize(c, "You must supply a username.")); return -1; } if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (command == '+') { if (account_get_auth_operator(acc, NULL) == 1) msgtemp = localize(c, "{} is already a Server Operator", username); else { account_set_auth_operator(acc, NULL, 1); msgtemp = localize(c, "{} has been promoted to a Server Operator", username); msgtemp2 = localize(c, "{} has promoted you to a Server Operator", conn_get_loggeduser(c)); changed = 1; } } else { if (account_get_auth_operator(acc, NULL) != 1) msgtemp = localize(c, "{} is no Server Operator, so you can't demote him", username); else { account_set_auth_operator(acc, NULL, 0); msgtemp = localize(c, "{} has been demoted from a Server Operator", username); msgtemp2 = localize(c, "{} has promoted you to a Server Operator", conn_get_loggeduser(c)); changed = 1; } } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); command_set_flags(dst_c); return 0; } static int _handle_aop_command(t_connection * c, char const * text) { char const * username; char const * channel; t_account * acc; t_connection * dst_c; int changed = 0; if (!(conn_get_channel(c)) || !(channel = channel_get_name(conn_get_channel(c)))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (account_get_auth_admin(conn_get_account(c), NULL) != 1 && account_get_auth_admin(conn_get_account(c), channel) != 1) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Admin to use this command.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (account_get_auth_admin(acc, channel) == 1) msgtemp = localize(c, "{} is already a Channel Admin", username); else { account_set_auth_admin(acc, channel, 1); msgtemp = localize(c, "{} has been promoted to a Channel Admin", username); msgtemp2 = localize(c, "{} has promoted you to a Channel Admin for channel \"{}\"", conn_get_loggeduser(c), channel); changed = 1; } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); command_set_flags(dst_c); return 0; } static int _handle_vop_command(t_connection * c, char const * text) { char const * username; char const * channel; t_account * acc; t_connection * dst_c; int changed = 0; if (!(conn_get_channel(c)) || !(channel = channel_get_name(conn_get_channel(c)))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (account_get_auth_admin(conn_get_account(c), NULL) != 1 && account_get_auth_admin(conn_get_account(c), channel) != 1) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Admin to use this command.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (account_get_auth_voice(acc, channel) == 1) msgtemp = localize(c, "{} is already on VOP list", username); else { account_set_auth_voice(acc, channel, 1); msgtemp = localize(c, "{} has been added to the VOP list", username); msgtemp2 = localize(c, "{} has added you to the VOP list of channel \"{}\"", conn_get_loggeduser(c), channel); changed = 1; } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); command_set_flags(dst_c); return 0; } static int _handle_voice_command(t_connection * c, char const * text) { char const * username; char const * channel; t_account * acc; t_connection * dst_c; int changed = 0; if (!(conn_get_channel(c)) || !(channel = channel_get_name(conn_get_channel(c)))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (!(account_is_operator_or_admin(conn_get_account(c), channel_get_name(conn_get_channel(c))))) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Operator to use this command.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (account_get_auth_voice(acc, channel) == 1) msgtemp = localize(c, "{} is already on VOP list, no need to Voice him", username); else { if ((!dst_c) || conn_get_channel(c) != conn_get_channel(dst_c)) { msgtemp = localize(c, "{} must be on the same channel to voice him", username); } else { if (channel_conn_has_tmpVOICE(conn_get_channel(c), dst_c)) msgtemp = localize(c, "{} already has Voice in this channel", username); else { if (account_is_operator_or_admin(acc, channel)) msgtemp = localize(c, "{} is already an operator or admin.", username); else { conn_set_tmpVOICE_channel(dst_c, channel); msgtemp = localize(c, "{} has been granted Voice in this channel", username); msgtemp2 = localize(c, "{} has granted you Voice in this channel", conn_get_loggeduser(c)); changed = 1; } } } } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); command_set_flags(dst_c); return 0; } static int _handle_devoice_command(t_connection * c, char const * text) { char const * username; char const * channel; t_account * acc; t_connection * dst_c; int done = 0; int changed = 0; if (!(conn_get_channel(c)) || !(channel = channel_get_name(conn_get_channel(c)))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (!(account_is_operator_or_admin(conn_get_account(c), channel_get_name(conn_get_channel(c))))) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Operator to use this command.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (account_get_auth_voice(acc, channel) == 1) { if ((account_get_auth_admin(conn_get_account(c), channel) == 1) || (account_get_auth_admin(conn_get_account(c), NULL) == 1)) { account_set_auth_voice(acc, channel, 0); msgtemp = localize(c, "{} has been removed from VOP list.", username); msgtemp2 = localize(c, "{} has removed you from VOP list of channel \"{}\"", conn_get_loggeduser(c), channel); changed = 1; } else { msgtemp = localize(c, "You must be at least Channel Admin to remove {} from the VOP list", username); } done = 1; } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); changed = 0; if ((dst_c) && channel_conn_has_tmpVOICE(conn_get_channel(c), dst_c) == 1) { conn_set_tmpVOICE_channel(dst_c, NULL); msgtemp = localize(c, "Voice has been taken from {} in this channel", username); msgtemp2 = localize(c, "{} has taken your Voice in channel \"{}\"", conn_get_loggeduser(c), channel); changed = 1; done = 1; } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); if (!done) { msgtemp = localize(c, "{} has no Voice in this channel, so it can't be taken away", username); message_send_text(c, message_type_info, c, msgtemp); } command_set_flags(dst_c); return 0; } static int _handle_op_command(t_connection * c, char const * text) { char const * username; char const * channel; t_account * acc; int OP_lvl; t_connection * dst_c; int changed = 0; if (!(conn_get_channel(c)) || !(channel = channel_get_name(conn_get_channel(c)))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } acc = conn_get_account(c); OP_lvl = 0; if (account_is_operator_or_admin(acc, channel)) OP_lvl = 1; else if (channel_conn_is_tmpOP(conn_get_channel(c), c)) OP_lvl = 2; if (OP_lvl == 0) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Operator or tempOP to use this command.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (OP_lvl == 1) // user is full op so he may fully op others { if (account_get_auth_operator(acc, channel) == 1) msgtemp = localize(c, "{} is already a Channel Operator", username); else { account_set_auth_operator(acc, channel, 1); msgtemp = localize(c, "{} has been promoted to a Channel Operator", username); msgtemp2 = localize(c, "{} has promoted you to a Channel Operator in channel \"{}\"", conn_get_loggeduser(c), channel); changed = 1; } } else { // user is only tempOP so he may only tempOP others if ((!(dst_c)) || (conn_get_channel(c) != conn_get_channel(dst_c))) msgtemp = localize(c, "{} must be on the same channel to tempOP him", username); else { if (account_is_operator_or_admin(acc, channel)) msgtemp = localize(c, "{} already is operator or admin, no need to tempOP him", username); else { conn_set_tmpOP_channel(dst_c, channel); msgtemp = localize(c, "{} has been promoted to a tempOP", username); msgtemp2 = localize(c, "{} has promoted you to a tempOP in this channel", conn_get_loggeduser(c)); changed = 1; } } } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); command_set_flags(dst_c); return 0; } static int _handle_tmpop_command(t_connection * c, char const * text) { char const * username; char const * channel; t_account * acc; t_connection * dst_c; int changed = 0; if (!(conn_get_channel(c)) || !(channel = channel_get_name(conn_get_channel(c)))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (!(account_is_operator_or_admin(conn_get_account(c), channel_get_name(conn_get_channel(c))) || channel_conn_is_tmpOP(conn_get_channel(c), c))) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Operator or tmpOP to use this command.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (channel_conn_is_tmpOP(conn_get_channel(c), dst_c)) msgtemp = localize(c, "{} has already tmpOP in this channel", username); else { if ((!(dst_c)) || (conn_get_channel(c) != conn_get_channel(dst_c))) msgtemp = localize(c, "{} must be on the same channel to tempOP him", username); else { if (account_is_operator_or_admin(acc, channel)) msgtemp = localize(c, "{} already is operator or admin, no need to tempOP him", username); else { conn_set_tmpOP_channel(dst_c, channel); msgtemp = localize(c, "{} has been promoted to tmpOP in this channel", username); msgtemp2 = localize(c, "{} has promoted you to a tempOP in this channel", conn_get_loggeduser(c)); changed = 1; } } } if (changed && dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); message_send_text(c, message_type_info, c, msgtemp); command_set_flags(dst_c); return 0; } static int _handle_deop_command(t_connection * c, char const * text) { char const * username; char const * channel; t_account * acc; int OP_lvl; t_connection * dst_c; int done = 0; if (!(conn_get_channel(c)) || !(channel = channel_get_name(conn_get_channel(c)))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } acc = conn_get_account(c); OP_lvl = 0; if (account_is_operator_or_admin(acc, channel)) OP_lvl = 1; else if (channel_conn_is_tmpOP(conn_get_channel(c), account_get_conn(acc))) OP_lvl = 2; if (OP_lvl == 0) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Operator or tempOP to use this command.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(acc = accountlist_find_account(username))) { msgtemp = localize(c, "There's no account with username {}.", username); message_send_text(c, message_type_info, c, msgtemp); return -1; } dst_c = account_get_conn(acc); if (OP_lvl == 1) // user is real OP and allowed to deOP { if (account_get_auth_admin(acc, channel) == 1 || account_get_auth_operator(acc, channel) == 1) { if (account_get_auth_admin(acc, channel) == 1) { if (account_get_auth_admin(conn_get_account(c), channel) != 1 && account_get_auth_admin(conn_get_account(c), NULL) != 1) message_send_text(c, message_type_info, c, localize(c, "You must be at least a Channel Admin to demote another Channel Admin")); else { account_set_auth_admin(acc, channel, 0); msgtemp = localize(c, "{} has been demoted from a Channel Admin.", username); message_send_text(c, message_type_info, c, msgtemp); if (dst_c) { msgtemp2 = localize(c, "{} has demoted you from a Channel Admin of channel \"{}\"", conn_get_loggeduser(c), channel); message_send_text(dst_c, message_type_info, c, msgtemp2); } } } if (account_get_auth_operator(acc, channel) == 1) { account_set_auth_operator(acc, channel, 0); msgtemp = localize(c, "{} has been demoted from a Channel Operator", username); message_send_text(c, message_type_info, c, msgtemp); if (dst_c) { msgtemp2 = localize(c, "{} has demoted you from a Channel Operator of channel \"{}\"", conn_get_loggeduser(c), channel); message_send_text(dst_c, message_type_info, c, msgtemp2); } } done = 1; } if ((dst_c) && channel_conn_is_tmpOP(conn_get_channel(c), dst_c)) { conn_set_tmpOP_channel(dst_c, NULL); msgtemp = localize(c, "{} has been demoted from a tempOP of this channel", username); message_send_text(c, message_type_info, c, msgtemp); if (dst_c) { msgtemp2 = localize(c, "{} has demoted you from a tmpOP of channel \"{}\"", conn_get_loggeduser(c), channel); message_send_text(dst_c, message_type_info, c, msgtemp2); } done = 1; } if (!done) { msgtemp = localize(c, "{} is no Channel Admin or Channel Operator or tempOP, so you can't demote him.", username); message_send_text(c, message_type_info, c, msgtemp); } } else //user is just a tempOP and may only deOP other tempOPs { if (dst_c && channel_conn_is_tmpOP(conn_get_channel(c), dst_c)) { conn_set_tmpOP_channel(account_get_conn(acc), NULL); msgtemp = localize(c, "{} has been demoted from a tempOP of this channel", username); message_send_text(c, message_type_info, c, msgtemp); msgtemp2 = localize(c, "{} has demoted you from a tempOP of channel \"{}\"", conn_get_loggeduser(c), channel); if (dst_c) message_send_text(dst_c, message_type_info, c, msgtemp2); } else { msgtemp = localize(c, "{} is no tempOP in this channel, so you can't demote him", username); message_send_text(c, message_type_info, c, msgtemp); } } command_set_flags(connlist_find_connection_by_accountname(username)); return 0; } static int _handle_friends_command(t_connection * c, char const * text) { int i; t_account *my_acc = conn_get_account(c); std::vector args = split_command(text, 2); if (args[1] == "add" || args[1] == "a") { t_packet * rpacket; t_connection * dest_c; t_account * friend_acc; t_server_friendslistreply_status status; t_game * game; t_channel * channel; char stat; t_list * flist; t_friend * fr; if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[2].c_str(); // username if (!(friend_acc = accountlist_find_account(text))) { message_send_text(c, message_type_info, c, localize(c, "That user does not exist.")); return -1; } switch (account_add_friend(my_acc, friend_acc)) { case -1: message_send_text(c, message_type_error, c, localize(c, "Server error.")); return -1; case -2: message_send_text(c, message_type_info, c, localize(c, "You can't add yourself to your friends list.")); return -1; case -3: msgtemp = localize(c, "You can only have a maximum of {} friends.", prefs_get_max_friends()); message_send_text(c, message_type_info, c, msgtemp); return -1; case -4: msgtemp = localize(c, "{} is already on your friends list!", text); message_send_text(c, message_type_info, c, msgtemp); return -1; } msgtemp = localize(c, "Added {} to your friends list.", text); message_send_text(c, message_type_info, c, msgtemp); dest_c = connlist_find_connection_by_account(friend_acc); if (dest_c != NULL) { msgtemp = localize(c, "{} added you to his/her friends list.", conn_get_username(c)); message_send_text(dest_c, message_type_info, dest_c, msgtemp); } if ((conn_get_class(c) != conn_class_bnet) || (!(rpacket = packet_create(packet_class_bnet)))) return 0; packet_set_size(rpacket, sizeof(t_server_friendadd_ack)); packet_set_type(rpacket, SERVER_FRIENDADD_ACK); packet_append_string(rpacket, account_get_name(friend_acc)); game = NULL; channel = NULL; if (!(dest_c)) { bn_byte_set(&status.location, FRIENDSTATUS_OFFLINE); bn_byte_set(&status.status, 0); bn_int_set(&status.clienttag, 0); } else { bn_int_set(&status.clienttag, conn_get_clienttag(dest_c)); stat = 0; flist = account_get_friends(my_acc); fr = friendlist_find_account(flist, friend_acc); if ((friend_get_mutual(fr))) stat |= FRIEND_TYPE_MUTUAL; if ((conn_get_dndstr(dest_c))) stat |= FRIEND_TYPE_DND; if ((conn_get_awaystr(dest_c))) stat |= FRIEND_TYPE_AWAY; bn_byte_set(&status.status, stat); if ((game = conn_get_game(dest_c))) { if (game_get_flag(game) != game_flag_private) bn_byte_set(&status.location, FRIENDSTATUS_PUBLIC_GAME); else bn_byte_set(&status.location, FRIENDSTATUS_PRIVATE_GAME); } else if ((channel = conn_get_channel(dest_c))) { bn_byte_set(&status.location, FRIENDSTATUS_CHAT); } else { bn_byte_set(&status.location, FRIENDSTATUS_ONLINE); } } packet_append_data(rpacket, &status, sizeof(status)); if (game) packet_append_string(rpacket, game_get_name(game)); else if (channel) packet_append_string(rpacket, channel_get_name(channel)); else packet_append_string(rpacket, ""); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } else if (args[1] == "msg" || args[1] == "w" || args[1] == "whisper" || args[1] == "m") { char const *msg; int cnt = 0; t_connection * dest_c; t_elem * curr; t_friend * fr; t_list * flist; if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } msg = args[2].c_str(); // message flist = account_get_friends(my_acc); if (flist == NULL) return -1; LIST_TRAVERSE(flist, curr) { if (!(fr = (t_friend*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (friend_get_mutual(fr)) { dest_c = connlist_find_connection_by_account(friend_get_account(fr)); if (!dest_c) continue; message_send_text(dest_c, message_type_whisper, c, msg); cnt++; } } if (cnt) message_send_text(c, message_type_friendwhisperack, c, msg); else message_send_text(c, message_type_info, c, localize(c, "All of your friends are offline.")); } else if (args[1] == "r" || args[1] == "remove" || args[1] == "del" || args[1] == "delete") { int num; t_packet * rpacket; if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[2].c_str(); // username switch ((num = account_remove_friend2(my_acc, text))) { case -1: return -1; case -2: msgtemp = localize(c, "{} was not found on your friends list.", text); message_send_text(c, message_type_info, c, msgtemp); return -1; default: msgtemp = localize(c, "Removed {} from your friends list.", text); message_send_text(c, message_type_info, c, msgtemp); if ((conn_get_class(c) != conn_class_bnet) || (!(rpacket = packet_create(packet_class_bnet)))) return 0; packet_set_size(rpacket, sizeof(t_server_frienddel_ack)); packet_set_type(rpacket, SERVER_FRIENDDEL_ACK); bn_byte_set(&rpacket->u.server_frienddel_ack.friendnum, num); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } } else if (args[1] == "p" || args[1] == "promote") { int num; int n; char const * dest_name; t_packet * rpacket; t_list * flist; t_friend * fr; t_account * dest_acc; unsigned int dest_uid; if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[2].c_str(); // username num = account_get_friendcount(my_acc); flist = account_get_friends(my_acc); for (n = 1; n < num; n++) if ((dest_uid = account_get_friend(my_acc, n)) && (fr = friendlist_find_uid(flist, dest_uid)) && (dest_acc = friend_get_account(fr)) && (dest_name = account_get_name(dest_acc)) && (strcasecmp(dest_name, text) == 0)) { account_set_friend(my_acc, n, account_get_friend(my_acc, n - 1)); account_set_friend(my_acc, n - 1, dest_uid); msgtemp = localize(c, "Promoted {} in your friends list.", dest_name); message_send_text(c, message_type_info, c, msgtemp); if ((conn_get_class(c) != conn_class_bnet) || (!(rpacket = packet_create(packet_class_bnet)))) return 0; packet_set_size(rpacket, sizeof(t_server_friendmove_ack)); packet_set_type(rpacket, SERVER_FRIENDMOVE_ACK); bn_byte_set(&rpacket->u.server_friendmove_ack.pos1, n - 1); bn_byte_set(&rpacket->u.server_friendmove_ack.pos2, n); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } } else if (args[1] == "d" || args[1] == "demote") { int num; int n; char const * dest_name; t_packet * rpacket; t_list * flist; t_friend * fr; t_account * dest_acc; unsigned int dest_uid; if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[2].c_str(); // username num = account_get_friendcount(my_acc); flist = account_get_friends(my_acc); for (n = 0; n < num - 1; n++) if ((dest_uid = account_get_friend(my_acc, n)) && (fr = friendlist_find_uid(flist, dest_uid)) && (dest_acc = friend_get_account(fr)) && (dest_name = account_get_name(dest_acc)) && (strcasecmp(dest_name, text) == 0)) { account_set_friend(my_acc, n, account_get_friend(my_acc, n + 1)); account_set_friend(my_acc, n + 1, dest_uid); msgtemp = localize(c, "Demoted {} in your friends list.", dest_name); message_send_text(c, message_type_info, c, msgtemp); if ((conn_get_class(c) != conn_class_bnet) || (!(rpacket = packet_create(packet_class_bnet)))) return 0; packet_set_size(rpacket, sizeof(t_server_friendmove_ack)); packet_set_type(rpacket, SERVER_FRIENDMOVE_ACK); bn_byte_set(&rpacket->u.server_friendmove_ack.pos1, n); bn_byte_set(&rpacket->u.server_friendmove_ack.pos2, n + 1); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } } else if (args[1] == "list" || args[1] == "l" || args[1] == "online" || args[1] == "o") { char const * frienduid; std::string status, software; t_connection * dest_c; t_account * friend_acc; t_game const * game; t_channel const * channel; t_friend * fr; t_list * flist; int num; unsigned int uid; bool online_only = false; if (args[1] == "online" || args[1] == "o") { online_only = true; } if (!online_only) { msgtemp = localize(c, "Your {} - Friends List", prefs_get_servername()); message_send_text(c, message_type_info, c, msgtemp); } else { msgtemp = localize(c, "Your {} - Online Friends List", prefs_get_servername()); message_send_text(c, message_type_info, c, msgtemp); } message_send_text(c, message_type_info, c, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="); num = account_get_friendcount(my_acc); flist = account_get_friends(my_acc); if (flist != NULL) { for (i = 0; i < num; i++) { if ((!(uid = account_get_friend(my_acc, i))) || (!(fr = friendlist_find_uid(flist, uid)))) { eventlog(eventlog_level_error, __FUNCTION__, "friend uid in list"); continue; } friend_acc = friend_get_account(fr); if (!(dest_c = connlist_find_connection_by_account(friend_acc))) { if (online_only) { continue; } status = localize(c, ", offline"); } else { software = localize(c, " using {}", clienttag_get_title(conn_get_clienttag(dest_c))); if (friend_get_mutual(fr)) { if ((game = conn_get_game(dest_c))) status = localize(c, ", in game \"{}\"", game_get_name(game)); else if ((channel = conn_get_channel(dest_c))) { if (strcasecmp(channel_get_name(channel), "Arranged Teams") == 0) status = localize(c, ", in game AT Preparation"); else status = localize(c, ", in channel \"{}\",", channel_get_name(channel)); } else status = localize(c, ", is in AT Preparation"); } else { if ((game = conn_get_game(dest_c))) status = localize(c, ", is in a game"); else if ((channel = conn_get_channel(dest_c))) status = localize(c, ", is in a chat channel"); else status = localize(c, ", is in AT Preparation"); } } frienduid = account_get_name(friend_acc); if (!software.empty()) std::snprintf(msgtemp0, sizeof(msgtemp0), "%d: %s%.16s%.128s, %.64s", i + 1, friend_get_mutual(fr) ? "*" : " ", frienduid, status.c_str(), software.c_str()); else std::snprintf(msgtemp0, sizeof(msgtemp0), "%d: %.16s%.128s", i + 1, frienduid, status.c_str()); message_send_text(c, message_type_info, c, msgtemp0); } } message_send_text(c, message_type_info, c, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="); message_send_text(c, message_type_info, c, localize(c, "End of Friends List")); } else { describe_command(c, args[0].c_str()); return -1; } return 0; } static int _handle_me_command(t_connection * c, char const * text) { t_channel const * channel; if (!(channel = conn_get_channel(c))) { message_send_text(c, message_type_error, c, localize(c, "You are not in a channel.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // message if (!conn_quota_exceeded(c, text)) channel_message_send(channel, message_type_emote, c, text); return 0; } static int _handle_whisper_command(t_connection * c, char const *text) { char const * username; /* both include NUL, so no need to add one for middle @ or * */ std::vector args = split_command(text, 2); if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username text = args[2].c_str(); // message do_whisper(c, username, text); return 0; } static int _handle_status_command(t_connection * c, char const *text) { t_clienttag clienttag; // get clienttag std::vector args = split_command(text, 1); if (!args[1].empty() && (clienttag = tag_validate_client(args[1].c_str()))) { // clienttag status msgtemp = localize(c, "There are currently {} user(s) in {} games of {}", conn_get_user_count_by_clienttag(clienttag), game_get_count_by_clienttag(clienttag), clienttag_get_title(clienttag)); message_send_text(c, message_type_info, c, msgtemp); } else { // overall status msgtemp = localize(c, "There are currently {} users online, in {} games, and in {} channels.", connlist_login_get_length(), gamelist_get_length(), channellist_get_length()); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_who_command(t_connection * c, char const *text) { t_connection const * conn; t_channel const * channel; unsigned int i; char const * tname; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } char const * cname = args[1].c_str(); // channel name if (!(channel = channellist_find_channel_by_name(cname, conn_get_country(c), realm_get_name(conn_get_realm(c))))) { message_send_text(c, message_type_error, c, localize(c, "That channel does not exist.")); message_send_text(c, message_type_error, c, localize(c, "(If you are trying to search for a user, use the /whois command.)")); return -1; } if (channel_check_banning(channel, c) == 1) { message_send_text(c, message_type_error, c, localize(c, "You are banned from that channel.")); return -1; } std::snprintf(msgtemp0, sizeof msgtemp0, "%s", localize(c, "Users in channel {}:", cname).c_str()); i = std::strlen(msgtemp0); for (conn = channel_get_first(channel); conn; conn = channel_get_next()) { if (i + std::strlen((tname = conn_get_username(conn))) + 2 > sizeof(msgtemp0)) /* " ", name, '\0' */ { message_send_text(c, message_type_info, c, msgtemp0); i = 0; } std::sprintf(&msgtemp0[i], " %s", tname); i += std::strlen(&msgtemp0[i]); } if (i > 0) message_send_text(c, message_type_info, c, msgtemp0); return 0; } static int _handle_whois_command(t_connection * c, char const * text) { std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username do_whois(c, text); return 0; } static int _handle_whoami_command(t_connection * c, char const *text) { char const * tname; if (!(tname = conn_get_username(c))) { message_send_text(c, message_type_error, c, localize(c, "Unable to obtain your account name.")); return -1; } do_whois(c, tname); return 0; } static int _handle_announce_command(t_connection * c, char const *text) { t_message * message; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // message msgtemp = localize(c, "Announcement from {}: {}", conn_get_username(c), text); if (!(message = message_create(message_type_broadcast, c, msgtemp.c_str()))) message_send_text(c, message_type_info, c, localize(c, "Could not broadcast message.")); else { if (message_send_all(message) < 0) message_send_text(c, message_type_info, c, localize(c, "Could not broadcast message.")); message_destroy(message); } return 0; } static int _handle_beep_command(t_connection * c, char const *text) { message_send_text(c, message_type_info, c, localize(c, "Audible notification on.")); /* FIXME: actually do something */ return 0; /* FIXME: these only affect CHAT clients... I think they prevent ^G from being sent */ } static int _handle_nobeep_command(t_connection * c, char const *text) { message_send_text(c, message_type_info, c, localize(c, "Audible notification off.")); /* FIXME: actually do something */ return 0; } static int _handle_version_command(t_connection * c, char const *text) { message_send_text(c, message_type_info, c, PVPGN_SOFTWARE " " PVPGN_VERSION); return 0; } static int _handle_copyright_command(t_connection * c, char const *text) { static char const * const info[] = { " Copyright (C) 2002 - 2014 See source for details", " ", " PvPGN is free software; you can redistribute it and/or", " modify it under the terms of the GNU General Public License", " as published by the Free Software Foundation; either version 2", " of the License, or (at your option) any later version.", " ", " This program is distributed in the hope that it will be useful,", " but WITHOUT ANY WARRANTY; without even the implied warranty of", " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", " GNU General Public License for more details.", " ", " You should have received a copy of the GNU General Public License", " along with this program; if not, write to the Free Software", " Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.", NULL }; unsigned int i; for (i = 0; info[i]; i++) message_send_text(c, message_type_info, c, info[i]); return 0; } static int _handle_uptime_command(t_connection * c, char const *text) { msgtemp = localize(c, "Uptime: {}", seconds_to_timestr(server_get_uptime())); message_send_text(c, message_type_info, c, msgtemp); return 0; } static int _handle_stats_command(t_connection * c, char const *text) { char const * username; t_account * account; char const * clienttag = NULL; t_clienttag clienttag_uint; char clienttag_str[5]; std::vector args = split_command(text, 2); // username username = args[1].c_str(); if (args[1].empty()) { account = conn_get_account(c); } else if (!(account = accountlist_find_account(username))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } // clienttag if (!args[2].empty() && args[2].length() == 4) clienttag = args[2].c_str(); else if (!(clienttag = tag_uint_to_str(clienttag_str, conn_get_clienttag(c)))) { message_send_text(c, message_type_error, c, localize(c, "Unable to determine client game.")); return -1; } clienttag_uint = tag_case_str_to_uint(clienttag); // custom stats if (prefs_get_custom_icons() == 1 && customicons_allowed_by_client(clienttag_uint)) { const char *text; // if text is not empty if (text = customicons_get_stats_text(account, clienttag_uint)) { // split by lines char* output_array = strtok((char*)text, "\n"); while (output_array) { message_send_text(c, message_type_info, c, output_array); output_array = strtok(NULL, "\n"); } xfree((char*)text); return 0; } } switch (clienttag_uint) { case CLIENTTAG_BNCHATBOT_UINT: message_send_text(c, message_type_error, c, localize(c, "This game does not support win/loss records.")); message_send_text(c, message_type_error, c, localize(c, "You must supply a user name and a valid program ID.")); message_send_text(c, message_type_error, c, localize(c, "Example: /stats joe STAR")); return -1; case CLIENTTAG_DIABLORTL_UINT: case CLIENTTAG_DIABLOSHR_UINT: msgtemp = localize(c, "{}'s record:", account_get_name(account)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "level: {}", account_get_normal_level(account, clienttag_uint)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "class: {}", bnclass_get_str(account_get_normal_class(account, clienttag_uint))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "stats: {} str {} mag {} dex {} vit {} gld", account_get_normal_strength(account, clienttag_uint), account_get_normal_magic(account, clienttag_uint), account_get_normal_dexterity(account, clienttag_uint), account_get_normal_vitality(account, clienttag_uint), account_get_normal_gold(account, clienttag_uint)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Diablo kills: {}", account_get_normal_diablo_kills(account, clienttag_uint)); message_send_text(c, message_type_info, c, msgtemp); return 0; case CLIENTTAG_WARCIIBNE_UINT: msgtemp = localize(c, "{}'s record:", account_get_name(account)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Normal games: {}-{}-{}", account_get_normal_wins(account, clienttag_uint), account_get_normal_losses(account, clienttag_uint), account_get_normal_disconnects(account, clienttag_uint)); message_send_text(c, message_type_info, c, msgtemp); if (account_get_ladder_rating(account, clienttag_uint, ladder_id_normal) > 0) msgtemp = localize(c, "Ladder games: {}-{}-{} (rating {})", account_get_ladder_wins(account, clienttag_uint, ladder_id_normal), account_get_ladder_losses(account, clienttag_uint, ladder_id_normal), account_get_ladder_disconnects(account, clienttag_uint, ladder_id_normal), account_get_ladder_rating(account, clienttag_uint, ladder_id_normal)); else msgtemp = localize(c, "Ladder games: 0-0-0"); message_send_text(c, message_type_info, c, msgtemp); if (account_get_ladder_rating(account, clienttag_uint, ladder_id_ironman) > 0) msgtemp = localize(c, "IronMan games: {}-{}-{} (rating {})", account_get_ladder_wins(account, clienttag_uint, ladder_id_ironman), account_get_ladder_losses(account, clienttag_uint, ladder_id_ironman), account_get_ladder_disconnects(account, clienttag_uint, ladder_id_ironman), account_get_ladder_rating(account, clienttag_uint, ladder_id_ironman)); else msgtemp = localize(c, "IronMan games: 0-0-0"); message_send_text(c, message_type_info, c, msgtemp); return 0; case CLIENTTAG_WARCRAFT3_UINT: case CLIENTTAG_WAR3XP_UINT: msgtemp = localize(c, "{}'s Ladder Record's:", account_get_name(account)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Users Solo Level: {}, Experience: {}", account_get_ladder_level(account, clienttag_uint, ladder_id_solo), account_get_ladder_xp(account, clienttag_uint, ladder_id_solo)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "SOLO Ladder Record: {}-{}-0", account_get_ladder_wins(account, clienttag_uint, ladder_id_solo), account_get_ladder_losses(account, clienttag_uint, ladder_id_solo)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "SOLO Rank: {}", account_get_ladder_rank(account, clienttag_uint, ladder_id_solo)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Users Team Level: {}, Experience: {}", account_get_ladder_level(account, clienttag_uint, ladder_id_team), account_get_ladder_xp(account, clienttag_uint, ladder_id_team)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "TEAM Ladder Record: {}-{}-0", account_get_ladder_wins(account, clienttag_uint, ladder_id_team), account_get_ladder_losses(account, clienttag_uint, ladder_id_team)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "TEAM Rank: {}", account_get_ladder_rank(account, clienttag_uint, ladder_id_team)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Users FFA Level: {}, Experience: {}", account_get_ladder_level(account, clienttag_uint, ladder_id_ffa), account_get_ladder_xp(account, clienttag_uint, ladder_id_ffa)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "FFA Ladder Record: {}-{}-0", account_get_ladder_wins(account, clienttag_uint, ladder_id_ffa), account_get_ladder_losses(account, clienttag_uint, ladder_id_ffa)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "FFA Rank: {}", account_get_ladder_rank(account, clienttag_uint, ladder_id_ffa)); message_send_text(c, message_type_info, c, msgtemp); if (account_get_teams(account)) { t_elem * curr; t_list * list; t_team * team; int teamcount = 0; list = account_get_teams(account); LIST_TRAVERSE(list, curr) { if (!(team = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (team_get_clienttag(team) != clienttag_uint) continue; teamcount++; msgtemp = localize(c, "Users AT Team No. {}", teamcount); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Users AT TEAM Level: {}, Experience: {}", team_get_level(team), team_get_xp(team)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "AT TEAM Ladder Record: {}-{}-0", team_get_wins(team), team_get_losses(team)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "AT TEAM Rank: {}", team_get_rank(team)); message_send_text(c, message_type_info, c, msgtemp); } } return 0; default: msgtemp = localize(c, "{}'s record:", account_get_name(account)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Normal games: {}-{}-{}", account_get_normal_wins(account, clienttag_uint), account_get_normal_losses(account, clienttag_uint), account_get_normal_disconnects(account, clienttag_uint)); message_send_text(c, message_type_info, c, msgtemp); if (account_get_ladder_rating(account, clienttag_uint, ladder_id_normal) > 0) msgtemp = localize(c, "Ladder games: {}-{}-{} (rating {})", account_get_ladder_wins(account, clienttag_uint, ladder_id_normal), account_get_ladder_losses(account, clienttag_uint, ladder_id_normal), account_get_ladder_disconnects(account, clienttag_uint, ladder_id_normal), account_get_ladder_rating(account, clienttag_uint, ladder_id_normal)); else msgtemp = localize(c, "Ladder games: 0-0-0"); message_send_text(c, message_type_info, c, msgtemp); return 0; } } static int _handle_time_command(t_connection * c, char const *text) { t_bnettime btsystem; t_bnettime btlocal; std::time_t now; struct std::tm * tmnow; btsystem = bnettime(); /* Battle.net time: Wed Jun 23 15:15:29 */ btlocal = bnettime_add_tzbias(btsystem, local_tzbias()); now = bnettime_to_time(btlocal); if (!(tmnow = std::gmtime(&now))) std::strcpy(msgtemp0, "?"); else std::strftime(msgtemp0, sizeof(msgtemp0), "%a %b %d %H:%M:%S", tmnow); msgtemp = localize(c, "Server Time: {}", msgtemp0); message_send_text(c, message_type_info, c, msgtemp); if (conn_get_class(c) == conn_class_bnet) { btlocal = bnettime_add_tzbias(btsystem, conn_get_tzbias(c)); now = bnettime_to_time(btlocal); if (!(tmnow = std::gmtime(&now))) std::strcpy(msgtemp0, "?"); else std::strftime(msgtemp0, sizeof(msgtemp0), "%a %b %d %H:%M:%S", tmnow); msgtemp = localize(c, "Your local time: {}", msgtemp0); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_channel_command(t_connection * c, char const *text) { t_channel * channel; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // channelname if (!conn_get_game(c)) { if (strcasecmp(text, "Arranged Teams") == 0) { message_send_text(c, message_type_error, c, msgtemp = localize(c, "Channel Arranged Teams is a RESTRICTED Channel!")); return -1; } if (!(std::strlen(text) < MAX_CHANNELNAME_LEN)) { msgtemp = localize(c, "Max channel name length exceeded (max {} symbols)", MAX_CHANNELNAME_LEN - 1); message_send_text(c, message_type_error, c, msgtemp); return -1; } if ((channel = conn_get_channel(c)) && (strcasecmp(channel_get_name(channel), text) == 0)) return -1; // we don't have to do anything, we are already in this channel if (conn_set_channel(c, text) < 0) conn_set_channel(c, CHANNEL_NAME_BANNED); /* should not fail */ if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) conn_update_w3_playerinfo(c); command_set_flags(c); } else message_send_text(c, message_type_error, c, localize(c, "Command disabled while inside a game.")); return 0; } static int _handle_rejoin_command(t_connection * c, char const *text) { if (channel_rejoin(c) != 0) message_send_text(c, message_type_error, c, localize(c, "You are not in a channel.")); if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) conn_update_w3_playerinfo(c); command_set_flags(c); return 0; } static int _handle_away_command(t_connection * c, char const *text) { std::vector args = split_command(text, 1); text = args[1].c_str(); // message if (text[0] == '\0') /* toggle away mode */ { if (!conn_get_awaystr(c)) { message_send_text(c, message_type_info, c, localize(c, "You are now marked as being away.")); conn_set_awaystr(c, "Currently not available"); } else { message_send_text(c, message_type_info, c, localize(c, "You are no longer marked as away.")); conn_set_awaystr(c, NULL); } } else { message_send_text(c, message_type_info, c, localize(c, "You are now marked as being away.")); conn_set_awaystr(c, text); } return 0; } static int _handle_dnd_command(t_connection * c, char const *text) { std::vector args = split_command(text, 1); text = args[1].c_str(); // message if (text[0] == '\0') /* toggle dnd mode */ { if (!conn_get_dndstr(c)) { message_send_text(c, message_type_info, c, localize(c, "Do Not Disturb mode engaged.")); conn_set_dndstr(c, localize(c, "Not available").c_str()); } else { message_send_text(c, message_type_info, c, localize(c, "Do Not Disturb mode canceled.")); conn_set_dndstr(c, NULL); } } else { message_send_text(c, message_type_info, c, localize(c, "Do Not Disturb mode engaged.")); conn_set_dndstr(c, text); } return 0; } static int _handle_squelch_command(t_connection * c, char const *text) { t_account * account; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username /* D2 std::puts * before username */ if (text[0] == '*') text++; if (!(account = accountlist_find_account(text))) { message_send_text(c, message_type_error, c, localize(c, "No such user.")); return -1; } if (conn_get_account(c) == account) { message_send_text(c, message_type_error, c, localize(c, "You can't squelch yourself.")); return -1; } if (conn_add_ignore(c, account) < 0) { message_send_text(c, message_type_error, c, localize(c, "Could not squelch user.")); return -1; } else { msgtemp = localize(c, "{} has been squelched.", account_get_name(account)); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_unsquelch_command(t_connection * c, char const *text) { t_account * account; t_connection * dest_c; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username /* D2 std::puts * before username */ if (text[0] == '*') text++; if (!(account = accountlist_find_account(text))) { message_send_text(c, message_type_info, c, localize(c, "No such user.")); return -1; } if (conn_del_ignore(c, account) < 0) { message_send_text(c, message_type_info, c, localize(c, "User was not being ignored.")); return -1; } else { t_message * message; message_send_text(c, message_type_info, c, localize(c, "No longer ignoring.")); if ((dest_c = account_get_conn(account))) { if (!(message = message_create(message_type_userflags, dest_c, NULL))) /* handles NULL text */ return -1; message_send(message, c); message_destroy(message); } } return 0; } static int _handle_kick_command(t_connection * c, char const *text) { char const * username; t_channel const * channel; t_connection * kuc; t_account * acc; std::vector args = split_command(text, 2); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username text = args[2].c_str(); // reason if (!(channel = conn_get_channel(c))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } acc = conn_get_account(c); if (account_get_auth_admin(acc, NULL) != 1 && /* default to false */ account_get_auth_admin(acc, channel_get_name(channel)) != 1 && /* default to false */ account_get_auth_operator(acc, NULL) != 1 && /* default to false */ account_get_auth_operator(acc, channel_get_name(channel)) != 1 && /* default to false */ !channel_conn_is_tmpOP(channel, account_get_conn(acc))) { message_send_text(c, message_type_error, c, localize(c, "You have to be at least a Channel Operator or tempOP to use this command.")); return -1; } if (!(kuc = connlist_find_connection_by_accountname(username))) { message_send_text(c, message_type_error, c, localize(c, "That user is not logged in.")); return -1; } if (conn_get_channel(kuc) != channel) { message_send_text(c, message_type_error, c, localize(c, "That user is not in this channel.")); return -1; } if (account_get_auth_admin(conn_get_account(kuc), NULL) == 1 || account_get_auth_admin(conn_get_account(kuc), channel_get_name(channel)) == 1) { message_send_text(c, message_type_error, c, localize(c, "You cannot kick administrators.")); return -1; } else if (account_get_auth_operator(conn_get_account(kuc), NULL) == 1 || account_get_auth_operator(conn_get_account(kuc), channel_get_name(channel)) == 1) { message_send_text(c, message_type_error, c, localize(c, "You cannot kick operators.")); return -1; } { char const * tname1; char const * tname2; tname1 = conn_get_loggeduser(kuc); tname2 = conn_get_loggeduser(c); if (!tname1 || !tname2) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL username"); return -1; } if (text[0] != '\0') msgtemp = localize(c, "{} has been kicked by {} ({}).", tname1, tname2, text); else msgtemp = localize(c, "{} has been kicked by {}.", tname1, tname2); channel_message_send(channel, message_type_info, c, msgtemp.c_str()); } conn_kick_channel(kuc, "Bye"); if (conn_get_class(kuc) == conn_class_bnet) conn_set_channel(kuc, CHANNEL_NAME_KICKED); /* should not fail */ return 0; } static int _handle_ban_command(t_connection * c, char const *text) { char const * username; t_channel * channel; t_connection * buc; std::vector args = split_command(text, 2); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username text = args[2].c_str(); // reason if (!(channel = conn_get_channel(c))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (account_get_auth_admin(conn_get_account(c), NULL) != 1 && /* default to false */ account_get_auth_admin(conn_get_account(c), channel_get_name(channel)) != 1 && /* default to false */ account_get_auth_operator(conn_get_account(c), NULL) != 1 && /* default to false */ account_get_auth_operator(conn_get_account(c), channel_get_name(channel)) != 1) /* default to false */ { message_send_text(c, message_type_error, c, localize(c, "You have to be at least a Channel Operator to use this command.")); return -1; } { t_account * account; if (!(account = accountlist_find_account(username))) { message_send_text(c, message_type_info, c, localize(c, "That account doesn't currently exist.")); return -1; } else if (account_get_auth_admin(account, NULL) == 1 || account_get_auth_admin(account, channel_get_name(channel)) == 1) { message_send_text(c, message_type_error, c, localize(c, "You cannot ban administrators.")); return -1; } else if (account_get_auth_operator(account, NULL) == 1 || account_get_auth_operator(account, channel_get_name(channel)) == 1) { message_send_text(c, message_type_error, c, localize(c, "You cannot ban operators.")); return -1; } } if (channel_ban_user(channel, username) < 0) { msgtemp = localize(c, "Unable to ban {}.", username); message_send_text(c, message_type_error, c, msgtemp); } else { char const * tname; tname = conn_get_loggeduser(c); if (text[0] != '\0') msgtemp = localize(c, "{} has been banned by {} ({}).", username, tname ? tname : "unknown", text); else msgtemp = localize(c, "{} has been banned by {}.", username, tname ? tname : "unknown"); channel_message_send(channel, message_type_info, c, msgtemp.c_str()); } if ((buc = connlist_find_connection_by_accountname(username)) && conn_get_channel(buc) == channel) conn_set_channel(buc, CHANNEL_NAME_BANNED); return 0; } static int _handle_unban_command(t_connection * c, char const *text) { t_channel * channel; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username if (!(channel = conn_get_channel(c))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (account_get_auth_admin(conn_get_account(c), NULL) != 1 && /* default to false */ account_get_auth_admin(conn_get_account(c), channel_get_name(channel)) != 1 && /* default to false */ account_get_auth_operator(conn_get_account(c), NULL) != 1 && /* default to false */ account_get_auth_operator(conn_get_account(c), channel_get_name(channel)) != 1) /* default to false */ { message_send_text(c, message_type_error, c, localize(c, "You are not a channel operator.")); return -1; } if (channel_unban_user(channel, text) < 0) { message_send_text(c, message_type_error, c, localize(c, "That user is not banned.")); return -1; } else { msgtemp = localize(c, "{} is no longer banned from this channel.", text); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_reply_command(t_connection * c, char const *text) { char const * dest; if (!(dest = conn_get_lastsender(c))) { message_send_text(c, message_type_error, c, localize(c, "No one messaged you, use /m instead")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // message do_whisper(c, dest, text); return 0; } static int _handle_realmann_command(t_connection * c, char const *text) { t_realm * realm; t_realm * trealm; t_connection * tc; t_elem const * curr; t_message * message; if (!(realm = conn_get_realm(c))) { message_send_text(c, message_type_info, c, localize(c, "You must join a realm first")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // message msgtemp = localize(c, "Announcement from {}@{}: {}", conn_get_username(c), realm_get_name(realm), text); if (!(message = message_create(message_type_broadcast, c, msgtemp.c_str()))) { message_send_text(c, message_type_info, c, "Could not broadcast message."); return -1; } else { LIST_TRAVERSE_CONST(connlist(), curr) { tc = (t_connection*)elem_get_data(curr); if (!tc) continue; if ((trealm = conn_get_realm(tc)) && (trealm == realm)) { message_send(message, tc); } } } message_destroy(message); return 0; } static int _handle_watch_command(t_connection * c, char const *text) { t_account * account; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username if (!(account = accountlist_find_account(text))) { message_send_text(c, message_type_info, c, localize(c, "That user does not exist.")); return -1; } if (conn_add_watch(c, account, 0) < 0) /* FIXME: adds all events for now */ { message_send_text(c, message_type_error, c, localize(c, "Add to watch list failed.")); return -1; } else { msgtemp = localize(c, "User {} added to your watch list.", text); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_unwatch_command(t_connection * c, char const *text) { t_account * account; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username if (!(account = accountlist_find_account(text))) { message_send_text(c, message_type_info, c, localize(c, "That user does not exist.")); return -1; } if (conn_del_watch(c, account, 0) < 0) /* FIXME: deletes all events for now */ { message_send_text(c, message_type_error, c, localize(c, "Removal from watch list failed.")); return -1; } else { msgtemp = localize(c, "User {} removed from your watch list.", text); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_watchall_command(t_connection * c, char const *text) { t_clienttag clienttag = 0; char const * clienttag_str; std::vector args = split_command(text, 1); clienttag_str = args[1].c_str(); // clienttag if (clienttag_str[0] != '\0') { if ( !(clienttag = tag_validate_client(args[1].c_str())) ) { describe_command(c, args[0].c_str()); return -1; } } if (conn_add_watch(c, NULL, clienttag) < 0) /* FIXME: adds all events for now */ message_send_text(c, message_type_error, c, localize(c, "Add to watch list failed.")); else if (clienttag) { msgtemp = localize(c, "All {} users added to your watch list.", tag_uint_to_str((char*)clienttag_str, clienttag)); message_send_text(c, message_type_info, c, msgtemp); } else message_send_text(c, message_type_info, c, localize(c, "All users added to your watch list.")); return 0; } static int _handle_unwatchall_command(t_connection * c, char const *text) { t_clienttag clienttag = 0; char const * clienttag_str; std::vector args = split_command(text, 1); clienttag_str = args[1].c_str(); // clienttag if (clienttag_str[0] != '\0') { if (!(clienttag = tag_validate_client(args[1].c_str()))) { describe_command(c, args[0].c_str()); return -1; } } if (conn_del_watch(c, NULL, clienttag) < 0) /* FIXME: deletes all events for now */ message_send_text(c, message_type_error, c, localize(c, "Removal from watch list failed.")); else if (clienttag) { msgtemp = localize(c, "All {} users removed from your watch list.", tag_uint_to_str((char*)clienttag_str, clienttag)); message_send_text(c, message_type_info, c, msgtemp); } else message_send_text(c, message_type_info, c, localize(c, "All users removed from your watch list.")); return 0; } static int _handle_lusers_command(t_connection * c, char const *text) { t_channel * channel; t_elem const * curr; char const * banned; unsigned int i; if (!(channel = conn_get_channel(c))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } std::snprintf(msgtemp0, sizeof msgtemp0, "%s", localize(c, "Banned users:").c_str()); i = std::strlen(msgtemp0); LIST_TRAVERSE_CONST(channel_get_banlist(channel), curr) { banned = (char*)elem_get_data(curr); if (i + std::strlen(banned) + 2 > sizeof(msgtemp0)) /* " ", name, '\0' */ { message_send_text(c, message_type_info, c, msgtemp0); i = 0; } std::sprintf(&msgtemp0[i], " %s", banned); i += std::strlen(&msgtemp0[i]); } if (i > 0) message_send_text(c, message_type_info, c, msgtemp0); return 0; } static int _news_cb(std::time_t date, t_lstr *lstr, void *data) { char strdate[64]; struct std::tm *tm; char save, *p, *q; t_connection *c = (t_connection*)data; tm = std::localtime(&date); if (tm) std::strftime(strdate, 64, "%B %d, %Y", tm); else std::snprintf(strdate, sizeof strdate, "%s", localize(c, "(invalid date)").c_str()); message_send_text(c, message_type_info, c, strdate); for (p = lstr_get_str(lstr); *p;) { for (q = p; *q && *q != '\r' && *q != '\n'; q++); save = *q; *q = '\0'; message_send_text(c, message_type_info, c, p); *q = save; p = q; for (; *p == '\n' || *p == '\r'; p++); } return 0; } static int _handle_news_command(t_connection * c, char const *text) { news_traverse(_news_cb, c); return 0; } struct glist_cb_struct { t_game_difficulty diff; t_clienttag tag; t_connection *c; bool lobby; }; static int _glist_cb(t_game *game, void *data) { auto cbdata = reinterpret_cast(data); // when cbdata->tag != 0, user is requesting games of the same client tag // return early when the game's client tag does not match the user's client tag if (cbdata->tag != 0 && cbdata->tag != game_get_clienttag(game)) { return 0; } // when cbdata->lobby == true, only display list of all games in the lobby // return early if game has started or is already finished if (cbdata->lobby == true && (game_get_status(game) == game_status_started || game_get_status(game) == game_status_done)) { return 0; } // when cbdata->diff != game_difficulty_none, user is requesting a specific game difficulty // return early if user is requesting a specific game difficulty that does not match the game's difficulty if (cbdata->diff != game_difficulty_none && cbdata->diff != game_get_difficulty(game)) { return 0; } unsigned int pref = prefs_get_hide_pass_games(); if (pref && game_get_flag(game) == game_flag_private) { // return early if hide_pass_games is true and the game is private if (cbdata->tag != 0) { return 0; } // if user used /games all and is not an admin, return early else if (cbdata->tag == 0 && account_get_auth_admin(conn_get_account(cbdata->c), nullptr) != 1) { return 0; } } std::snprintf(msgtemp0, sizeof(msgtemp0), " %-16.16s %1.1s %-8.8s %-21.21s %5u ", game_get_name(game), game_get_flag(game) != game_flag_private ? "n" : "y", game_status_get_str(game_get_status(game)), game_type_get_str(game_get_type(game)), game_get_ref(game)); if (!cbdata->tag) { std::strcat(msgtemp0, clienttag_uint_to_str(game_get_clienttag(game))); std::strcat(msgtemp0, " "); } if ((!prefs_get_hide_addr()) || (account_get_command_groups(conn_get_account(cbdata->c)) & command_get_group("/admin-addr"))) /* default to false */ std::strcat(msgtemp0, addr_num_to_addr_str(game_get_addr(game), game_get_port(game))); message_send_text(cbdata->c, message_type_info, cbdata->c, msgtemp0); return 0; } static int _handle_games_command(t_connection * c, char const *text) { char clienttag_str[5]; char const * dest; char const * difficulty; struct glist_cb_struct cbdata; std::vector args = split_command(text, 2); dest = args[1].c_str(); // clienttag difficulty = args[1].c_str(); // difficulty (only for diablo) cbdata.c = c; cbdata.lobby = false; if (strcasecmp(difficulty, "norm") == 0) cbdata.diff = game_difficulty_normal; else if (strcasecmp(difficulty, "night") == 0) cbdata.diff = game_difficulty_nightmare; else if (strcasecmp(difficulty, "hell") == 0) cbdata.diff = game_difficulty_hell; else cbdata.diff = game_difficulty_none; if (dest[0] == '\0') { cbdata.tag = conn_get_clienttag(c); message_send_text(c, message_type_info, c, localize(c, "Currently accessible games:")); } else if (strcasecmp(dest, "all") == 0) { cbdata.tag = 0; message_send_text(c, message_type_info, c, localize(c, "All current games:")); } else if (strcasecmp(dest, "lobby") == 0 || strcasecmp(dest, "l") == 0) { cbdata.tag = conn_get_clienttag(c); cbdata.lobby = true; message_send_text(c, message_type_info, c, localize(c, "Games in lobby:")); } else { if (!(cbdata.tag = tag_validate_client(dest))) { describe_command(c, args[0].c_str()); return -1; } if (cbdata.diff == game_difficulty_none) msgtemp = localize(c, "Current games of type {}", tag_uint_to_str(clienttag_str, cbdata.tag)); else msgtemp = localize(c, "Current games of type {} {}", tag_uint_to_str(clienttag_str, cbdata.tag), difficulty); message_send_text(c, message_type_info, c, msgtemp); } msgtemp = localize(c, " ------name------ p -status- --------type--------- count "); if (!cbdata.tag) msgtemp += localize(c, "ctag "); if ((!prefs_get_hide_addr()) || (account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-addr"))) /* default to false */ msgtemp += localize(c, "--------addr--------"); message_send_text(c, message_type_info, c, msgtemp); gamelist_traverse(_glist_cb, &cbdata, gamelist_source_none); return 0; } static int _handle_channels_command(t_connection * c, char const *text) { t_elem const * curr; t_channel const * channel; t_clienttag clienttag; t_connection const * conn; t_account * acc; char const * name; int first; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // clienttag if (text[0] == '\0') { clienttag = conn_get_clienttag(c); message_send_text(c, message_type_info, c, localize(c, "Currently accessible channels:")); } else if (strcasecmp(text, "all") == 0) { clienttag = 0; message_send_text(c, message_type_info, c, localize(c, "All current channels:")); } else { if (!(clienttag = tag_validate_client(text))) { describe_command(c, args[0].c_str()); return -1; } msgtemp = localize(c, "Current channels of type {}", text); message_send_text(c, message_type_info, c, msgtemp); } msgtemp = localize(c, " -----------name----------- users ----admin/operator----"); message_send_text(c, message_type_info, c, msgtemp); LIST_TRAVERSE_CONST(channellist(), curr) { channel = (t_channel*)elem_get_data(curr); if ((!(channel_get_flags(channel) & channel_flags_clan)) && (!clienttag || !prefs_get_hide_temp_channels() || channel_get_permanent(channel)) && (!clienttag || !channel_get_clienttag(channel) || channel_get_clienttag(channel) == clienttag) && ((channel_get_max(channel) != 0) || //only show restricted channels to OPs and Admins ((channel_get_max(channel) == 0 && account_is_operator_or_admin(conn_get_account(c), NULL)))) && (!(channel_get_flags(channel) & channel_flags_thevoid)) // don't list TheVoid ) { std::snprintf(msgtemp0, sizeof(msgtemp0), " %-26.26s %5d - ", channel_get_name(channel), channel_get_length(channel)); first = 1; for (conn = channel_get_first(channel); conn; conn = channel_get_next()) { acc = conn_get_account(conn); if (account_is_operator_or_admin(acc, channel_get_name(channel)) || channel_conn_is_tmpOP(channel, account_get_conn(acc))) { name = conn_get_loggeduser(conn); if (std::strlen(msgtemp0) + std::strlen(name) + 6 >= MAX_MESSAGE_LEN) break; if (!first) std::strcat(msgtemp0, " ,"); std::strcat(msgtemp0, name); if (account_get_auth_admin(acc, NULL) == 1) std::strcat(msgtemp0, "(A)"); else if (account_get_auth_operator(acc, NULL) == 1) std::strcat(msgtemp0, "(O)"); else if (account_get_auth_admin(acc, channel_get_name(channel)) == 1) std::strcat(msgtemp0, "(a)"); else if (account_get_auth_operator(acc, channel_get_name(channel)) == 1) std::strcat(msgtemp0, "(o)"); first = 0; } } message_send_text(c, message_type_info, c, msgtemp0); } } return 0; } static int _handle_addacct_command(t_connection * c, char const *text) { unsigned int i; t_account * temp; t_hash passhash; char const * username, *pass; std::vector args = split_command(text, 2); if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (account_check_name(username) < 0) { message_send_text(c, message_type_error, c, localize(c, "Account name contains some invalid symbol!")); return -1; } if (args[2].length() > MAX_USERPASS_LEN) { msgtemp = localize(c, "Maximum password length allowed is {}", MAX_USERPASS_LEN); message_send_text(c, message_type_error, c, msgtemp); return -1; } for (i = 0; i < args[2].length(); i++) args[2][i] = safe_tolower(args[2][i]); pass = args[2].c_str(); // password bnet_hash(&passhash, std::strlen(pass), pass); msgtemp = localize(c, "Trying to add account \"{}\" with password \"{}\"", username, pass); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Hash is: {}", hash_get_str(passhash)); message_send_text(c, message_type_info, c, msgtemp); temp = accountlist_create_account(username, hash_get_str(passhash)); if (!temp) { message_send_text(c, message_type_error, c, localize(c, "Failed to create account!")); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account \"{}\" not created (failed)", conn_get_socket(c), username); return -1; } msgtemp = localize(c, "Account {} created.", account_get_uid(temp)); message_send_text(c, message_type_info, c, msgtemp); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account \"{}\" created", conn_get_socket(c), username); return 0; } static int _handle_chpass_command(t_connection * c, char const *text) { unsigned int i; t_account * account; t_account * temp; t_hash passhash; char const * username; std::string pass; std::vector args = split_command(text, 2); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } if (args[2].empty()) { username = conn_get_username(c); pass = args[1]; } else { username = args[1].c_str(); pass = args[2]; } temp = accountlist_find_account(username); account = conn_get_account(c); if ((temp == account && account_get_auth_changepass(account) == 0) || /* default to true */ (temp != account && !(account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-chpass")))) /* default to false */ { eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change for \"{}\" refused (no change access)", conn_get_socket(c), username); message_send_text(c, message_type_error, c, localize(c, "Only admins may change passwords for other accounts.")); return -1; } if (!temp) { message_send_text(c, message_type_error, c, localize(c, "Account does not exist.")); return -1; } if (pass.length() > MAX_USERPASS_LEN) { msgtemp = localize(c, "Maximum password length allowed is {}", MAX_USERPASS_LEN); message_send_text(c, message_type_error, c, msgtemp); return -1; } for (i = 0; i < pass.length(); i++) pass[i] = safe_tolower(pass[i]); bnet_hash(&passhash, pass.length(), pass.c_str()); msgtemp = localize(c, "Trying to change password for account \"{}\" to \"{}\"", username, pass.c_str()); message_send_text(c, message_type_info, c, msgtemp); if (account_set_pass(temp, hash_get_str(passhash)) < 0) { message_send_text(c, message_type_error, c, localize(c, "Unable to set password.")); return -1; } if (account_get_auth_admin(account, NULL) == 1 || account_get_auth_operator(account, NULL) == 1) { msgtemp = localize(c, "Password for account {} updated.", account_get_uid(temp)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Hash is: {}", hash_get_str(passhash)); message_send_text(c, message_type_info, c, msgtemp); } else { msgtemp = localize(c, "Password for account {} updated.", username); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_connections_command(t_connection *c, char const *text) { t_elem const * curr; t_connection * conn; char name[19]; char const * channel_name; char clienttag_str[5]; if (!prefs_get_enable_conn_all() && !(account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-con"))) /* default to false */ { message_send_text(c, message_type_error, c, localize(c, "This command is only enabled for admins.")); return -1; } message_send_text(c, message_type_info, c, localize(c, "Current connections:")); std::vector args = split_command(text, 1); text = args[1].c_str(); if (text[0] == '\0') { msgtemp = localize(c, " -class -tag -----name------ -lat(ms)- ----channel---- --game--"); message_send_text(c, message_type_info, c, msgtemp); } else if (std::strcmp(text, "all") == 0) /* print extended info */ { if (prefs_get_hide_addr() && !(account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-addr"))) msgtemp = localize(c, " -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game--"); else msgtemp = localize(c, " -#- -class ----state--- -tag -----name------ -session-- -flag- -lat(ms)- ----channel---- --game-- ---------addr--------"); message_send_text(c, message_type_info, c, msgtemp); } else { message_send_text(c, message_type_error, c, localize(c, "Unknown option.")); return -1; } LIST_TRAVERSE_CONST(connlist(), curr) { conn = (t_connection*)elem_get_data(curr); std::snprintf(name, sizeof name, "%s", conn_get_account(conn) ? conn_get_username(conn) : "(none)"); if (conn_get_channel(conn) != NULL) channel_name = channel_get_name(conn_get_channel(conn)); else channel_name = localize(c, "none").c_str(); std::string game_name; if (conn_get_game(conn) != NULL) game_name = game_get_name(conn_get_game(conn)); else game_name = localize(c, "none"); if (text[0] == '\0') std::snprintf(msgtemp0, sizeof(msgtemp0), " %-6.6s %4.4s %-15.15s %9u %-16.16s %-8.8s", conn_class_get_str(conn_get_class(conn)), tag_uint_to_str(clienttag_str, conn_get_fake_clienttag(conn)), name, conn_get_latency(conn), channel_name, game_name.c_str()); else if (prefs_get_hide_addr() && !(account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-addr"))) /* default to false */ std::snprintf(msgtemp0, sizeof(msgtemp0), " %3d %-6.6s %-12.12s %4.4s %-15.15s 0x%08x 0x%04x %9u %-16.16s %-8.8s", conn_get_socket(conn), conn_class_get_str(conn_get_class(conn)), conn_state_get_str(conn_get_state(conn)), tag_uint_to_str(clienttag_str, conn_get_fake_clienttag(conn)), name, conn_get_sessionkey(conn), conn_get_flags(conn), conn_get_latency(conn), channel_name, game_name.c_str()); else std::snprintf(msgtemp0, sizeof(msgtemp0), " %3d %-6.6s %-12.12s %4.4s %-15.15s 0x%08x 0x%04x %9u %-16.16s %-8.8s %.16s", conn_get_socket(conn), conn_class_get_str(conn_get_class(conn)), conn_state_get_str(conn_get_state(conn)), tag_uint_to_str(clienttag_str, conn_get_fake_clienttag(conn)), name, conn_get_sessionkey(conn), conn_get_flags(conn), conn_get_latency(conn), channel_name, game_name.c_str(), addr_num_to_addr_str(conn_get_addr(conn), conn_get_port(conn))); message_send_text(c, message_type_info, c, msgtemp0); } return 0; } static int _handle_finger_command(t_connection * c, char const *text) { char const * dest; t_account * account; t_connection * conn; char * tok; t_clanmember * clanmemb; std::time_t then; struct std::tm * tmthen; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } dest = args[1].c_str(); // username; if (!(account = accountlist_find_account(dest))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } then = account_get_ll_ctime(account); tmthen = std::localtime(&then); /* FIXME: determine user's timezone */ // do not display sex if empty std::string sex = account_get_sex(account); std::string pattern = "Login: {} {} Sex: {}"; pattern = (sex.length() > 0) ? pattern : pattern.substr(0, pattern.find("Sex: ", 0)); msgtemp = localize(c, pattern.c_str(), account_get_name(account), account_get_uid(account), account_get_sex(account)); message_send_text(c, message_type_info, c, msgtemp); std::strftime(msgtemp0, sizeof(msgtemp0), "%a %b %d %H:%M %Y", tmthen); msgtemp = localize(c, "Created: {}", msgtemp0); message_send_text(c, message_type_info, c, msgtemp); if ((clanmemb = account_get_clanmember(account))) { t_clan * clan; char status; if ((clan = clanmember_get_clan(clanmemb))) { msgtemp = localize(c, "Clan: {}", clan_get_name(clan)); if ((status = clanmember_get_status(clanmemb))) { switch (status) { case CLAN_CHIEFTAIN: msgtemp += localize(c, " Rank: Chieftain"); break; case CLAN_SHAMAN: msgtemp += localize(c, " Rank: Shaman"); break; case CLAN_GRUNT: msgtemp += localize(c, " Rank: Grunt"); break; case CLAN_PEON: msgtemp += localize(c, " Rank: Peon"); break; default:; } } message_send_text(c, message_type_info, c, msgtemp); } } // do not display age if empty std::string age = account_get_age(account); pattern = "Location: {} Age: {}"; pattern = (age.length() > 0) ? pattern : pattern.substr(0, pattern.find("Age: ", 0)); std::string loc = account_get_loc(account); msgtemp = localize(c, pattern.c_str(), (!loc.empty()) ? loc : "unknown", account_get_age(account)); message_send_text(c, message_type_info, c, msgtemp); if ((conn = connlist_find_connection_by_accountname(dest))) { msgtemp = localize(c, "Client: {} Ver: {} Country: {}", clienttag_get_title(conn_get_clienttag(conn)), conn_get_clientver(conn), conn_get_country(conn)); message_send_text(c, message_type_info, c, msgtemp); } const char* const ip_tmp = account_get_ll_ip(account); std::string ip(ip_tmp ? ip_tmp : ""); if (ip.empty() == true || !(account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-addr"))) /* default to false */ ip = localize(c, "unknown"); { then = account_get_ll_time(account); tmthen = std::localtime(&then); /* FIXME: determine user's timezone */ if (tmthen) std::strftime(msgtemp0, sizeof(msgtemp0), "%a %b %d %H:%M %Y", tmthen); else std::strcpy(msgtemp0, "?"); if (!(conn)) msgtemp = localize(c, "Last login {} from ", msgtemp0); else msgtemp = localize(c, "On since {} from ", msgtemp0); } msgtemp += ip; message_send_text(c, message_type_info, c, msgtemp); /* check /admin-addr for admin privileges */ if ((account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-addr"))) { std::string yes = localize(c, "Yes"); std::string no = localize(c, "No"); /* the player who requested /finger has admin privileges give him more info about the one he queries; is_admin, is_operator, is_locked, email */ msgtemp = localize(c, "Operator: {}, Admin: {}, Locked: {}, Muted: {}", account_get_auth_operator(account, NULL) == 1 ? yes : no, account_get_auth_admin(account, NULL) == 1 ? yes : no, account_get_auth_lock(account) == 1 ? yes : no, account_get_auth_mute(account) == 1 ? yes : no); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Email: {}", account_get_email(account)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Last login Owner: {}", account_get_ll_owner(account)); message_send_text(c, message_type_info, c, msgtemp); } if (conn) { msgtemp = localize(c, "Idle {}", seconds_to_timestr(conn_get_idletime(conn))); message_send_text(c, message_type_info, c, msgtemp); } std::strncpy(msgtemp0, account_get_desc(account).c_str(), sizeof(msgtemp0)); msgtemp0[sizeof(msgtemp0)-1] = '\0'; for (tok = std::strtok(msgtemp0, "\r\n"); tok; tok = std::strtok(NULL, "\r\n")) message_send_text(c, message_type_info, c, tok); message_send_text(c, message_type_info, c, ""); return 0; } /* FIXME: do we want to show just Server Admin or Channel Admin Also? [Omega] */ static int _handle_admins_command(t_connection * c, char const *text) { unsigned int i; t_elem const * curr; t_connection * tc; char const * nick; std::snprintf(msgtemp0, sizeof msgtemp0, "%s", localize(c, "Currently logged on Administrators:").c_str()); i = std::strlen(msgtemp0); LIST_TRAVERSE_CONST(connlist(), curr) { tc = (t_connection*)elem_get_data(curr); if (!tc) continue; if (!conn_get_account(tc)) continue; if (account_get_auth_admin(conn_get_account(tc), NULL) == 1) { if ((nick = conn_get_username(tc))) { if (i + std::strlen(nick) + 2 > sizeof(msgtemp0)) /* " ", name, '\0' */ { message_send_text(c, message_type_info, c, msgtemp0); i = 0; } std::sprintf(&msgtemp0[i], " %s", nick); i += std::strlen(&msgtemp0[i]); } } } if (i > 0) message_send_text(c, message_type_info, c, msgtemp0); return 0; } static int _handle_quit_command(t_connection * c, char const *text) { if (conn_get_game(c)) eventlog(eventlog_level_warn, __FUNCTION__, "[{}] user '{}' tried to disconnect while in game, cheat attempt ?", conn_get_socket(c), conn_get_loggeduser(c)); else { message_send_text(c, message_type_info, c, localize(c, "Connection closed.")); conn_set_state(c, conn_state_destroy); } return 0; } static int _handle_kill_command(t_connection * c, char const *text) { t_connection * user; char const * username, * min; std::vector args = split_command(text, 2); username = args[1].c_str(); // username min = args[2].c_str(); // minutes of ban if (username[0] == '\0' || (username[0] == '#' && (username[1] < '0' || username[1] > '9'))) { describe_command(c, args[0].c_str()); return -1; } if (username[0] == '#') { if (!(user = connlist_find_connection_by_socket(std::atoi(username + 1)))) { message_send_text(c, message_type_error, c, localize(c, "That connection doesn't exist.")); return -1; } } else { if (!(user = connlist_find_connection_by_accountname(username))) { message_send_text(c, message_type_error, c, localize(c, "That user is not logged in?")); return -1; } } if (min[0] != '\0' && ipbanlist_add(c, addr_num_to_ip_str(conn_get_addr(user)), ipbanlist_str_to_time_t(c, min)) == 0) { ipbanlist_save(prefs_get_ipbanfile()); message_send_text(user, message_type_info, user, localize(c, "An admin has closed your connection and banned your IP address.")); } else message_send_text(user, message_type_info, user, localize(c, "Connection closed by admin.")); conn_set_state(user, conn_state_destroy); message_send_text(c, message_type_info, c, localize(c, "Operation successful.")); return 0; } static int _handle_killsession_command(t_connection * c, char const *text) { t_connection * user; char const * session, *min; std::vector args = split_command(text, 2); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } session = args[1].c_str(); // session id min = args[1].c_str(); // minutes of ban if (!std::isxdigit((int)session[0])) { message_send_text(c, message_type_error, c, localize(c, "That is not a valid session.")); return -1; } if (!(user = connlist_find_connection_by_sessionkey((unsigned int)std::strtoul(session, NULL, 16)))) { message_send_text(c, message_type_error, c, localize(c, "That session does not exist.")); return -1; } if (min[0] != '\0' && ipbanlist_add(c, addr_num_to_ip_str(conn_get_addr(user)), ipbanlist_str_to_time_t(c, min)) == 0) { ipbanlist_save(prefs_get_ipbanfile()); message_send_text(user, message_type_info, user, localize(c, "Connection closed by admin and banned your IP's.")); } else message_send_text(user, message_type_info, user, localize(c, "Connection closed by admin.")); conn_set_state(user, conn_state_destroy); return 0; } static int _handle_gameinfo_command(t_connection * c, char const *text) { t_game const * game; char clienttag_str[5]; std::vector args = split_command(text, 1); text = args[1].c_str(); if (text[0] == '\0') { // current user game if (!(game = conn_get_game(c))) { message_send_text(c, message_type_error, c, localize(c, "You are not in a game.")); return -1; } } else if (!(game = gamelist_find_game_available(text, conn_get_clienttag(c), game_type_all))) { message_send_text(c, message_type_error, c, localize(c, "That game does not exist.")); return -1; } std::string pub = localize(c, "public"); std::string prv = localize(c, "private"); msgtemp = localize(c, "Name: {} ID: {} ({})", game_get_name(game), game_get_id(game), game_get_flag(game) != game_flag_private ? pub : prv); message_send_text(c, message_type_info, c, msgtemp); { t_account * owner; char const * tname; char const * namestr; if (!(owner = conn_get_account(game_get_owner(game)))) { tname = NULL; namestr = localize(c, "none").c_str(); } else if (!(tname = conn_get_loggeduser(game_get_owner(game)))) namestr = localize(c, "unknown").c_str(); else namestr = tname; msgtemp = localize(c, "Owner: {}", namestr); } message_send_text(c, message_type_info, c, msgtemp); if (!prefs_get_hide_addr() || (account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-addr"))) /* default to false */ { unsigned int addr; unsigned short port; unsigned int taddr; unsigned short tport; taddr = addr = game_get_addr(game); tport = port = game_get_port(game); trans_net(conn_get_addr(c), &taddr, &tport); if (taddr == addr && tport == port) msgtemp = localize(c, "Address: {}", addr_num_to_addr_str(addr, port)); else msgtemp = localize(c, "Address: {} (trans {})", addr_num_to_addr_str(addr, port), addr_num_to_addr_str(taddr, tport)); message_send_text(c, message_type_info, c, msgtemp); } msgtemp = localize(c, "Client: {} (version {}, startver {})", tag_uint_to_str(clienttag_str, game_get_clienttag(game)), vernum_to_verstr(game_get_version(game)), game_get_startver(game)); message_send_text(c, message_type_info, c, msgtemp); { std::time_t gametime; struct std::tm * gmgametime; gametime = game_get_create_time(game); if (!(gmgametime = std::localtime(&gametime))) std::strcpy(msgtemp0, "?"); else std::strftime(msgtemp0, sizeof(msgtemp0), GAME_TIME_FORMAT, gmgametime); msgtemp = localize(c, "Created: {}", msgtemp0); message_send_text(c, message_type_info, c, msgtemp); gametime = game_get_start_time(game); if (gametime != (std::time_t)0) { if (!(gmgametime = std::localtime(&gametime))) std::strcpy(msgtemp0, "?"); else std::strftime(msgtemp0, sizeof(msgtemp0), GAME_TIME_FORMAT, gmgametime); } else std::strcpy(msgtemp0, ""); msgtemp = localize(c, "Started: {}", msgtemp0); message_send_text(c, message_type_info, c, msgtemp); } msgtemp = localize(c, "Status: {}", game_status_get_str(game_get_status(game))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Type: {}", game_type_get_str(game_get_type(game))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Speed: {}", game_speed_get_str(game_get_speed(game))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Difficulty: {}", game_difficulty_get_str(game_get_difficulty(game))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Option: {}", game_option_get_str(game_get_option(game))); message_send_text(c, message_type_info, c, msgtemp); { char const * mapname; if (!(mapname = game_get_mapname(game))) mapname = localize(c, "unknown").c_str(); msgtemp = localize(c, "Map: {}", mapname); message_send_text(c, message_type_info, c, msgtemp); } msgtemp = localize(c, "Map Size: {}x{}", game_get_mapsize_x(game), game_get_mapsize_y(game)); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Map Tileset: {}", game_tileset_get_str(game_get_tileset(game))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Map Type: {}", game_maptype_get_str(game_get_maptype(game))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Players: {} current, {} total, {} max", game_get_ref(game), game_get_count(game), game_get_maxplayers(game)); message_send_text(c, message_type_info, c, msgtemp); { char const * description; if (!(description = game_get_description(game))) description = ""; msgtemp = localize(c, "Description: {}", description); } return 0; } static int _handle_ladderactivate_command(t_connection * c, char const *text) { ladders.activate(); message_send_text(c, message_type_info, c, localize(c, "Copied current scores to active scores on all ladders.")); return 0; } static int _handle_rehash_command(t_connection * c, char const *text) { int mode = restart_mode_all; // all by default std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } std::string mode_str = args[1]; if (mode_str == "all") mode = restart_mode_all; else if (mode_str == "i18n") mode = restart_mode_i18n; else if (mode_str == "channels") mode = restart_mode_channels; else if (mode_str == "realms") mode = restart_mode_realms; else if (mode_str == "autoupdate") mode = restart_mode_autoupdate; else if (mode_str == "news") mode = restart_mode_news; else if (mode_str == "versioncheck") mode = restart_mode_versioncheck; else if (mode_str == "ipbans") mode = restart_mode_ipbans; else if (mode_str == "helpfile") mode = restart_mode_helpfile; else if (mode_str == "banners") mode = restart_mode_banners; else if (mode_str == "tracker") mode = restart_mode_tracker; else if (mode_str == "commandgroups") mode = restart_mode_commandgroups; else if (mode_str == "aliasfile") mode = restart_mode_aliasfile; else if (mode_str == "transfile") mode = restart_mode_transfile; else if (mode_str == "tournament") mode = restart_mode_tournament; else if (mode_str == "icons") mode = restart_mode_icons; else if (mode_str == "anongame") mode = restart_mode_anongame; else if (mode_str == "lua") mode = restart_mode_lua; else { message_send_text(c, message_type_info, c, localize(c, "Invalid mode.")); return -1; } server_restart_wraper(mode); msgtemp = localize(c, "Rehash of \"{}\" is complete!", mode_str.c_str()); message_send_text(c, message_type_info, c, msgtemp); return 0; } /** * /find */ static int _handle_find_command(t_connection * c, char const *text) { unsigned int i = 0; t_account *account; char const *tname; t_entry *curr; t_hashtable *accountlist_head = accountlist(); std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); msgtemp = localize(c, " -- name -- similar to {}", text); message_send_text(c, message_type_info, c, msgtemp); HASHTABLE_TRAVERSE(accountlist_head, curr) { if (!curr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL account in list"); } else { account = (t_account *)entry_get_data(curr); if ((tname = accountlist_find_vague_account(account, text)) != NULL) { message_send_text(c, message_type_info, c, tname); return 0; } } } return 0; } /** * Save changes of accounts and clans from the cache to a storage */ static int _handle_save_command(t_connection * c, char const *text) { clanlist_save(); accountlist_save(FS_FORCE | FS_ALL); accountlist_flush(FS_FORCE | FS_ALL); message_send_text(c, message_type_info, c, localize(c, "Pending changes has been saved into the database.")); return 0; } static int _handle_shutdown_command(t_connection * c, char const *text) { char const * dest; unsigned int delay; std::vector args = split_command(text, 1); dest = args[1].c_str(); // delay if (dest[0] == '\0') delay = prefs_get_shutdown_delay(); else if (clockstr_to_seconds(dest, &delay) < 0) { message_send_text(c, message_type_error, c, localize(c, "Invalid delay.")); return -1; } server_quit_delay(delay); if (delay) message_send_text(c, message_type_info, c, localize(c, "You've initialized the shutdown sequence.")); else message_send_text(c, message_type_info, c, localize(c, "You've canceled the shutdown sequence.")); return 0; } static int _handle_ladderinfo_command(t_connection * c, char const *text) { char const * rank_s, *tag_s; unsigned int rank; t_account * account; t_team * team; t_clienttag clienttag; const LadderReferencedObject* referencedObject; LadderList* ladderList; std::vector args = split_command(text, 2); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } rank_s = args[1].c_str(); // rank tag_s = args[2].c_str(); // clienttag if (str_to_uint(rank_s, &rank) < 0 || rank < 1) { message_send_text(c, message_type_error, c, "Invalid rank."); return -1; } if (!(clienttag = tag_validate_client(tag_s))) { if (!(clienttag = conn_get_clienttag(c))) { message_send_text(c, message_type_error, c, localize(c, "Unable to determine client game.")); return -1; } } if (clienttag == CLIENTTAG_STARCRAFT_UINT) { ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, clienttag, ladder_sort_highestrated, ladder_time_active)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "StarCraft active %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_active_wins(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal), account_get_ladder_active_losses(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal), account_get_ladder_active_disconnects(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal), account_get_ladder_active_rating(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "StarCraft active %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, clienttag, ladder_sort_highestrated, ladder_time_current)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "StarCraft current %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_wins(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal), account_get_ladder_losses(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal), account_get_ladder_disconnects(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal), account_get_ladder_rating(account, CLIENTTAG_STARCRAFT_UINT, ladder_id_normal)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "StarCraft current %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); } else if (clienttag == CLIENTTAG_BROODWARS_UINT) { ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, clienttag, ladder_sort_highestrated, ladder_time_active)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "Brood War active %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_active_wins(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal), account_get_ladder_active_losses(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal), account_get_ladder_active_disconnects(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal), account_get_ladder_active_rating(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "Brood War active %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, clienttag, ladder_sort_highestrated, ladder_time_current)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "Brood War current %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_wins(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal), account_get_ladder_losses(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal), account_get_ladder_disconnects(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal), account_get_ladder_rating(account, CLIENTTAG_BROODWARS_UINT, ladder_id_normal)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "Brood War current %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); } else if (clienttag == CLIENTTAG_WARCIIBNE_UINT) { ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, clienttag, ladder_sort_highestrated, ladder_time_active)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II standard active %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_active_wins(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal), account_get_ladder_active_losses(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal), account_get_ladder_active_disconnects(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal), account_get_ladder_active_rating(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II standard active %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, clienttag, ladder_sort_highestrated, ladder_time_active)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II IronMan active %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_active_wins(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman), account_get_ladder_active_losses(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman), account_get_ladder_active_disconnects(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman), account_get_ladder_active_rating(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II IronMan active %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, clienttag, ladder_sort_highestrated, ladder_time_current)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II standard current %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_wins(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal), account_get_ladder_losses(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal), account_get_ladder_disconnects(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal), account_get_ladder_rating(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_normal)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II standard current %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, clienttag, ladder_sort_highestrated, ladder_time_current)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II IronMan current %5u: %-20.20s %u/%u/%u rating %u", rank, account_get_name(account), account_get_ladder_wins(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman), account_get_ladder_losses(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman), account_get_ladder_disconnects(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman), account_get_ladder_rating(account, CLIENTTAG_WARCIIBNE_UINT, ladder_id_ironman)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft II IronMan current %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); } // --> aaron else if (clienttag == CLIENTTAG_WARCRAFT3_UINT || clienttag == CLIENTTAG_WAR3XP_UINT) { ladderList = ladders.getLadderList(LadderKey(ladder_id_solo, clienttag, ladder_sort_default, ladder_time_default)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 Solo %5u: %-20.20s %u/%u/0", rank, account_get_name(account), account_get_ladder_wins(account, clienttag, ladder_id_solo), account_get_ladder_losses(account, clienttag, ladder_id_solo)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 Solo %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_team, clienttag, ladder_sort_default, ladder_time_default)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 Team %5u: %-20.20s %u/%u/0", rank, account_get_name(account), account_get_ladder_wins(account, clienttag, ladder_id_team), account_get_ladder_losses(account, clienttag, ladder_id_team)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 Team %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_ffa, clienttag, ladder_sort_default, ladder_time_default)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (account = referencedObject->getAccount())) { std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 FFA %5u: %-20.20s %u/%u/0", rank, account_get_name(account), account_get_ladder_wins(account, clienttag, ladder_id_ffa), account_get_ladder_losses(account, clienttag, ladder_id_ffa)); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 FFA %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); ladderList = ladders.getLadderList(LadderKey(ladder_id_ateam, clienttag, ladder_sort_default, ladder_time_default)); referencedObject = ladderList->getReferencedObject(rank); if ((referencedObject) && (team = referencedObject->getTeam())) { t_xstr * membernames = xstr_alloc(); for (unsigned char i = 0; i < team_get_size(team); i++){ xstr_cat_str(membernames, account_get_name(team_get_member(team, i))); if ((i)) xstr_cat_char(membernames, ','); } std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 AT Team %5u: %-80.80s %d/%d/0", rank, xstr_get_str(membernames), team_get_wins(team), team_get_losses(team)); xstr_free(membernames); } else std::snprintf(msgtemp0, sizeof(msgtemp0), "WarCraft3 AT Team %5u: ", rank); message_send_text(c, message_type_info, c, msgtemp0); } //<--- else { message_send_text(c, message_type_error, c, localize(c, "This game does not support win/loss records.")); message_send_text(c, message_type_error, c, localize(c, "You must supply a rank and a valid program ID.")); message_send_text(c, message_type_error, c, localize(c, "Example: /ladderinfo 1 STAR")); } return 0; } static int _handle_timer_command(t_connection * c, char const *text) { unsigned int delta; t_timer_data data; char const * delta_s, *msgtext_s; std::vector args = split_command(text, 2); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } delta_s = args[1].c_str(); // timer delta msgtext_s = args[2].c_str(); // message text display when timer elapsed if (clockstr_to_seconds(delta_s, &delta) < 0) { message_send_text(c, message_type_error, c, localize(c, "Invalid duration.")); return -1; } if (msgtext_s[0] == '\0') data.p = xstrdup(localize(c, "Your timer has expired.").c_str()); else data.p = xstrdup(msgtext_s); if (timerlist_add_timer(c, std::time(NULL) + (std::time_t)delta, user_timer_cb, data) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "Could not add timer"); xfree(data.p); message_send_text(c, message_type_error, c, localize(c, "Could not set timer.")); } else { msgtemp = localize(c, "Timer set for {} second(s)", seconds_to_timestr(delta)); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_serverban_command(t_connection *c, char const *text) { char const * username; t_connection * dest_c; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!(dest_c = connlist_find_connection_by_accountname(username))) { message_send_text(c, message_type_error, c, localize(c, "That user is not logged on.")); return -1; } msgtemp = localize(c, "Banning {} who is using IP address {}", conn_get_username(dest_c), addr_num_to_ip_str(conn_get_game_addr(dest_c))); message_send_text(c, message_type_info, c, msgtemp); message_send_text(c, message_type_info, c, localize(c, "User's account is also LOCKED! Only an admin can unlock it!")); msgtemp = localize(c, "/ipban a {}", addr_num_to_ip_str(conn_get_game_addr(dest_c))); handle_ipban_command(c, msgtemp.c_str()); account_set_auth_lock(conn_get_account(dest_c), 1); //now kill the connection msgtemp = localize(c, "You have been banned by Admin: {}", conn_get_username(c)); message_send_text(dest_c, message_type_error, dest_c, msgtemp); message_send_text(dest_c, message_type_error, dest_c, localize(c, "Your account is also LOCKED! Only an admin can UNLOCK it!")); conn_set_state(dest_c, conn_state_destroy); return 0; } static int _handle_netinfo_command(t_connection * c, char const *text) { char const * username; t_connection * conn; t_game const * game; unsigned int addr; unsigned short port; unsigned int taddr; unsigned short tport; std::vector args = split_command(text, 1); username = args[1].c_str(); // username if (username[0] == '\0') username = conn_get_username(c); if (!(conn = connlist_find_connection_by_accountname(username))) { message_send_text(c, message_type_error, c, localize(c, "That user is not logged on.")); return -1; } if (conn_get_account(conn) != conn_get_account(c) && prefs_get_hide_addr() && !(account_get_command_groups(conn_get_account(c)) & command_get_group("/admin-addr"))) // default to false { message_send_text(c, message_type_error, c, localize(c, "Address information for other users is only available to admins.")); return -1; } msgtemp = localize(c, "Server TCP: {} (bind {})", addr_num_to_addr_str(conn_get_real_local_addr(conn), conn_get_real_local_port(conn)), addr_num_to_addr_str(conn_get_local_addr(conn), conn_get_local_port(conn))); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Client TCP: {}", addr_num_to_addr_str(conn_get_addr(conn), conn_get_port(conn))); message_send_text(c, message_type_info, c, msgtemp); taddr = addr = conn_get_game_addr(conn); tport = port = conn_get_game_port(conn); trans_net(conn_get_addr(c), &taddr, &tport); if (taddr == addr && tport == port) msgtemp = localize(c, "Client UDP: {}", addr_num_to_addr_str(addr, port)); else msgtemp = localize(c, "Client UDP: {} (trans {})", addr_num_to_addr_str(addr, port), addr_num_to_addr_str(taddr, tport)); message_send_text(c, message_type_info, c, msgtemp); if ((game = conn_get_game(conn))) { taddr = addr = game_get_addr(game); tport = port = game_get_port(game); trans_net(conn_get_addr(c), &taddr, &tport); if (taddr == addr && tport == port) msgtemp = localize(c, "Game UDP: {}", addr_num_to_addr_str(addr, port)); else msgtemp = localize(c, "Game UDP: {} (trans {})", addr_num_to_addr_str(addr, port), addr_num_to_addr_str(taddr, tport)); } else msgtemp = localize(c, "Game UDP: none"); message_send_text(c, message_type_info, c, msgtemp); return 0; } static int _handle_quota_command(t_connection * c, char const * text) { msgtemp = localize(c, "Your quota allows you to write {} line(s) per {} second(s).", prefs_get_quota_lines(), prefs_get_quota_time()); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "Long lines will be wrapped every {} characters.", prefs_get_quota_wrapline()); message_send_text(c, message_type_info, c, msgtemp); msgtemp = localize(c, "You are not allowed to send lines with more than {} characters.", prefs_get_quota_maxline()); message_send_text(c, message_type_info, c, msgtemp); return 0; } static int _handle_lockacct_command(t_connection * c, char const *text) { t_connection * user; t_account * account; char const * username, *reason = "", *hours = "24"; // default time 24 hours unsigned int sectime; std::vector args = split_command(text, 3); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!args[2].empty()) hours = args[2].c_str(); // hours if (!args[3].empty()) reason = args[3].c_str(); // reason if (!(account = accountlist_find_account(username))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } account_set_auth_lock(account, 1); sectime = (atoi(hours) == 0) ? 0 : (atoi(hours) * 60 * 60) + now; // get unlock time in the future account_set_auth_locktime(account, sectime); account_set_auth_lockreason(account, reason); account_set_auth_lockby(account, conn_get_username(c)); // send message to author msgtemp = localize(c, "Account {} is now locked", account_get_name(account)); msgtemp += account_get_locktext(c, account, false); message_send_text(c, message_type_error, c, msgtemp); // send message to locked user if ((user = connlist_find_connection_by_accountname(username))) { msgtemp = localize(c, "Your account has just been locked"); msgtemp += account_get_locktext(c, account, true); message_send_text(user, message_type_error, user, msgtemp); } return 0; } static int _handle_unlockacct_command(t_connection * c, char const *text) { t_connection * user; t_account * account; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username if (!(account = accountlist_find_account(text))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } if ((user = connlist_find_connection_by_accountname(text))) { msgtemp = localize(c, "Your account has just been unlocked by {}", conn_get_username(c)); message_send_text(user, message_type_info, user, msgtemp); } account_set_auth_lock(account, 0); message_send_text(c, message_type_error, c, localize(c, "That user's account is now unlocked.")); return 0; } static int _handle_muteacct_command(t_connection * c, char const *text) { t_connection * user; t_account * account; char const * username, *reason = "", *hours = "1"; // default time 1 hour unsigned int sectime; std::vector args = split_command(text, 3); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username if (!args[2].empty()) hours = args[2].c_str(); // hours if (!args[3].empty()) reason = args[3].c_str(); // reason if (!(account = accountlist_find_account(username))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } account_set_auth_mute(account, 1); // get unlock time in the future sectime = (atoi(hours) == 0) ? 0 : (atoi(hours) * 60 * 60) + now; account_set_auth_mutetime(account, sectime); account_set_auth_mutereason(account, reason); account_set_auth_muteby(account, conn_get_username(c)); // send message to author msgtemp = localize(c, "Account {} is now muted", account_get_name(account)); msgtemp += account_get_mutetext(c, account, false); message_send_text(c, message_type_error, c, msgtemp); // send message to muted user if ((user = connlist_find_connection_by_accountname(username))) { msgtemp = localize(c, "Your account has just been muted"); msgtemp += account_get_mutetext(c, account, true); message_send_text(user, message_type_error, user, msgtemp); } return 0; } static int _handle_unmuteacct_command(t_connection * c, char const *text) { t_connection * user; t_account * account; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // username if (!(account = accountlist_find_account(text))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } if ((user = connlist_find_connection_by_accountname(text))) { msgtemp = localize(c, "Your account has just been unmuted by {}", conn_get_username(c)); message_send_text(user, message_type_info, user, msgtemp); } account_set_auth_mute(account, 0); message_send_text(c, message_type_error, c, localize(c, "That user's account is now unmuted.")); return 0; } static int _handle_flag_command(t_connection * c, char const *text) { char const * flag_s; unsigned int newflag; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } flag_s = args[1].c_str(); // flag newflag = std::strtoul(flag_s, NULL, 0); conn_set_flags(c, newflag); std::snprintf(msgtemp0, sizeof(msgtemp0), "0x%08x.", newflag); msgtemp = localize(c, "Flags set to {}.", msgtemp0); message_send_text(c, message_type_info, c, msgtemp); return 0; } static int _handle_tag_command(t_connection * c, char const *text) { char const * tag_s; t_clienttag clienttag; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } tag_s = args[1].c_str(); // flag if (clienttag = tag_validate_client(tag_s)) { unsigned int oldflags = conn_get_flags(c); conn_set_clienttag(c, clienttag); if ((clienttag == CLIENTTAG_WARCRAFT3_UINT) || (clienttag == CLIENTTAG_WAR3XP_UINT)) conn_update_w3_playerinfo(c); channel_rejoin(c); conn_set_flags(c, oldflags); channel_update_userflags(c); msgtemp = localize(c, "Client tag set to {}.", tag_s); } else msgtemp = localize(c, "Invalid clienttag {} specified", tag_s); message_send_text(c, message_type_info, c, msgtemp); return 0; } static int _handle_ipscan_command(t_connection * c, char const * text) { /* Description of _handle_ipscan_command --------------------------------------- Finds all currently logged in users with the given ip address. */ t_account * account; t_connection * conn; std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } text = args[1].c_str(); // ip or username std::string ip; if (account = accountlist_find_account(text)) { conn = account_get_conn(account); if (conn) { // conn_get_addr returns int, so there can never be a NULL string construct ip = addr_num_to_ip_str(conn_get_addr(conn)); } else { message_send_text(c, message_type_info, c, localize(c, "Warning: That user is not online, using last known address.")); ip = account_get_ll_ip(account); if (ip.empty()) { message_send_text(c, message_type_error, c, localize(c, "Sorry, no IP address could be retrieved.")); return 0; } } } else { ip = text; } message_send_text(c, message_type_info, c, localize(c, "Scanning online users for IP {}...", ip)); t_elem const * curr; int count = 0; LIST_TRAVERSE_CONST(connlist(), curr) { conn = (t_connection *)elem_get_data(curr); if (!conn) { // got empty element continue; } if (ip.compare(addr_num_to_ip_str(conn_get_addr(conn))) == 0) { std::snprintf(msgtemp0, sizeof(msgtemp0), " %s", conn_get_loggeduser(conn)); message_send_text(c, message_type_info, c, msgtemp0); count++; } } if (count == 0) { message_send_text(c, message_type_error, c, localize(c, "There are no online users with that IP address")); } return 0; } static int _handle_set_command(t_connection * c, char const *text) { t_account * account; char const * username, *key, *value; std::vector args = split_command(text, 3); if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); // username key = args[2].c_str(); // key value = args[3].c_str(); // value // disallow get/set value for password hash and username (hash can be cracked easily, account name should be permanent) if (strcasecmp(key, "bnet\\acct\\passhash1") == 0 || strcasecmp(key, "bnet\\acct\\username") == 0 || strcasecmp(key, "bnet\\acct\\verifier") == 0 || strcasecmp(key, "bnet\\acct\\salt") == 0 || strcasecmp(key, "bnet\\username") == 0) { message_send_text(c, message_type_info, c, localize(c, "Access denied due to security reasons.")); return -1; } if (!(account = accountlist_find_account(username))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } if (*value == '\0') { if (account_get_strattr(account, key)) { msgtemp = localize(c, "Current value of {} is \"{}\"", key, account_get_strattr(account, key)); message_send_text(c, message_type_error, c, msgtemp); } else message_send_text(c, message_type_error, c, localize(c, "Value currently not set")); return 0; } // unset value if (strcasecmp(value, "null") == 0) value = NULL; std::sprintf(msgtemp0, " \"%.64s\" (%.128s = \"%.128s\")", account_get_name(account), key, value); if (account_set_strattr(account, key, value) < 0) { msgtemp = localize(c, "Unable to set key for"); msgtemp += msgtemp0; message_send_text(c, message_type_error, c, msgtemp); } else { msgtemp = localize(c, "Key set successfully for"); msgtemp += msgtemp0; message_send_text(c, message_type_error, c, msgtemp); eventlog(eventlog_level_warn, __FUNCTION__, "Key set by \"{}\" for {}", account_get_name(conn_get_account(c)),msgtemp0); } return 0; } static int _handle_motd_command(t_connection * c, char const *text) { std::string filename = i18n_filename(prefs_get_motdfile(), conn_get_gamelang_localized(c)); std::FILE* fp = std::fopen(filename.c_str(), "r"); if (fp) { message_send_file(c, fp); if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close motd file \"{}\" after reading (std::fopen: {})", filename, std::strerror(errno)); } else { eventlog(eventlog_level_error, __FUNCTION__, "could not open motd file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); message_send_text(c, message_type_error, c, localize(c, "Unable to open motd.")); } return 0; } static int _handle_tos_command(t_connection * c, char const * text) { /* handle /tos - shows terms of service by user request -raistlinthewiz */ std::string filename = i18n_filename(prefs_get_tosfile(), conn_get_gamelang_localized(c)); /* FIXME: if user enters relative path to tos file in config, above routine will fail */ std::FILE* fp = std::fopen(filename.c_str(), "r"); if (fp) { char * buff; unsigned len; while ((buff = file_get_line(fp))) { if ((len = std::strlen(buff)) < MAX_MESSAGE_LEN) { i18n_convert(c, buff); message_send_text(c, message_type_info, c, buff); } else { /* lines in TOS file can be > MAX_MESSAGE_LEN, so split them truncating is not an option for TOS -raistlinthewiz */ while (len > MAX_MESSAGE_LEN - 1) { std::strncpy(msgtemp0, buff, MAX_MESSAGE_LEN - 1); msgtemp0[MAX_MESSAGE_LEN-1] = '\0'; buff += MAX_MESSAGE_LEN - 1; len -= MAX_MESSAGE_LEN - 1; message_send_text(c, message_type_info, c, msgtemp0); } if (len > 0) /* does it exist a small last part ? */ { i18n_convert(c, buff); message_send_text(c, message_type_info, c, buff); } } } if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close tos file \"{}\" after reading (std::fopen: {})", filename, std::strerror(errno)); } else { eventlog(eventlog_level_error, __FUNCTION__, "could not open tos file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); message_send_text(c, message_type_error, c, localize(c, "Unable to send TOS (Terms of Service).")); } return 0; } static int _handle_ping_command(t_connection * c, char const *text) { unsigned int i; t_connection * user; t_game * game; std::vector args = split_command(text, 1); text = args[1].c_str(); // username if (text[0] == '\0') { if ((game = conn_get_game(c))) { for (i = 0; i < game_get_count(game); i++) { if ((user = game_get_player_conn(game, i))) { msgtemp = localize(c, "{} latency: {}", conn_get_username(user), conn_get_latency(user)); message_send_text(c, message_type_info, c, msgtemp); } } return 0; } msgtemp = localize(c, "Your latency {}", conn_get_latency(c)); } else if ((user = connlist_find_connection_by_accountname(text))) msgtemp = localize(c, "{} latency ()", text, conn_get_latency(user)); else { msgtemp = localize(c, "Invalid user."); return -1; } message_send_text(c, message_type_info, c, msgtemp); return 0; } static int _handle_commandgroups_command(t_connection * c, char const * text) { t_account * account; char const * command, *username; unsigned int usergroups; // from user account unsigned int groups = 0; // converted from arg3 char tempgroups[9]; // converted from usergroups std::vector args = split_command(text, 3); // display help if [list] without [username], or not [list] without [groups] if ( ((args[1] == "list" || args[1] == "l") && args[2].empty()) || (!(args[1] == "list" || args[1] == "l") && args[3].empty()) ) { describe_command(c, args[0].c_str()); return -1; } command = args[1].c_str(); // command username = args[2].c_str(); // username //args[3]; // groups if (!(account = accountlist_find_account(username))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } usergroups = account_get_command_groups(account); if (!std::strcmp(command, "list") || !std::strcmp(command, "l")) { if (usergroups & 1) tempgroups[0] = '1'; else tempgroups[0] = ' '; if (usergroups & 2) tempgroups[1] = '2'; else tempgroups[1] = ' '; if (usergroups & 4) tempgroups[2] = '3'; else tempgroups[2] = ' '; if (usergroups & 8) tempgroups[3] = '4'; else tempgroups[3] = ' '; if (usergroups & 16) tempgroups[4] = '5'; else tempgroups[4] = ' '; if (usergroups & 32) tempgroups[5] = '6'; else tempgroups[5] = ' '; if (usergroups & 64) tempgroups[6] = '7'; else tempgroups[6] = ' '; if (usergroups & 128) tempgroups[7] = '8'; else tempgroups[7] = ' '; tempgroups[8] = '\0'; msgtemp = localize(c, "{}'s command group(s): {}", username, tempgroups); message_send_text(c, message_type_info, c, msgtemp); return 0; } // iterate chars in string for (std::string::iterator g = args[3].begin(); g != args[3].end(); ++g) { if (*g == '1') groups |= 1; else if (*g == '2') groups |= 2; else if (*g == '3') groups |= 4; else if (*g == '4') groups |= 8; else if (*g == '5') groups |= 16; else if (*g == '6') groups |= 32; else if (*g == '7') groups |= 64; else if (*g == '8') groups |= 128; else { msgtemp = localize(c, "Got bad group: {}", *g); message_send_text(c, message_type_info, c, msgtemp); return -1; } } if (!std::strcmp(command, "add") || !std::strcmp(command, "a")) { account_set_command_groups(account, usergroups | groups); msgtemp = localize(c, "Groups {} has been added to {}", args[3].c_str(), username); message_send_text(c, message_type_info, c, msgtemp); return 0; } if (!std::strcmp(command, "del") || !std::strcmp(command, "d")) { account_set_command_groups(account, usergroups & (255 - groups)); msgtemp = localize(c, "Groups {} has been deleted from {}", args[3].c_str(), username); message_send_text(c, message_type_info, c, msgtemp); return 0; } msgtemp = localize(c, "Got unknown command: {}", command); message_send_text(c, message_type_info, c, msgtemp); return -1; } static int _handle_topic_command(t_connection * c, char const * text) { std::vector args = split_command(text, 1); std::string topicstr = args[1]; t_channel * channel = conn_get_channel(c); if (channel == nullptr) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } class_topic Topic; char const * channel_name = channel_get_name(channel); // set channel topic if (!topicstr.empty()) { if ((topicstr.size() + 1) > MAX_TOPIC_LEN) { msgtemp = localize(c, "Max topic length exceeded (max {} symbols)", MAX_TOPIC_LEN); message_send_text(c, message_type_error, c, msgtemp); return -1; } if (!(account_is_operator_or_admin(conn_get_account(c), channel_name))) { msgtemp = localize(c, "You must be at least a Channel Operator of {} to set the topic", channel_name); message_send_text(c, message_type_error, c, msgtemp); return -1; } bool do_save; if (channel_get_permanent(channel)) do_save = true; else do_save = false; Topic.set(std::string(channel_name), std::string(topicstr), do_save); } // display channel topic if (Topic.display(c, std::string(channel_name)) == false) { msgtemp = localize(c, "{} topic: no topic", channel_name); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int _handle_moderate_command(t_connection * c, char const * text) { unsigned oldflags; t_channel * channel; if (!(channel = conn_get_channel(c))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } if (!(account_is_operator_or_admin(conn_get_account(c), channel_get_name(channel)))) { message_send_text(c, message_type_error, c, localize(c, "You must be at least a Channel Operator to use this command.")); return -1; } oldflags = channel_get_flags(channel); if (channel_set_flags(channel, oldflags ^ channel_flags_moderated)) { eventlog(eventlog_level_error, __FUNCTION__, "could not set channel {} flags", channel_get_name(channel)); message_send_text(c, message_type_error, c, localize(c, "Unable to change channel flags.")); return -1; } else { if (oldflags & channel_flags_moderated) channel_message_send(channel, message_type_info, c, localize(c, "Channel is now unmoderated.").c_str()); else channel_message_send(channel, message_type_info, c, localize(c, "Channel is now moderated.").c_str()); } return 0; } static void _reset_d1_stats(t_account *account, t_clienttag ctag, t_connection *c) { account_set_normal_level(account, ctag, 0); account_set_normal_strength(account, ctag, 0), account_set_normal_magic(account, ctag, 0), account_set_normal_dexterity(account, ctag, 0), account_set_normal_vitality(account, ctag, 0), account_set_normal_gold(account, ctag, 0); msgtemp = localize(c, "Reset {}'s {} stats", account_get_name(account), clienttag_get_title(ctag)); message_send_text(c, message_type_info, c, msgtemp); } static void _reset_scw2_stats(t_account *account, t_clienttag ctag, t_connection *c) { LadderList* ladderList; unsigned int uid = account_get_uid(account); account_set_normal_wins(account, ctag, 0); account_set_normal_losses(account, ctag, 0); account_set_normal_draws(account, ctag, 0); account_set_normal_disconnects(account, ctag, 0); // normal, current if (account_get_ladder_rating(account, ctag, ladder_id_normal) > 0) { account_set_ladder_wins(account, ctag, ladder_id_normal, 0); account_set_ladder_losses(account, ctag, ladder_id_normal, 0); account_set_ladder_draws(account, ctag, ladder_id_normal, 0); account_set_ladder_disconnects(account, ctag, ladder_id_normal, 0); account_set_ladder_rating(account, ctag, ladder_id_normal, 0); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, ctag, ladder_sort_highestrated, ladder_time_current)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, ctag, ladder_sort_mostwins, ladder_time_current)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, ctag, ladder_sort_mostgames, ladder_time_current)); if (ladderList != NULL) ladderList->delEntry(uid); } // ironman, current if (account_get_ladder_rating(account, ctag, ladder_id_ironman) > 0) { account_set_ladder_wins(account, ctag, ladder_id_ironman, 0); account_set_ladder_losses(account, ctag, ladder_id_ironman, 0); account_set_ladder_draws(account, ctag, ladder_id_ironman, 0); account_set_ladder_disconnects(account, ctag, ladder_id_ironman, 0); account_set_ladder_rating(account, ctag, ladder_id_ironman, 0); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, ctag, ladder_sort_highestrated, ladder_time_current)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, ctag, ladder_sort_mostwins, ladder_time_current)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, ctag, ladder_sort_mostgames, ladder_time_current)); if (ladderList != NULL) ladderList->delEntry(uid); } // normal, active if (account_get_ladder_active_rating(account, ctag, ladder_id_normal) > 0) { account_set_ladder_active_wins(account, ctag, ladder_id_normal, 0); account_set_ladder_active_losses(account, ctag, ladder_id_normal, 0); account_set_ladder_active_draws(account, ctag, ladder_id_normal, 0); account_set_ladder_active_disconnects(account, ctag, ladder_id_normal, 0); account_set_ladder_active_rating(account, ctag, ladder_id_normal, 0); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, ctag, ladder_sort_highestrated, ladder_time_active)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, ctag, ladder_sort_mostwins, ladder_time_active)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_normal, ctag, ladder_sort_mostgames, ladder_time_active)); if (ladderList != NULL) ladderList->delEntry(uid); } // ironman, active if (account_get_ladder_active_rating(account, ctag, ladder_id_ironman) > 0) { account_set_ladder_active_wins(account, ctag, ladder_id_ironman, 0); account_set_ladder_active_losses(account, ctag, ladder_id_ironman, 0); account_set_ladder_active_draws(account, ctag, ladder_id_ironman, 0); account_set_ladder_active_disconnects(account, ctag, ladder_id_ironman, 0); account_set_ladder_active_rating(account, ctag, ladder_id_ironman, 0); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, ctag, ladder_sort_highestrated, ladder_time_active)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, ctag, ladder_sort_mostwins, ladder_time_active)); if (ladderList != NULL) ladderList->delEntry(uid); ladderList = ladders.getLadderList(LadderKey(ladder_id_ironman, ctag, ladder_sort_mostgames, ladder_time_active)); if (ladderList != NULL) ladderList->delEntry(uid); } msgtemp = localize(c, "Reset {}'s {} stats", account_get_name(account), clienttag_get_title(ctag)); message_send_text(c, message_type_info, c, msgtemp); } static void _reset_w3_stats(t_account *account, t_clienttag ctag, t_connection *c) { LadderList* ladderList; unsigned int uid = account_get_uid(account); account_set_ladder_level(account, ctag, ladder_id_solo, 0); account_set_ladder_xp(account, ctag, ladder_id_solo, 0); account_set_ladder_wins(account, ctag, ladder_id_solo, 0); account_set_ladder_losses(account, ctag, ladder_id_solo, 0); account_set_ladder_rank(account, ctag, ladder_id_solo, 0); ladderList = ladders.getLadderList(LadderKey(ladder_id_solo, ctag, ladder_sort_default, ladder_time_default)); if (ladderList != NULL) ladderList->delEntry(uid); account_set_ladder_level(account, ctag, ladder_id_team, 0); account_set_ladder_xp(account, ctag, ladder_id_team, 0); account_set_ladder_wins(account, ctag, ladder_id_team, 0); account_set_ladder_losses(account, ctag, ladder_id_team, 0); account_set_ladder_rank(account, ctag, ladder_id_team, 0); ladderList = ladders.getLadderList(LadderKey(ladder_id_team, ctag, ladder_sort_default, ladder_time_default)); if (ladderList != NULL) ladderList->delEntry(uid); account_set_ladder_level(account, ctag, ladder_id_ffa, 0); account_set_ladder_xp(account, ctag, ladder_id_ffa, 0); account_set_ladder_wins(account, ctag, ladder_id_ffa, 0); account_set_ladder_losses(account, ctag, ladder_id_ffa, 0); account_set_ladder_rank(account, ctag, ladder_id_ffa, 0); ladderList = ladders.getLadderList(LadderKey(ladder_id_ffa, ctag, ladder_sort_default, ladder_time_default)); if (ladderList != NULL) ladderList->delEntry(uid); // this would now need a way to delete the team for all members now //account_set_atteamcount(account,ctag,0); msgtemp = localize(c, "Reset {}'s {} stats", account_get_name(account), clienttag_get_title(ctag)); message_send_text(c, message_type_info, c, msgtemp); } static int _handle_clearstats_command(t_connection *c, char const *text) { char const * username, * tag; unsigned int all; t_account * account; t_clienttag ctag = 0; std::vector args = split_command(text, 2); if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } username = args[1].c_str(); tag = args[2].c_str(); // clienttag account = accountlist_find_account(username); if (!account) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } if (strcasecmp(tag, "all") != 0) { ctag = tag_validate_client(tag); all = 0; } else all = 1; if (all || ctag == CLIENTTAG_DIABLORTL_UINT) _reset_d1_stats(account, CLIENTTAG_DIABLORTL_UINT, c); if (all || ctag == CLIENTTAG_DIABLOSHR_UINT) _reset_d1_stats(account, CLIENTTAG_DIABLOSHR_UINT, c); if (all || ctag == CLIENTTAG_WARCIIBNE_UINT) _reset_scw2_stats(account, CLIENTTAG_WARCIIBNE_UINT, c); if (all || ctag == CLIENTTAG_STARCRAFT_UINT) _reset_scw2_stats(account, CLIENTTAG_STARCRAFT_UINT, c); if (all || ctag == CLIENTTAG_BROODWARS_UINT) _reset_scw2_stats(account, CLIENTTAG_BROODWARS_UINT, c); if (all || ctag == CLIENTTAG_SHAREWARE_UINT) _reset_scw2_stats(account, CLIENTTAG_SHAREWARE_UINT, c); if (all || ctag == CLIENTTAG_WARCRAFT3_UINT) _reset_w3_stats(account, CLIENTTAG_WARCRAFT3_UINT, c); if (all || ctag == CLIENTTAG_WAR3XP_UINT) _reset_w3_stats(account, CLIENTTAG_WAR3XP_UINT, c); ladders.update(); return 0; } /* Send message to all clients (similar to announce, but in messagebox) */ static int _handle_alert_command(t_connection * c, char const * text) { t_clienttag clienttag; t_clienttag clienttag_dest; clienttag = conn_get_clienttag(c); // disallow clients that doesn't support SID_MESSAGEBOX if (clienttag != CLIENTTAG_STARCRAFT_UINT && clienttag != CLIENTTAG_BROODWARS_UINT && clienttag != CLIENTTAG_STARJAPAN_UINT && clienttag != CLIENTTAG_SHAREWARE_UINT && clienttag != CLIENTTAG_DIABLORTL_UINT && clienttag != CLIENTTAG_DIABLOSHR_UINT && clienttag != CLIENTTAG_WARCIIBNE_UINT && clienttag != CLIENTTAG_BNCHATBOT_UINT) { message_send_text(c, message_type_error, c, localize(c, "Your game client doesn't support MessageBox.")); return -1; } std::vector args = split_command(text, 1); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } // reduntant line - it adds a caption to message box std::string goodtext = args[1] + localize(c, "\n\n***************************\nBy {}", conn_get_username(c)); // caption msgtemp = localize(c, "Information from {}", prefs_get_servername()); msgtemp = localize(c, " for {}", prefs_get_servername()); t_connection * conn; t_elem const * curr; // send to online users LIST_TRAVERSE_CONST(connlist(), curr) { if (conn = (t_connection*)elem_get_data(curr)) { clienttag_dest = conn_get_clienttag(conn); if (clienttag_dest != CLIENTTAG_STARCRAFT_UINT && clienttag_dest != CLIENTTAG_BROODWARS_UINT && clienttag_dest != CLIENTTAG_STARJAPAN_UINT && clienttag_dest != CLIENTTAG_SHAREWARE_UINT && clienttag_dest != CLIENTTAG_DIABLORTL_UINT && clienttag_dest != CLIENTTAG_DIABLOSHR_UINT && clienttag_dest != CLIENTTAG_WARCIIBNE_UINT && clienttag_dest != CLIENTTAG_BNCHATBOT_UINT) { continue; } messagebox_show(conn, goodtext.c_str(), msgtemp.c_str()); } } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/command.h000066400000000000000000000022631462640461400176550ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_COMMAND_PROTOS #define INCLUDED_COMMAND_PROTOS #define JUST_NEED_TYPES #include #include #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_command(t_connection * c, char const * text); extern std::vector split_command(char const * text, int args_count); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/command_groups.cpp000066400000000000000000000104131462640461400216030ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define COMMAND_GROUPS_INTERNAL_ACCESS #include "command_groups.h" #include #include #include #include "common/eventlog.h" #include "common/list.h" #include "common/util.h" #include "common/xalloc.h" #include "common/setup_after.h" //#define COMMANDGROUPSDEBUG 1 namespace pvpgn { namespace bnetd { static t_list * command_groups_head = NULL; static std::FILE * fp = NULL; extern int command_groups_load(char const * filename) { unsigned int line; unsigned int pos; char * buff; char * temp; char const * command; unsigned int group; t_command_groups * entry; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } command_groups_head = list_create(); for (line = 1; (buff = file_get_line(fp)); line++) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if ((temp = std::strrchr(buff, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } if (!(temp = std::strtok(buff, " \t"))) { /* std::strtok modifies the string it is passed */ eventlog(eventlog_level_error, __FUNCTION__, "missing group on line {} of file \"{}\"", line, filename); continue; } if (str_to_uint(temp, &group)<0) { eventlog(eventlog_level_error, __FUNCTION__, "group '{}' not a valid group (1-8)", temp); continue; } if (group == 0 || group > 8) { eventlog(eventlog_level_error, __FUNCTION__, "group '{}' not within groups limits (1-8)", group); continue; } while ((command = std::strtok(NULL, " \t"))) { entry = (t_command_groups*)xmalloc(sizeof(t_command_groups)); entry->group = 1 << (group - 1); entry->command = xstrdup(command); list_append_data(command_groups_head, entry); #ifdef COMMANDGROUPSDEBUG eventlog(eventlog_level_info, __FUNCTION__, "Added command: {} - with group {}", entry->command, entry->group); #endif } } file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); return 0; } extern int command_groups_unload(void) { t_elem * curr; t_command_groups * entry; if (command_groups_head) { LIST_TRAVERSE(command_groups_head, curr) { if (!(entry = (t_command_groups*)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); else { xfree(entry->command); xfree(entry); } list_remove_elem(command_groups_head, &curr); } list_destroy(command_groups_head); command_groups_head = NULL; } return 0; } extern unsigned int command_get_group(char const * command) { t_elem const * curr; t_command_groups * entry; if (command_groups_head) { LIST_TRAVERSE(command_groups_head, curr) { if (!(entry = (t_command_groups*)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); else if (!(std::strcmp(entry->command, command))) return entry->group; } } return 0; } extern int command_groups_reload(char const * filename) { command_groups_unload(); return command_groups_load(filename); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/command_groups.h000066400000000000000000000025361462640461400212570ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_COMMAND_GROUPS_TYPES #define INCLUDED_COMMAND_GROUPS_TYPES namespace pvpgn { namespace bnetd { #ifdef COMMAND_GROUPS_INTERNAL_ACCESS typedef struct { char * command; unsigned int group; } t_command_groups; #endif } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_COMMAND_GROUPS_PROTOS #define INCLUDED_COMMAND_GROUPS_PROTOS namespace pvpgn { namespace bnetd { extern int command_groups_load(char const * filename); extern int command_groups_unload(void); extern int command_groups_reload(char const * filename); extern unsigned int command_get_group(char const * command); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/connection.cpp000066400000000000000000003270031462640461400207330ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define CONNECTION_INTERNAL_ACCESS #include "common/setup_before.h" #include "connection.h" #include #include #include #ifdef WIN32_GUI #include #endif #include "compat/strcasecmp.h" #include "compat/strncasecmp.h" #include "compat/socket.h" #include "compat/psock.h" #include "common/eventlog.h" #include "common/addr.h" #include "common/queue.h" #include "common/packet.h" #include "common/tag.h" #include "common/bn_type.h" #include "common/version.h" #include "common/util.h" #include "common/list.h" #include "common/bnet_protocol.h" #include "common/field_sizes.h" #include "common/rcm.h" #include "common/fdwatch.h" #include "common/elist.h" #include "common/xalloc.h" #include "account.h" #include "account_wrap.h" #include "realm.h" #include "channel.h" #include "game.h" #include "tick.h" #include "message.h" #include "prefs.h" #include "watch.h" #include "timer.h" #include "irc.h" #include "ipban.h" #include "game_conv.h" #include "udptest_send.h" #include "character.h" #include "versioncheck.h" #include "anongame.h" #include "clan.h" #include "topic.h" #include "server.h" #include "handle_d2cs.h" #include "command_groups.h" #include "attrlayer.h" #include "anongame_wol.h" #include "icons.h" #include "i18n.h" #include "common/setup_after.h" #include "prefs.h" #ifdef WITH_LUA #include "luainterface.h" #endif namespace pvpgn { namespace bnetd { /* types and data structures used for the connlist array */ typedef struct { t_connection *c; t_elist freelist; } t_conn_entry; t_conn_entry *connarray = NULL; t_elist arrayflist; static int totalcount = 0; static t_list * conn_head = NULL; static t_list * conn_dead = NULL; static void conn_send_welcome(t_connection * c); static void conn_send_issue(t_connection * c); static int connarray_create(void); static void connarray_destroy(void); static t_connection *connarray_get_conn(unsigned index); static unsigned connarray_add_conn(t_connection *c); static void connarray_del_conn(unsigned index); static void conn_send_welcome(t_connection * c) { char const * filename; std::FILE * fp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (c->protocol.cflags & conn_flags_welcomed) return; if ((conn_get_class(c) == conn_class_irc) || (conn_get_class(c) == conn_class_wol)) { c->protocol.cflags |= conn_flags_welcomed; return; } if (filename = prefs_get_motdfile()) { std::string lang_filename = i18n_filename(filename, conn_get_gamelang_localized(c)); if (fp = std::fopen(lang_filename.c_str(), "r")) { message_send_file(c, fp); if (std::fclose(fp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close MOTD file \"{}\" after reading (std::fopen: {})", lang_filename, std::strerror(errno)); } } else { eventlog(eventlog_level_error, __FUNCTION__, "could not open MOTD file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); } } c->protocol.cflags |= conn_flags_welcomed; } static void conn_send_issue(t_connection * c) { char const * filename; std::FILE * fp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if ((filename = prefs_get_issuefile())) if ((fp = std::fopen(filename, "r"))) { message_send_file(c, fp); if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close issue file \"{}\" after reading (std::fopen: {})", filename, std::strerror(errno)); } else eventlog(eventlog_level_error, __FUNCTION__, "could not open issue file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); else eventlog(eventlog_level_debug, __FUNCTION__, "no issue file"); } // [zap-zero] 20020629 extern void conn_shutdown(t_connection * c, std::time_t now, t_timer_data foo) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (now == (std::time_t)0) /* zero means user logged out before expiration */ { eventlog(eventlog_level_trace, __FUNCTION__, "[{}] connection already closed", conn_get_socket(c)); return; } eventlog(eventlog_level_trace, __FUNCTION__, "[{}] closing connection", conn_get_socket(c)); conn_set_state(c, conn_state_destroy); } extern void conn_test_latency(t_connection * c, std::time_t now, t_timer_data delta) { t_packet * packet; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (now == (std::time_t)0) /* zero means user logged out before expiration */ return; if (conn_get_state(c) == conn_state_destroy) // [zap-zero] 20020910 return; // state_destroy: do nothing if ((conn_get_class(c) == conn_class_irc) || (conn_get_class(c) == conn_class_wol)) { /* We should start pinging the client after we received the first line ... */ /* NOTE: RFC2812 only suggests that PINGs are being sent * if no other activity is detected. However it explecitly * allows PINGs to be sent if there is activity on this * connection. In other words we just don't care :) */ if (conn_get_ircping(c) != 0) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] ping timeout (closing connection)", conn_get_socket(c)); conn_set_latency(c, 0); conn_set_state(c, conn_state_destroy); } irc_send_ping(c); } else if (conn_get_class(c) == conn_class_w3route) { if (!(packet = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet_create failed", conn_get_socket(c)); } else { packet_set_size(packet, sizeof(t_server_w3route_echoreq)); packet_set_type(packet, SERVER_W3ROUTE_ECHOREQ); bn_int_set(&packet->u.server_w3route_echoreq.ticks, get_ticks()); conn_push_outqueue(c, packet); packet_del_ref(packet); } } else { /* FIXME: I think real Battle.net sends these even before login */ if (!conn_get_game(c)) { if ((packet = packet_create(packet_class_bnet))) { packet_set_size(packet, sizeof(t_server_echoreq)); packet_set_type(packet, SERVER_ECHOREQ); bn_int_set(&packet->u.server_echoreq.ticks, get_ticks()); conn_push_outqueue(c, packet); packet_del_ref(packet); } else { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); } } } if (timerlist_add_timer(c, now + (std::time_t)delta.n, conn_test_latency, delta) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not add timer"); } static void conn_send_nullmsg(t_connection * c, std::time_t now, t_timer_data delta) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (now == (std::time_t)0) /* zero means user logged out before expiration */ return; message_send_text(c, message_type_null, c, NULL); if (timerlist_add_timer(c, now + (std::time_t)delta.n, conn_send_nullmsg, delta) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not add timer"); } extern char const * conn_class_get_str(t_conn_class cclass) { switch (cclass) { case conn_class_init: return "init"; case conn_class_bnet: return "bnet"; case conn_class_file: return "file"; case conn_class_bot: return "bot"; case conn_class_d2cs_bnetd: return "d2cs_bnetd"; case conn_class_telnet: return "telnet"; case conn_class_irc: return "irc"; case conn_class_wol: return "wol"; case conn_class_wserv: return "wserv"; case conn_class_apireg: return "apireg"; case conn_class_wgameres: return "wgameres"; case conn_class_wladder: return "wladder"; case conn_class_none: return "none"; case conn_class_w3route: return "w3route"; default: return "UNKNOWN"; } } extern char const * conn_state_get_str(t_conn_state state) { switch (state) { case conn_state_empty: return "empty"; case conn_state_initial: return "initial"; case conn_state_connected: return "connected"; case conn_state_bot_username: return "bot_username"; case conn_state_bot_password: return "bot_password"; case conn_state_loggedin: return "loggedin"; case conn_state_destroy: return "destroy"; case conn_state_untrusted: return "untrusted"; case conn_state_pending_raw: return "pending_raw"; default: return "UNKNOWN"; } } extern int conn_set_realm_cb(void *data, void *newref); extern t_connection * conn_create(int tsock, int usock, unsigned int real_local_addr, unsigned short real_local_port, unsigned int local_addr, unsigned short local_port, unsigned int addr, unsigned short port) { t_connection * temp; if (tsock < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad TCP socket {}", tsock); return NULL; } if (usock < -1) /* -1 is allowed for some connection classes like bot, irc, and telnet */ { eventlog(eventlog_level_error, __FUNCTION__, "got bad UDP socket {}", usock); return NULL; } temp = (t_connection*)xmalloc(sizeof(t_connection)); temp->socket.tcp_sock = tsock; temp->socket.tcp_addr = addr; temp->socket.tcp_port = port; temp->socket.udp_sock = usock; temp->socket.udp_addr = addr; /* same for now but client can request it to be different */ temp->socket.local_addr = local_addr; temp->socket.local_port = local_port; temp->socket.real_local_addr = real_local_addr; temp->socket.real_local_port = real_local_port; temp->socket.udp_port = port; temp->socket.fdw_idx = -1; temp->protocol.cclass = conn_class_init; temp->protocol.state = conn_state_initial; temp->protocol.sessionkey = ((unsigned int)std::rand()) ^ ((unsigned int)now + (unsigned int)real_local_port); temp->protocol.sessionnum = connarray_add_conn(temp); temp->protocol.secret = ((unsigned int)std::rand()) ^ (totalcount + ((unsigned int)now)); temp->protocol.flags = MF_PLUG; temp->protocol.latency = 0; temp->protocol.chat.dnd = NULL; temp->protocol.chat.away = NULL; temp->protocol.chat.ignore_list = NULL; temp->protocol.chat.ignore_count = 0; temp->protocol.chat.quota.totcount = 0; temp->protocol.chat.quota.list = list_create(); temp->protocol.client.versionid = 0; temp->protocol.client.gameversion = 0; temp->protocol.client.checksum = 0; temp->protocol.client.archtag = 0; temp->protocol.client.clienttag = 0; temp->protocol.client.clientver = NULL; temp->protocol.client.gamelang = 0; temp->protocol.client.country = NULL; temp->protocol.client.tzbias = 0; temp->protocol.client.host = NULL; temp->protocol.client.user = NULL; temp->protocol.client.clientexe = NULL; temp->protocol.client.owner = NULL; temp->protocol.client.cdkey = NULL; temp->protocol.client.versioncheck = nullptr; temp->protocol.account = NULL; temp->protocol.chat.channel = NULL; temp->protocol.chat.last_message = now; temp->protocol.chat.lastsender = NULL; temp->protocol.chat.irc.ircline = NULL; temp->protocol.chat.irc.ircping = 0; temp->protocol.chat.irc.ircpass = NULL; temp->protocol.chat.tmpOP_channel = NULL; temp->protocol.chat.tmpVOICE_channel = NULL; temp->protocol.game = NULL; temp->protocol.queues.outqueue = NULL; temp->protocol.queues.outsize = 0; temp->protocol.queues.outsizep = 0; temp->protocol.queues.inqueue = NULL; temp->protocol.queues.insize = 0; temp->protocol.loggeduser = NULL; temp->protocol.d2.realm = NULL; rcm_regref_init(&temp->protocol.d2.realm_regref, &conn_set_realm_cb, temp); temp->protocol.d2.character = NULL; temp->protocol.d2.realminfo = NULL; temp->protocol.d2.charname = NULL; temp->protocol.w3.w3_playerinfo = NULL; temp->protocol.w3.routeconn = NULL; temp->protocol.w3.anongame = NULL; temp->protocol.w3.anongame_search_starttime = 0; temp->protocol.w3.client_proof = NULL; temp->protocol.w3.server_proof = NULL; temp->protocol.bound = NULL; elist_init(&temp->protocol.timers); temp->protocol.wol.ingame = 0; temp->protocol.wol.codepage = 0; temp->protocol.wol.pageme = true; temp->protocol.wol.findme = true; temp->protocol.wol.apgar = NULL; temp->protocol.wol.anongame_player = NULL; temp->protocol.cr_time = now; temp->protocol.passfail_count = 0; temp->protocol.cflags = 0; list_prepend_data(conn_head, temp); eventlog(eventlog_level_debug, __FUNCTION__, "[{}][{}] sessionkey=0x{:08} sessionnum=0x{:08}", temp->socket.tcp_sock, temp->socket.udp_sock, temp->protocol.sessionkey, temp->protocol.sessionnum); return temp; } extern t_anongame * conn_create_anongame(t_connection *c) { t_anongame * temp; int i; if (c->protocol.w3.anongame) { eventlog(eventlog_level_error, __FUNCTION__, "anongame already allocated"); return c->protocol.w3.anongame; } temp = (t_anongame*)xmalloc(sizeof(t_anongame)); temp->count = 0; temp->id = 0; temp->tid = 0; for (i = 0; i < ANONGAME_MAX_GAMECOUNT / 2; i++) temp->tc[i] = NULL; temp->race = 0; temp->playernum = 0; temp->handle = 0; temp->addr = 0; temp->loaded = 0; temp->joined = 0; temp->map_prefs = 0xffffffff; temp->type = 0; temp->gametype = 0; temp->queue = 0; temp->info = NULL; c->protocol.w3.anongame = temp; return temp; } extern t_anongame * conn_get_anongame(t_connection *c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.w3.anongame; } extern void conn_destroy_anongame(t_connection *c) { t_anongame * a; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!(a = c->protocol.w3.anongame)) { eventlog(eventlog_level_error, __FUNCTION__, "NULL anongame"); return; } // delete reference to this connection if (a->info) { a->info->player[a->playernum - 1] = NULL; if (--(a->info->currentplayers) == 0) anongameinfo_destroy(a->info); } // [quetzal] 20020824 // unqueue from anongame search list, // if we got AT game, unqueue entire team. if (anongame_arranged(a->queue)) { anongame_unqueue(a->tc[0], a->queue); } else { anongame_unqueue(c, a->queue); } xfree(c->protocol.w3.anongame); c->protocol.w3.anongame = NULL; } extern void conn_destroy(t_connection * c, t_elem ** elem, int conn_or_dead_list) { char const * classstr; t_elem * curr; if (c == NULL) { eventlog(eventlog_level_error, "conn_destroy", "got NULL connection"); return; } classstr = conn_class_get_str(c->protocol.cclass); if (list_remove_data(conn_head, c, (conn_or_dead_list) ? &curr : elem) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); return; } if (c->protocol.cclass == conn_class_d2cs_bnetd) { t_realm * realm; realm = conn_get_realm(c); if (realm) realm_deactive(realm); else { eventlog(eventlog_level_error, __FUNCTION__, "could not find realm for d2cs connection"); } } else if (c->protocol.cclass == conn_class_w3route && c->protocol.w3.routeconn && c->protocol.w3.routeconn->protocol.w3.anongame) { anongame_stats(c); conn_destroy_anongame(c->protocol.w3.routeconn); // [zap-zero] destroy anongame too when game connection is invalid } if (c->protocol.d2.realm) { realm_add_player_number(c->protocol.d2.realm, -1); realm_put(c->protocol.d2.realm, &c->protocol.d2.realm_regref); } /* free the memory with user quota */ { t_qline * qline; LIST_TRAVERSE(c->protocol.chat.quota.list, curr) { qline = (t_qline*)elem_get_data(curr); xfree(qline); list_remove_elem(c->protocol.chat.quota.list, &curr); } list_destroy(c->protocol.chat.quota.list); } /* if this user in a channel, notify everyone that the user has left */ if (c->protocol.chat.channel) channel_del_connection(c->protocol.chat.channel, c, message_type_quit, NULL); if ((c->protocol.game) && (c->protocol.account)) { if (game_get_status(c->protocol.game) == game_status_started) { game_set_self_report(c->protocol.game, c->protocol.account, game_result_disconnect); game_set_report(c->protocol.game, c->protocol.account, "disconnect", "disconnect"); } } conn_set_game(c, NULL, NULL, NULL, game_type_none, 0); c->protocol.state = conn_state_empty; watchlist->del(c); timerlist_del_all_timers(c); clanmember_set_offline(c); if (c->protocol.account) watchlist->dispatch(c->protocol.account, NULL, c->protocol.client.clienttag, Watch::ET_logout); if (c->protocol.chat.lastsender) xfree((void *)c->protocol.chat.lastsender); /* avoid warning */ if (c->protocol.chat.away) xfree((void *)c->protocol.chat.away); /* avoid warning */ if (c->protocol.chat.dnd) xfree((void *)c->protocol.chat.dnd); /* avoid warning */ if (c->protocol.chat.tmpOP_channel) xfree((void *)c->protocol.chat.tmpOP_channel); /* avoid warning */ if (c->protocol.chat.tmpVOICE_channel) xfree((void *)c->protocol.chat.tmpVOICE_channel); /* avoid warning */ if (c->protocol.client.clientver) xfree((void *)c->protocol.client.clientver); /* avoid warning */ if (c->protocol.client.country) xfree((void *)c->protocol.client.country); /* avoid warning */ if (c->protocol.client.host) xfree((void *)c->protocol.client.host); /* avoid warning */ if (c->protocol.client.user) xfree((void *)c->protocol.client.user); /* avoid warning */ if (c->protocol.client.clientexe) xfree((void *)c->protocol.client.clientexe); /* avoid warning */ if (c->protocol.client.owner) xfree((void *)c->protocol.client.owner); /* avoid warning */ if (c->protocol.client.cdkey) xfree((void *)c->protocol.client.cdkey); /* avoid warning */ if (c->protocol.d2.realminfo) xfree((void *)c->protocol.d2.realminfo); /* avoid warning */ if (c->protocol.d2.charname) xfree((void *)c->protocol.d2.charname); /* avoid warning */ if (c->protocol.chat.irc.ircline) xfree((void *)c->protocol.chat.irc.ircline); /* avoid warning */ if (c->protocol.chat.irc.ircpass) xfree((void *)c->protocol.chat.irc.ircpass); /* avoid warning */ if (c->protocol.wol.apgar) xfree((void *)c->protocol.wol.apgar); /* avoid warning */ if (c->protocol.wol.anongame_player) anongame_wol_destroy(c); /* ADDED BY UNDYING SOULZZ 4/8/02 */ if (c->protocol.w3.w3_playerinfo) xfree((void *)c->protocol.w3.w3_playerinfo); /* avoid warning */ if (c->protocol.w3.client_proof) xfree((void *)c->protocol.w3.client_proof); /* avoid warning */ if (c->protocol.w3.server_proof) xfree((void *)c->protocol.w3.server_proof); /* avoid warning */ if (c->protocol.bound) c->protocol.bound->protocol.bound = NULL; if (c->protocol.chat.ignore_count > 0) { if (!c->protocol.chat.ignore_list) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL ignore_list with ignore_count={}", c->protocol.chat.ignore_count); } else { xfree(c->protocol.chat.ignore_list); } } if (c->protocol.account) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" logged out", c->socket.tcp_sock, conn_get_loggeduser(c)); //amadeo #ifdef WIN32_GUI guiOnUpdateUserList(); #endif if (prefs_get_sync_on_logoff()) { if (account_save(conn_get_account(c), FS_FORCE) < 0) eventlog(eventlog_level_error, __FUNCTION__, "cannot sync account (sync_on_logoff)"); } #ifdef WITH_LUA lua_handle_user(c, NULL, NULL, luaevent_user_disconnect); #endif if (account_get_conn(c->protocol.account) == c) /* make sure you don't set this when already on new conn (relogin with same account) */ account_set_conn(c->protocol.account, NULL); c->protocol.account = NULL; /* the account code will free the memory later */ } /* logged user is no longer only for logged in users */ if (c->protocol.loggeduser) xfree((void*)c->protocol.loggeduser); /* make sure the connection is closed */ if (c->socket.tcp_sock != -1) { /* -1 means that the socket was already closed by conn_close() */ fdwatch_del_fd(c->socket.fdw_idx); psock_shutdown(c->socket.tcp_sock, PSOCK_SHUT_RDWR); psock_close(c->socket.tcp_sock); } /* clear out the packet queues */ if (c->protocol.queues.inqueue) packet_del_ref(c->protocol.queues.inqueue); queue_clear(&c->protocol.queues.outqueue); // [zap-zero] 20020601 if (c->protocol.w3.routeconn) { c->protocol.w3.routeconn->protocol.w3.routeconn = NULL; if (c->protocol.w3.routeconn->protocol.cclass == conn_class_w3route) conn_set_state(c->protocol.w3.routeconn, conn_state_destroy); } if (c->protocol.w3.anongame) conn_destroy_anongame(c); /* delete the conn from the dead list if its there, we dont check for error * because connections may be destroyed without first setting state to destroy */ if (conn_dead) list_remove_data(conn_dead, c, (conn_or_dead_list) ? elem : &curr); connarray_del_conn(c->protocol.sessionnum); eventlog(eventlog_level_info, __FUNCTION__, "[{}] closed {} connection", c->socket.tcp_sock, classstr); xfree(c); } extern int conn_match(t_connection const * c, char const * username) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!username) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL username"); return -1; } if (!c->protocol.account) return 0; return account_match(c->protocol.account, username); } extern t_conn_class conn_get_class(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return conn_class_none; } return c->protocol.cclass; } extern void conn_set_class(t_connection * c, t_conn_class cclass) { t_timer_data data; unsigned long delta; t_conn_class oldclass; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (c->protocol.cclass == cclass) return; oldclass = c->protocol.cclass; c->protocol.cclass = cclass; switch (cclass) { case conn_class_bnet: if (prefs_get_udptest_port() != 0) conn_set_game_port(c, (unsigned short)prefs_get_udptest_port()); udptest_send(c); /* remove any init timers */ if (oldclass == conn_class_init) timerlist_del_all_timers(c); delta = prefs_get_latency(); data.n = delta; if (timerlist_add_timer(c, now + (std::time_t)delta, conn_test_latency, data) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not add timer"); eventlog(eventlog_level_debug, __FUNCTION__, "added latency check timer"); break; case conn_class_w3route: delta = prefs_get_latency(); data.n = delta; if (timerlist_add_timer(c, now + (std::time_t)delta, conn_test_latency, data)<0) eventlog(eventlog_level_error, __FUNCTION__, "could not add timer"); break; case conn_class_bot: case conn_class_telnet: { t_packet * rpacket; if (cclass == conn_class_bot) { if ((delta = prefs_get_nullmsg())>0) { data.n = delta; if (timerlist_add_timer(c, now + (std::time_t)delta, conn_send_nullmsg, data) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not add timer"); } } /* remove any init timers */ if (oldclass == conn_class_init) timerlist_del_all_timers(c); conn_send_issue(c); if (!(rpacket = packet_create(packet_class_raw))) eventlog(eventlog_level_error, __FUNCTION__, "could not create rpacket"); else { packet_append_ntstring(rpacket, "Username: "); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } break; } default: /* remove any init timers */ if (oldclass == conn_class_init) timerlist_del_all_timers(c); break; } } extern t_conn_state conn_get_state(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return conn_state_empty; } return c->protocol.state; } extern void conn_set_state(t_connection * c, t_conn_state state) { t_elem * elem; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } /* special case for destroying connections, add them to conn_dead list */ if (state == conn_state_destroy && c->protocol.state != conn_state_destroy) { if (!conn_dead) conn_dead = list_create(); list_append_data(conn_dead, c); } else if (state != conn_state_destroy && c->protocol.state == conn_state_destroy) if (list_remove_data(conn_dead, c, &elem)) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove dead connection"); return; } c->protocol.state = state; } extern unsigned int conn_get_sessionkey(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.sessionkey; } extern unsigned int conn_get_sessionnum(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.sessionnum; } extern unsigned int conn_get_secret(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.secret; } extern unsigned int conn_get_addr(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.tcp_addr; } extern unsigned short conn_get_port(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.tcp_port; } extern unsigned int conn_get_local_addr(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.local_addr; } extern unsigned short conn_get_local_port(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.local_port; } extern unsigned int conn_get_real_local_addr(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.real_local_addr; } extern unsigned short conn_get_real_local_port(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.real_local_port; } extern unsigned int conn_get_game_addr(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.udp_addr; } extern int conn_set_game_addr(t_connection * c, unsigned int game_addr) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->socket.udp_addr = game_addr; return 0; } extern unsigned short conn_get_game_port(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.udp_port; } extern int conn_set_game_port(t_connection * c, unsigned short game_port) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->socket.udp_port = game_port; return 0; } extern void conn_set_host(t_connection * c, char const * host) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!host) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL host"); return; } if (c->protocol.client.host) xfree((void *)c->protocol.client.host); /* avoid warning */ c->protocol.client.host = xstrdup(host); } extern void conn_set_user(t_connection * c, char const * user) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!user) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL user"); return; } if (c->protocol.client.user) xfree((void *)c->protocol.client.user); /* avoid warning */ c->protocol.client.user = xstrdup(user); } extern void conn_set_owner(t_connection * c, char const * owner) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!owner) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL owner"); return; } if (c->protocol.client.owner) xfree((void *)c->protocol.client.owner); /* avoid warning */ c->protocol.client.owner = xstrdup(owner); } extern const char * conn_get_user(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.client.user; } extern const char * conn_get_owner(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.client.owner; } extern void conn_set_cdkey(t_connection * c, char const * cdkey) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!cdkey) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL cdkey"); return; } if (c->protocol.client.cdkey) xfree((void *)c->protocol.client.cdkey); /* avoid warning */ c->protocol.client.cdkey = xstrdup(cdkey); } extern char const * conn_get_clientexe(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if (!c->protocol.client.clientexe) return ""; return c->protocol.client.clientexe; } extern void conn_set_clientexe(t_connection * c, char const * clientexe) { char const * temp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!clientexe) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clientexe"); return; } temp = xstrdup(clientexe); if (c->protocol.client.clientexe) xfree((void *)c->protocol.client.clientexe); /* avoid warning */ c->protocol.client.clientexe = temp; } extern char const * conn_get_clientver(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if (!c->protocol.client.clientver) return ""; return c->protocol.client.clientver; } extern void conn_set_clientver(t_connection * c, char const * clientver) { char const * temp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!clientver) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clientver"); return; } temp = xstrdup(clientver); if (c->protocol.client.clientver) xfree((void *)c->protocol.client.clientver); /* avoid warning */ c->protocol.client.clientver = temp; } extern t_tag conn_get_archtag(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; /* unknown */ } return c->protocol.client.archtag; } extern void conn_set_archtag(t_connection * c, t_tag archtag) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!tag_check_arch(archtag)) { eventlog(eventlog_level_error, __FUNCTION__, "got UNKNOWN archtag"); return; } if (c->protocol.client.archtag != archtag) c->protocol.client.archtag = archtag; } extern t_tag conn_get_gamelang(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.client.gamelang; } extern void conn_set_gamelang(t_connection * c, t_tag gamelang) { char gamelang_str[5]; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!gamelang) return; /* only war3 & w3xp have gamelang */ bool found; language_find_by_tag(gamelang, found); if (!found) { eventlog(eventlog_level_warn, __FUNCTION__, "got UNKNOWN gamelang"); return; } if (c->protocol.client.gamelang != gamelang) eventlog(eventlog_level_info, __FUNCTION__, "[{}] setting client gamelang to \"{}\"", conn_get_socket(c), tag_uint_to_str(gamelang_str, gamelang)); c->protocol.client.gamelang = gamelang; } extern t_clienttag conn_get_clienttag(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return CLIENTTAG_UNKNOWN_UINT; } if (!c->protocol.client.clienttag) return CLIENTTAG_UNKNOWN_UINT; return c->protocol.client.clienttag; } extern t_clienttag conn_get_fake_clienttag(t_connection const * c) { char const * clienttag; t_account * account; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; /* unknown */ } account = conn_get_account(c); if (account) /* BITS remote connections don't need to have an account */ if ((clienttag = account_get_strattr(account, "BNET\\fakeclienttag"))) return tag_str_to_uint(clienttag); return c->protocol.client.clienttag; } extern void conn_set_clienttag(t_connection * c, t_clienttag clienttag) { char clienttag_str[5]; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!tag_check_client(clienttag)) { eventlog(eventlog_level_error, __FUNCTION__, "got UNKNOWN clienttag \"{}\"", tag_uint_to_str(clienttag_str, clienttag)); return; } if (c->protocol.client.clienttag != clienttag) { c->protocol.client.clienttag = clienttag; if (c->protocol.chat.channel) channel_update_userflags(c); } } extern unsigned long conn_get_gameversion(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.client.gameversion; } extern int conn_set_gameversion(t_connection * c, unsigned long gameversion) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->protocol.client.gameversion = gameversion; return 0; } extern unsigned long conn_get_checksum(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.client.checksum; } extern int conn_set_checksum(t_connection * c, unsigned long checksum) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->protocol.client.checksum = checksum; return 0; } extern unsigned long conn_get_versionid(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.client.versionid; } extern int conn_set_versionid(t_connection * c, unsigned long versionid) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->protocol.client.versionid = versionid; return 0; } extern int conn_get_tzbias(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.client.tzbias; } extern void conn_set_tzbias(t_connection * c, int tzbias) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } c->protocol.client.tzbias = tzbias; } static void conn_set_account(t_connection * c, t_account * account) { t_connection * other; char const * tname; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return; } if ((other = connlist_find_connection_by_accountname((tname = account_get_name(account))))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] forcing logout of previous login for \"{}\"", conn_get_socket(c), tname); conn_set_state(other, conn_state_destroy); } c->protocol.account = account; c->protocol.state = conn_state_loggedin; account_set_conn(account, c); { char const * flagstr; if ((flagstr = account_get_strattr(account, "BNET\\flags\\initial"))) conn_add_flags(c, std::strtoul(flagstr, NULL, 0)); } account_set_ll_time(c->protocol.account, (unsigned int)now); account_set_ll_owner(c->protocol.account, c->protocol.client.owner); account_set_ll_clienttag(c->protocol.account, c->protocol.client.clienttag); account_set_ll_ip(c->protocol.account, addr_num_to_ip_str(c->socket.tcp_addr)); if (c->protocol.client.host) { xfree((void *)c->protocol.client.host); /* avoid warning */ c->protocol.client.host = NULL; } if (c->protocol.client.user) { xfree((void *)c->protocol.client.user); /* avoid warning */ c->protocol.client.user = NULL; } if (c->protocol.client.clientexe) { xfree((void *)c->protocol.client.clientexe); /* avoid warning */ c->protocol.client.clientexe = NULL; } if (c->protocol.client.owner) { xfree((void *)c->protocol.client.owner); /* avoid warning */ c->protocol.client.owner = NULL; } if (c->protocol.client.cdkey) { xfree((void *)c->protocol.client.cdkey); /* avoid warning */ c->protocol.client.cdkey = NULL; } clanmember_set_online(c); totalcount++; watchlist->dispatch(c->protocol.account, NULL, c->protocol.client.clienttag, Watch::ET_login); return; } extern void conn_login(t_connection *c, t_account *a, const char *loggeduser) { assert(c != NULL); assert(a != NULL); assert(loggeduser != NULL); conn_set_account(c, a); if (std::strcmp(conn_get_loggeduser(c), loggeduser)) conn_set_loggeduser(c, loggeduser); } extern t_account * conn_get_account(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.account; } extern int conn_set_loggeduser(t_connection * c, char const * username) { const char * temp; assert(c != NULL); assert(username != NULL); if (username[0] != '#') temp = xstrdup(username); else { unsigned int userid = 0; str_to_uint(&username[1], &userid); if (userid != 0) { if (prefs_get_account_force_username()) { t_account* account = accountlist_find_account_by_uid(userid); temp = xstrdup(account_get_name(account)); } else temp = xstrdup(std::string("#" + userid).c_str()); } else { //theoretically this should never happen... eventlog(eventlog_level_error, __FUNCTION__, "got invalid numeric uid \"{}\"", username); // set value that would have been set prior to this bugfix... temp = xstrdup(username); } } if (c->protocol.loggeduser) xfree((void*)c->protocol.loggeduser); c->protocol.loggeduser = temp; return 0; } extern char const * conn_get_loggeduser(t_connection const * c) { assert(c != NULL); if (!c->protocol.loggeduser && c->protocol.account) return account_get_name(c->protocol.account); return c->protocol.loggeduser; } extern unsigned int conn_get_flags(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.flags; } extern int conn_set_flags(t_connection * c, unsigned int flags) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (flags != c->protocol.flags) { c->protocol.flags = flags; if (c->protocol.chat.channel) channel_update_userflags(c); } return 0; } extern void conn_add_flags(t_connection * c, unsigned int flags) { unsigned int oldflags; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } oldflags = c->protocol.flags; c->protocol.flags |= flags; if (oldflags != c->protocol.flags && c->protocol.chat.channel) channel_update_userflags(c); } extern void conn_del_flags(t_connection * c, unsigned int flags) { unsigned int oldflags; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } oldflags = c->protocol.flags; c->protocol.flags &= ~flags; if (oldflags != c->protocol.flags && c->protocol.chat.channel) channel_update_userflags(c); } extern unsigned int conn_get_latency(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.latency; } extern void conn_set_latency(t_connection * c, unsigned int ms) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (c->protocol.latency != ms) { c->protocol.latency = ms; if (c->protocol.chat.channel) channel_update_latency(c); } } extern char const * conn_get_awaystr(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.chat.away; } extern int conn_set_awaystr(t_connection * c, char const * away) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (c->protocol.chat.away) xfree((void *)c->protocol.chat.away); /* avoid warning */ if (!away) c->protocol.chat.away = NULL; else c->protocol.chat.away = xstrdup(away); return 0; } extern char const * conn_get_dndstr(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.chat.dnd; } extern int conn_set_dndstr(t_connection * c, char const * dnd) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (c->protocol.chat.dnd) xfree((void *)c->protocol.chat.dnd); /* avoid warning */ if (!dnd) c->protocol.chat.dnd = NULL; else c->protocol.chat.dnd = xstrdup(dnd); return 0; } extern int conn_add_ignore(t_connection * c, t_account * account) { t_account * * newlist; t_connection *dest_c; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } newlist = (t_account**)xrealloc(c->protocol.chat.ignore_list, sizeof(t_account const *)*(c->protocol.chat.ignore_count + 1)); newlist[c->protocol.chat.ignore_count++] = account; c->protocol.chat.ignore_list = newlist; dest_c = account_get_conn(account); if (dest_c) { t_message *message; message = message_create(message_type_userflags, dest_c, NULL); if (!message) return 0; message_send(message, c); message_destroy(message); } return 0; } extern int conn_del_ignore(t_connection * c, t_account const * account) { t_account * * newlist; t_account * temp; unsigned int i; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } for (i = 0; i < c->protocol.chat.ignore_count; i++) if (c->protocol.chat.ignore_list[i] == account) break; if (i == c->protocol.chat.ignore_count) return -1; /* not in list */ /* swap entry to be deleted with last entry */ temp = c->protocol.chat.ignore_list[c->protocol.chat.ignore_count - 1]; c->protocol.chat.ignore_list[c->protocol.chat.ignore_count - 1] = c->protocol.chat.ignore_list[i]; c->protocol.chat.ignore_list[i] = temp; if (c->protocol.chat.ignore_count == 1) /* some realloc()s are buggy */ { xfree(c->protocol.chat.ignore_list); newlist = NULL; } else newlist = (t_account**)xrealloc(c->protocol.chat.ignore_list, sizeof(t_account const *)*(c->protocol.chat.ignore_count - 1)); c->protocol.chat.ignore_count--; c->protocol.chat.ignore_list = newlist; return 0; } extern int conn_add_watch(t_connection * c, t_account * account, t_clienttag clienttag) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } watchlist->add(c, account, clienttag, Watch::ET_login | Watch::ET_logout | Watch::ET_joingame | Watch::ET_leavegame); return 0; } extern int conn_del_watch(t_connection * c, t_account * account, t_clienttag clienttag) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (watchlist->del(c, account, clienttag, Watch::ET_login | Watch::ET_logout | Watch::ET_joingame | Watch::ET_leavegame) < 0) return -1; return 0; } extern int conn_check_ignoring(t_connection const * c, char const * me) { unsigned int i; t_account * temp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!me || !(temp = accountlist_find_account(me))) return -1; if (c->protocol.chat.ignore_list) for (i = 0; i < c->protocol.chat.ignore_count; i++) if (c->protocol.chat.ignore_list[i] == temp) return 1; return 0; } extern t_channel * conn_get_channel(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.chat.channel; } extern int conn_set_channel_var(t_connection * c, t_channel * channel) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->protocol.chat.channel = channel; return 0; } extern int conn_set_channel(t_connection * c, char const * channelname) { t_channel * channel; t_channel * oldchannel; t_account * acc; t_elem * curr; int clantag = 0; t_clan * clan = NULL; t_clanmember * member = NULL; unsigned int created; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } acc = c->protocol.account; if (!acc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!channelname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channelname"); return -1; } oldchannel = c->protocol.chat.channel; channel = channellist_find_channel_by_name(channelname, conn_get_country(c), realm_get_name(conn_get_realm(c))); if (channel && (channel == oldchannel)) return 0; if ((strncasecmp(channelname, "clan ", 5) == 0) && (std::strlen(channelname) < 10)) clantag = str_to_clantag(&channelname[5]); if ((clantag) && !((account_get_auth_admin(acc, channelname) == 1) || (account_get_auth_admin(acc, NULL) == 1))) { /* PELISH: Admins should be able to join any channel */ clan = account_get_clan(acc); if ((!clan) || (clan_get_clantag(clan) != clantag)) { if (!channel) { message_send_text(c, message_type_error, c, std::string("Unable to join channel " + std::string(channelname) + ", there is no member of that clan in the channel!").c_str()); if (conn_get_game(c) || c->protocol.chat.channel == NULL) { // FIXME: This is not tested to be according to battle.net!! // This is fix for empty clan channels with preventing to join CHANNEL_NAME_BANNED when is used _handle_join_command message_send_text(c, message_type_error, c, std::string("You have been redirected to " + std::string(CHANNEL_NAME_BANNED)).c_str()); channel = channellist_find_channel_by_name(CHANNEL_NAME_BANNED, conn_get_country(c), realm_get_name(conn_get_realm(c))); } else return 0; } else { t_clan * ch_clan; if ((ch_clan = clanlist_find_clan_by_clantag(clantag)) && (clan_get_channel_type(ch_clan) == 1)) { message_send_text(c, message_type_error, c, localize(c, "This is a private clan channel, unable to join!")); return 0; } } } else { if ((clan) && (clan_get_clantag(clan) == clantag) && (member = account_get_clanmember(acc))) { if (clanmember_get_status(member) >= CLAN_SHAMAN) /* PELISH: Giving tmpOP to SHAMAN and CHIEFTAIN on clanchannel */ conn_set_tmpOP_channel(c, channelname); } } } if (c->protocol.chat.channel) conn_part_channel(c); if (channel) { if (channel_check_banning(channel, c)) { message_send_text(c, message_type_error, c, localize(c, "You are banned from that channel.")); return -1; } if ((account_get_auth_admin(acc, NULL) != 1) && (account_get_auth_admin(acc, channelname) != 1) && (account_get_auth_operator(acc, NULL) != 1) && (account_get_auth_operator(acc, channelname) != 1) && (channel_get_max(channel) == 0)) { message_send_text(c, message_type_error, c, localize(c, "That channel is for Admins/Operators only.")); return -1; } if ((account_get_auth_admin(acc, NULL) != 1) && (account_get_auth_admin(acc, channelname) != 1) && (account_get_auth_operator(acc, NULL) != 1) && (account_get_auth_operator(acc, channelname) != 1) && (channel_get_max(channel) != -1) && (channel_get_curr(channel) >= channel_get_max(channel))) { message_send_text(c, message_type_error, c, localize(c, "The channel is currently full.")); return -1; } } if (conn_set_joingamewhisper_ack(c, 0) < 0) eventlog(eventlog_level_error, __FUNCTION__, "Unable to reset conn_set_joingamewhisper_ack flag"); if (conn_set_leavegamewhisper_ack(c, 0) < 0) eventlog(eventlog_level_error, __FUNCTION__, "Unable to reset conn_set_leavegamewhisper_ack flag"); /* if you're entering a channel, make sure they didn't exit a game without telling us */ if (c->protocol.game) { game_del_player(conn_get_game(c), c); c->protocol.game = NULL; } created = 0; if (!channel) { if (clantag) channel = channel_create(channelname, channelname, 0, 0, 1, 1, prefs_get_chanlog(), NULL, NULL, (prefs_get_maxusers_per_channel() > 0) ? prefs_get_maxusers_per_channel() : -1, 0, 1, 0); else channel = channel_create(channelname, channelname, 0, 0, 1, 1, prefs_get_chanlog(), NULL, NULL, (prefs_get_maxusers_per_channel() > 0) ? prefs_get_maxusers_per_channel() : -1, 0, 0, 0); if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create channel on join \"{}\"", conn_get_socket(c), channelname); return -1; } created = 1; } c->protocol.chat.channel = channel; if (channel_add_connection(channel, c) < 0) { if (created) channel_destroy(channel, &curr); c->protocol.chat.channel = NULL; return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] joined channel \"{}\"", conn_get_socket(c), channel_get_name(c->protocol.chat.channel)); conn_send_welcome(c); if (c->protocol.chat.channel && (channel_get_flags(c->protocol.chat.channel) & channel_flags_thevoid)) message_send_text(c, message_type_info, c, localize(c, "This channel does not have chat privileges.")); if (clantag && clan && (clan_get_clantag(clan) == clantag)) { char msgtemp[MAX_MESSAGE_LEN]; std::sprintf(msgtemp, "%s", clan_get_motd(clan)); message_send_text(c, message_type_info, c, msgtemp); } if (conn_is_irc_variant(c) == 0) { class_topic Topic; Topic.display(c, std::string(channel_get_name(c->protocol.chat.channel))); } if (c->protocol.chat.channel && (channel_get_flags(c->protocol.chat.channel) & channel_flags_moderated)) message_send_text(c, message_type_error, c, localize(c, "This channel is moderated.")); if (c->protocol.chat.channel != oldchannel) clanmember_on_change_status_by_connection(c); return 0; } extern int conn_part_channel(t_connection * c) { if (!c) { ERROR0("got NULL connection"); return -1; } if (!c->protocol.chat.channel) { ERROR0("client want to PART channel but is not in channel! c->protocol.chat.channel == NULL"); return -1; } channel_del_connection(c->protocol.chat.channel, c, message_type_part, NULL); c->protocol.chat.channel = NULL; return 0; } extern int conn_kick_channel(t_connection * c, char const * text) { /* According to RFC2812 in *text is reason, wol sends by text op nickname */ if (!c) { ERROR0("got NULL connection"); return -1; } if (!c->protocol.chat.channel) { ERROR0("client want to KICK channel but is not in channel! c->protocol.chat.channel == NULL"); return -1; } channel_del_connection(c->protocol.chat.channel, c, message_type_kick, text); c->protocol.chat.channel = NULL; return 0; } extern int conn_quit_channel(t_connection * c, char const * text) { /* According to RFC2812 in *text is user difinable QUIT message i.e.: Gone to have lunch */ if (!c) { ERROR0("got NULL connection"); return -1; } if (!c->protocol.chat.channel) { ERROR0("client want to QUIT channel but is not in channel! c->protocol.chat.channel == NULL"); return -1; } channel_del_connection(c->protocol.chat.channel, c, message_type_quit, text); c->protocol.chat.channel = NULL; return 0; } extern t_game * conn_get_game(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.game; } extern int conn_set_game(t_connection * c, char const * gamename, char const * gamepass, char const * gameinfo, t_game_type type, int version) /* * If game not exists (create) version != 0 (called in handle_bnet.c, function _client_startgameX()) * If game exists (join) version == 0 always (called in handle_bnet.c, function _client_joingame()) * If game exists (join) gameinfo == "" (called in handle_bnet.c, function _client_joingame()) * [KWS] */ { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } if (c->protocol.game) { if (gamename) { if (strcasecmp(gamename, game_get_name(c->protocol.game))) eventlog(eventlog_level_error, __FUNCTION__, "[{}] tried to join a new game \"{}\" while already in a game \"{}\"!", conn_get_socket(c), gamename, game_get_name(c->protocol.game)); else return 0; } game_del_player(conn_get_game(c), c); c->protocol.game = NULL; } if (gamename) { if (!(c->protocol.game = gamelist_find_game_available(gamename, c->protocol.client.clienttag, type)) && !gamelist_find_game_available(gamename, c->protocol.client.clienttag, game_type_all)) { /* do not allow creation of games with same name of same clienttag when game is not started or done */ // create game with initial values c->protocol.game = game_create(gamename, gamepass, gameinfo, type, version, c->protocol.client.clienttag, conn_get_gameversion(c)); if (c->protocol.game && conn_get_realm(c) && conn_get_charname(c)) { game_set_realmname(c->protocol.game, realm_get_name(conn_get_realm(c))); realm_add_game_number(conn_get_realm(c), 1); send_d2cs_gameinforeq(c); } } if (c->protocol.game) { // add new player to the game if (game_add_player(conn_get_game(c), gamepass, version, c) < 0) { c->protocol.game = NULL; // bad password or version # return -1; } #ifdef WITH_LUA // handle game create when it's owner joins the game if (c == game_get_owner(c->protocol.game)) lua_handle_game(c->protocol.game, NULL, luaevent_game_create); #endif if (game_is_ladder(c->protocol.game)) { if (c == game_get_owner(c->protocol.game)) message_send_text(c, message_type_info, c, localize(c, "Created ladder game")); else message_send_text(c, message_type_info, c, localize(c, "Joined ladder game")); } } } else c->protocol.game = NULL; return 0; } extern unsigned int conn_get_tcpaddr(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->socket.tcp_addr; } extern t_packet * conn_get_in_queue(t_connection * c) { assert(c); return c->protocol.queues.inqueue; } extern void conn_put_in_queue(t_connection * c, t_packet * packet) { assert(c); c->protocol.queues.inqueue = packet; } extern unsigned int conn_get_in_size(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.queues.insize; } extern void conn_set_in_size(t_connection * c, unsigned int size) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } c->protocol.queues.insize = size; } extern unsigned int conn_get_out_size(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.queues.outsize; } extern void conn_set_out_size(t_connection * c, unsigned int size) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } c->protocol.queues.outsize = size; } extern int conn_push_outqueue(t_connection * c, t_packet * packet) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } // Protection from hack attempt // Limit out queue packets due to it may cause memory leak with not enough memory program crash on a server machine t_queue ** q = &c->protocol.queues.outqueue; if (prefs_get_packet_limit() && queue_get_length((t_queue const * const *)q) > prefs_get_packet_limit()) { queue_clear(q); conn_set_state(c, conn_state_destroy); eventlog(eventlog_level_error, __FUNCTION__, "outqueue reached limit of {} packets (hack attempt?)", prefs_get_packet_limit()); return 0; } queue_push_packet((t_queue * *)&c->protocol.queues.outqueue, packet); if (!c->protocol.queues.outsizep++) fdwatch_update_fd(c->socket.fdw_idx, fdwatch_type_read | fdwatch_type_write); return 0; } extern int conn_clear_outqueue(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } queue_clear(&c->protocol.queues.outqueue); return 0; } extern t_packet * conn_peek_outqueue(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if (c->protocol.queues.outqueue) return queue_peek_packet((t_queue const * const *)&c->protocol.queues.outqueue); else return 0; } extern t_packet * conn_pull_outqueue(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if (c->protocol.queues.outsizep) { if (!(--c->protocol.queues.outsizep)) fdwatch_update_fd(c->socket.fdw_idx, fdwatch_type_read); return queue_pull_packet((t_queue * *)&c->protocol.queues.outqueue); } return NULL; } extern char const * conn_get_username_real(t_connection const * c, char const * fn, unsigned int ln) { char const * result; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection (from {}:{})", fn, ln); return NULL; } if (!c->protocol.account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account (from {}:{})", fn, ln); return NULL; } result = account_get_name(c->protocol.account); if (result == NULL) eventlog(eventlog_level_error, __FUNCTION__, "returned previous error after being called by {}:{}", fn, ln); return result; } extern char const * conn_get_chatname(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if ((c->protocol.cclass == conn_class_bnet) && c->protocol.bound) { if (c->protocol.d2.character) return character_get_name(c->protocol.d2.character); if (c->protocol.bound->protocol.d2.character) return character_get_name(c->protocol.bound->protocol.d2.character); eventlog(eventlog_level_error, __FUNCTION__, "[{}] got connection class {} bound to class {} without a character", conn_get_socket(c), conn_class_get_str(c->protocol.cclass), c->protocol.bound->protocol.cclass); } if (!c->protocol.account) return NULL; /* no name yet */ return conn_get_loggeduser(c); } extern int conn_unget_chatname(t_connection const * c, char const * name) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if ((c->protocol.cclass == conn_class_bnet) && c->protocol.bound) return 0; return 0; } extern char const * conn_get_chatcharname(t_connection const * c, t_connection const * dst) { char const * accname; char * chatcharname; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if (!c->protocol.account) return NULL; /* no name yet */ /* for D2 Users */ accname = conn_get_loggeduser(c); if (!accname) return NULL; if (dst && dst->protocol.d2.charname) { const char *mychar; if (c->protocol.d2.charname) mychar = c->protocol.d2.charname; else mychar = ""; chatcharname = (char*)xmalloc(std::strlen(accname) + 2 + std::strlen(mychar)); std::sprintf(chatcharname, "%s*%s", mychar, accname); } else chatcharname = xstrdup(accname); return chatcharname; } extern int conn_unget_chatcharname(t_connection const * c, char const * name) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!name) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL name"); return -1; } xfree((void *)name); /* avoid warning */ return 0; } extern t_message_class conn_get_message_class(t_connection const * c, t_connection const * dst) { if (dst && dst->protocol.d2.charname) /* message to D2 user must be char*account */ return message_class_charjoin; return message_class_normal; } extern unsigned int conn_get_userid(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } if (!c->protocol.account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return 0; } return account_get_uid(c->protocol.account); } extern int conn_get_socket(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return c->socket.tcp_sock; } extern int conn_get_game_socket(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return c->socket.udp_sock; } extern int conn_set_game_socket(t_connection * c, int usock) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->socket.udp_sock = usock; return 0; } /* Player icon that displayed in a channel in all games (except Warcraft 3) */ extern char const * conn_get_playerinfo(t_connection const * c) { t_account * account; static char playerinfo[MAX_PLAYERINFO_STR]; t_clienttag clienttag; char revtag[5]; char const * usericon; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if (!(account = conn_get_account(c))) { eventlog(eventlog_level_error, __FUNCTION__, "connection has no account"); return NULL; } if (!(clienttag = conn_get_fake_clienttag(c))) { eventlog(eventlog_level_error, __FUNCTION__, "connection has NULL fakeclienttag"); return NULL; } tag_uint_to_revstr(revtag, clienttag); // allow set icon to a user directly from the database (override default tag always if not null) if (usericon = account_get_user_icon(account, clienttag)) std::sprintf(revtag, "%s", usericon); else if (clienttag == CLIENTTAG_BNCHATBOT_UINT) { std::strcpy(playerinfo, revtag); /* FIXME: what to return here? */ } else if ((clienttag == CLIENTTAG_STARCRAFT_UINT) || (clienttag == CLIENTTAG_BROODWARS_UINT)) { if (conn_get_versionid(c) <= 0x000000c7) { std::sprintf(playerinfo, "%s %u %d %u %u %u", revtag, account_get_ladder_rating(account, clienttag, ladder_id_normal), account_get_ladder_rank(account, clienttag, ladder_id_normal), account_get_normal_wins(account, clienttag), 0, 0); } else { std::sprintf(playerinfo, "%s %u %d %u %u %u %u %u %u %s", revtag, account_get_ladder_rating(account, clienttag, ladder_id_normal), account_get_ladder_rank(account, clienttag, ladder_id_normal), account_get_normal_wins(account, clienttag), 0, 0, account_get_ladder_high_rating(account, clienttag, ladder_id_normal), 0, 0, revtag); } } else if (clienttag == CLIENTTAG_SHAREWARE_UINT) { std::sprintf(playerinfo, "%s %u %d %u %u %u", revtag, account_get_ladder_rating(account, clienttag, ladder_id_normal), account_get_ladder_rank(account, clienttag, ladder_id_normal), account_get_normal_wins(account, clienttag), 0, 0); } else if (clienttag == CLIENTTAG_DIABLORTL_UINT) { std::sprintf(playerinfo, "%s %u %u %u %u %u %u %u %u %u", revtag, account_get_normal_level(account, clienttag), account_get_normal_class(account, clienttag), account_get_normal_diablo_kills(account, clienttag), account_get_normal_strength(account, clienttag), account_get_normal_magic(account, clienttag), account_get_normal_dexterity(account, clienttag), account_get_normal_vitality(account, clienttag), account_get_normal_gold(account, clienttag), 0); } else if (clienttag == CLIENTTAG_DIABLOSHR_UINT) { std::sprintf(playerinfo, "%s %u %u %u %u %u %u %u %u %u", revtag, account_get_normal_level(account, clienttag), account_get_normal_class(account, clienttag), account_get_normal_diablo_kills(account, clienttag), account_get_normal_strength(account, clienttag), account_get_normal_magic(account, clienttag), account_get_normal_dexterity(account, clienttag), account_get_normal_vitality(account, clienttag), account_get_normal_gold(account, clienttag), 0); } else if (clienttag == CLIENTTAG_WARCIIBNE_UINT) { unsigned int a, b; a = account_get_ladder_rating(account, clienttag, ladder_id_normal); b = account_get_ladder_rating(account, clienttag, ladder_id_ironman); std::sprintf(playerinfo, "%s %u %d %u %u %u %u %u %d", revtag, a, account_get_ladder_rank(account, clienttag, ladder_id_normal), account_get_normal_wins(account, clienttag), 0, 0, (a > b) ? a : b, b, account_get_ladder_rank(account, clienttag, ladder_id_ironman)); } else if (clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT) { /* This sets portrait of character */ if (!conn_get_realm(c) || !conn_get_realminfo(c)) { std::strcpy(playerinfo, revtag); } else { std::strcpy(playerinfo, conn_get_realminfo(c)); } } else std::strcpy(playerinfo, revtag); /* open char */ // if custom_icons is enabled then set a custom client tag by player rating // do not override user selected icon if it's not null if (!usericon && prefs_get_custom_icons() == 1 && customicons_allowed_by_client(clienttag)) { if (t_icon_info * icon = customicons_get_icon_by_account(account, clienttag)) std::snprintf(playerinfo, sizeof playerinfo, "%s", icon->icon_code); } #ifdef WITH_LUA // change icon info from Lua if (const char * iconinfo = lua_handle_user_icon((t_connection*)c, playerinfo)) return iconinfo; #endif return playerinfo; } extern int conn_set_playerinfo(t_connection const * c, char const * playerinfo) { t_clienttag clienttag; char clienttag_str[5]; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!playerinfo) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL playerinfo"); return -1; } clienttag = c->protocol.client.clienttag; if (clienttag == CLIENTTAG_DIABLORTL_UINT) { unsigned int level; unsigned int chclass; unsigned int diablo_kills; unsigned int strength; unsigned int magic; unsigned int dexterity; unsigned int vitality; unsigned int gold; if (std::sscanf(playerinfo, "LTRD %u %u %u %u %u %u %u %u %*u", &level, &chclass, &diablo_kills, &strength, &magic, &dexterity, &vitality, &gold) != 8) { eventlog(eventlog_level_error, __FUNCTION__, "got bad playerinfo"); return -1; } account_set_normal_level(conn_get_account(c), clienttag, level); account_set_normal_class(conn_get_account(c), clienttag, chclass); account_set_normal_diablo_kills(conn_get_account(c), clienttag, diablo_kills); account_set_normal_strength(conn_get_account(c), clienttag, strength); account_set_normal_magic(conn_get_account(c), clienttag, magic); account_set_normal_dexterity(conn_get_account(c), clienttag, dexterity); account_set_normal_vitality(conn_get_account(c), clienttag, vitality); account_set_normal_gold(conn_get_account(c), clienttag, gold); } else if (clienttag == CLIENTTAG_DIABLOSHR_UINT) { unsigned int level; unsigned int chclass; unsigned int diablo_kills; unsigned int strength; unsigned int magic; unsigned int dexterity; unsigned int vitality; unsigned int gold; if (std::sscanf(playerinfo, "RHSD %u %u %u %u %u %u %u %u %*u", &level, &chclass, &diablo_kills, &strength, &magic, &dexterity, &vitality, &gold) != 8) { eventlog(eventlog_level_error, __FUNCTION__, "got bad playerinfo"); return -1; } account_set_normal_level(conn_get_account(c), clienttag, level); account_set_normal_class(conn_get_account(c), clienttag, chclass); account_set_normal_diablo_kills(conn_get_account(c), clienttag, diablo_kills); account_set_normal_strength(conn_get_account(c), clienttag, strength); account_set_normal_magic(conn_get_account(c), clienttag, magic); account_set_normal_dexterity(conn_get_account(c), clienttag, dexterity); account_set_normal_vitality(conn_get_account(c), clienttag, vitality); account_set_normal_gold(conn_get_account(c), clienttag, gold); } else if (clienttag == CLIENTTAG_DIABLO2DV_UINT) { /* not much to do */ /* FIXME: get char name here? */ eventlog(eventlog_level_trace, __FUNCTION__, "[{}] playerinfo request for client \"{}\" playerinfo=\"{}\"", conn_get_socket(c), tag_uint_to_str(clienttag_str, clienttag), playerinfo); } else if (clienttag == CLIENTTAG_DIABLO2XP_UINT) { /* in playerinfo we get strings of the form "Realmname,charname" */ eventlog(eventlog_level_trace, __FUNCTION__, "[{}] playerinfo request for client \"{}\" playerinfo=\"{}\"", conn_get_socket(c), tag_uint_to_str(clienttag_str, clienttag), playerinfo); } else { eventlog(eventlog_level_warn, __FUNCTION__, "setting playerinfo for client \"{}\" not supported (playerinfo=\"{}\")", tag_uint_to_str(clienttag_str, clienttag), playerinfo); return -1; } return 0; } extern char const * conn_get_realminfo(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.d2.realminfo; } extern int conn_set_realminfo(t_connection * c, char const * realminfo) { char const * temp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (realminfo) temp = xstrdup(realminfo); else temp = NULL; if (c->protocol.d2.realminfo) /* if it was set before, free it now */ xfree((void *)c->protocol.d2.realminfo); /* avoid warning */ c->protocol.d2.realminfo = temp; return 0; } extern char const * conn_get_charname(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.d2.charname; } extern int conn_set_charname(t_connection * c, char const * charname) { char const * temp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (charname) temp = xstrdup(charname); else temp = charname; if (c->protocol.d2.charname) /* free it, if it was previously set */ xfree((void *)c->protocol.d2.charname); /* avoid warning */ c->protocol.d2.charname = temp; return 0; } extern int conn_set_idletime(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->protocol.chat.last_message = now; return 0; } extern unsigned int conn_get_idletime(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return (unsigned int)std::difftime(now, c->protocol.chat.last_message); } extern t_realm * conn_get_realm(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.d2.realm; } extern int conn_set_realm(t_connection * c, t_realm * realm) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (c->protocol.d2.realm) realm_put(c->protocol.d2.realm, &c->protocol.d2.realm_regref); if (!realm) c->protocol.d2.realm = NULL; else { c->protocol.d2.realm = realm_get(realm, &c->protocol.d2.realm_regref); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] set to \"{}\"", conn_get_socket(c), realm_get_name(realm)); } return 0; } extern int conn_set_realm_cb(void *data, void *newref) { t_connection *c = (t_connection*)data; t_realm *newrealm = (t_realm*)newref; assert(c->protocol.d2.realm); /* this should never be NULL here */ /* we are removing a reference */ realm_put(c->protocol.d2.realm, &c->protocol.d2.realm_regref); if (newrealm) c->protocol.d2.realm = realm_get(newrealm, &c->protocol.d2.realm_regref); else { /* close the connection for players on unconfigured realms */ conn_set_state(c, conn_state_destroy); c->protocol.d2.realm = NULL; } return 0; } extern int conn_set_character(t_connection * c, t_character * character) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!character) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character"); return -1; } c->protocol.d2.character = character; return 0; } extern void conn_set_country(t_connection * c, char const * country) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!country) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL country"); return; } if (c->protocol.client.country) xfree((void *)c->protocol.client.country); /* avoid warning */ c->protocol.client.country = xstrdup(country); } extern char const * conn_get_country(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.client.country; } extern int conn_bind(t_connection * c1, t_connection * c2) { if (!c1) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!c2) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c1->protocol.bound = c2; c2->protocol.bound = c1; return 0; } extern int conn_set_ircline(t_connection * c, char const * line) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!line) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL line"); return -1; } if (c->protocol.chat.irc.ircline) xfree((void *)c->protocol.chat.irc.ircline); /* avoid warning */ c->protocol.chat.irc.ircline = xstrdup(line); return 0; } extern char const * conn_get_ircline(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.chat.irc.ircline; } extern int conn_set_ircpass(t_connection * c, char const * pass) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (c->protocol.chat.irc.ircpass) xfree((void *)c->protocol.chat.irc.ircpass); /* avoid warning */ if (!pass) c->protocol.chat.irc.ircpass = NULL; else c->protocol.chat.irc.ircpass = xstrdup(pass); return 0; } extern char const * conn_get_ircpass(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.chat.irc.ircpass; } extern int conn_set_ircping(t_connection * c, unsigned int ping) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } c->protocol.chat.irc.ircping = ping; return 0; } extern unsigned int conn_get_ircping(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return c->protocol.chat.irc.ircping; } // NonReal extern int conn_get_welcomed(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return (c->protocol.cflags & conn_flags_welcomed); } // NonReal extern void conn_set_welcomed(t_connection * c, int welcomed) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } c->protocol.cflags |= conn_flags_welcomed; } /* ADDED BY UNDYING SOULZZ 4/7/02 */ extern int conn_set_w3_playerinfo(t_connection * c, const char * w3_playerinfo) { const char * temp; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } temp = xstrdup(w3_playerinfo); if (c->protocol.w3.w3_playerinfo) xfree((void *)c->protocol.w3.w3_playerinfo); c->protocol.w3.w3_playerinfo = temp; return 1; } extern const char * conn_get_w3_playerinfo(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.w3.w3_playerinfo; } extern int conn_quota_exceeded(t_connection * con, char const * text) { t_qline * qline; t_elem * curr; if (!prefs_get_quota() || !conn_get_account(con) // FIXME: (HarpyWar) do not allow flood for admins due to possible abuse with quick command sending that high load a server processor // If we really need to ignore flood protection, it can be allowed in Lua config for special users /* || (account_get_command_groups(conn_get_account(con)) & command_get_group("/admin-con"))*/ ) return 0; if (std::strlen(text) > prefs_get_quota_maxline()) { message_send_text(con, message_type_error, con, localize(con, "Your line length quota has been exceeded!")); return 1; } LIST_TRAVERSE(con->protocol.chat.quota.list, curr) { qline = (t_qline*)elem_get_data(curr); if (now >= qline->inf + (std::time_t)prefs_get_quota_time()) { /* these lines are at least quota_time old */ list_remove_elem(con->protocol.chat.quota.list, &curr); if (qline->count > con->protocol.chat.quota.totcount) eventlog(eventlog_level_error, __FUNCTION__, "qline->count={} but con->protocol.chat.quota.totcount={}", qline->count, con->protocol.chat.quota.totcount); con->protocol.chat.quota.totcount -= qline->count; xfree(qline); } else break; /* old items are first, so we know nothing else will match */ } qline = (t_qline*)xmalloc(sizeof(t_qline)); qline->inf = now; /* set the moment */ if (std::strlen(text) > prefs_get_quota_wrapline()) /* round up on the divide */ qline->count = (std::strlen(text) + prefs_get_quota_wrapline() - 1) / prefs_get_quota_wrapline(); else qline->count = 1; list_append_data(con->protocol.chat.quota.list, qline); con->protocol.chat.quota.totcount += qline->count; if (con->protocol.chat.quota.totcount >= prefs_get_quota_lines()) { message_send_text(con, message_type_error, con, localize(con, "Your message quota has been exceeded!")); if (con->protocol.chat.quota.totcount >= prefs_get_quota_dobae()) { /* kick out the dobae user for violation of the quota rule */ conn_set_state(con, conn_state_destroy); if (con->protocol.chat.channel) channel_message_log(con->protocol.chat.channel, con, 0, "DISCONNECTED FOR DOBAE ABUSE"); return 2; } return 1; } return 0; } extern int conn_set_lastsender(t_connection * c, char const * sender) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conection"); return -1; } if (c->protocol.chat.lastsender) xfree((void *)c->protocol.chat.lastsender); /* avoid warning */ if (!sender) { c->protocol.chat.lastsender = NULL; return 0; } c->protocol.chat.lastsender = xstrdup(sender); return 0; } extern char const * conn_get_lastsender(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.chat.lastsender; } const VersionCheck *conn_get_versioncheck(t_connection *c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return nullptr; } return c->protocol.client.versioncheck; } bool conn_set_versioncheck(t_connection *c, const VersionCheck* versioncheck) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return false; } c->protocol.client.versioncheck = versioncheck; return true; } extern int conn_get_echoback(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } return (c->protocol.cflags & conn_flags_echoback); } extern void conn_set_echoback(t_connection * c, int echoback) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (echoback) c->protocol.cflags |= conn_flags_echoback; else c->protocol.cflags &= ~conn_flags_echoback; } extern int conn_set_udpok(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!(c->protocol.cflags & conn_flags_udpok)) { c->protocol.cflags |= conn_flags_udpok; c->protocol.flags &= ~MF_PLUG; } return 0; } extern t_connection * conn_get_routeconn(t_connection const * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return c->protocol.w3.routeconn; } extern int conn_set_routeconn(t_connection * c, t_connection * rc) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conection"); return -1; } c->protocol.w3.routeconn = rc; return 0; } extern int conn_get_crtime(t_connection *c) { if (!c) { eventlog(eventlog_level_error, "conn_get_crtime", "got NULL connection"); return -1; } return c->protocol.cr_time; } extern int conn_set_joingamewhisper_ack(t_connection * c, unsigned int value) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (value) c->protocol.cflags |= conn_flags_joingamewhisper; else c->protocol.cflags &= ~conn_flags_joingamewhisper; return 0; } extern int conn_get_joingamewhisper_ack(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return (c->protocol.cflags & conn_flags_joingamewhisper); } extern int conn_set_leavegamewhisper_ack(t_connection * c, unsigned int value) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (value) c->protocol.cflags |= conn_flags_leavegamewhisper; else c->protocol.cflags &= ~conn_flags_leavegamewhisper; return 0; } extern int conn_get_leavegamewhisper_ack(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return (c->protocol.cflags & conn_flags_leavegamewhisper); } extern int conn_set_anongame_search_starttime(t_connection * c, std::time_t t) { if (c == NULL) { eventlog(eventlog_level_error, "conn_set_anongame_search_starttime", "got NULL connection"); return -1; } c->protocol.w3.anongame_search_starttime = t; return 0; } extern std::time_t conn_get_anongame_search_starttime(t_connection * c) { if (c == NULL) { eventlog(eventlog_level_error, "conn_set_anongame_search_starttime", "got NULL connection"); return ((std::time_t) 0); } return c->protocol.w3.anongame_search_starttime; } extern t_elist * conn_get_timer(t_connection *c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return &c->protocol.timers; } extern int conn_add_fdwatch(t_connection *c, fdwatch_handler handle) { assert(c); c->socket.fdw_idx = fdwatch_add_fd(c->socket.tcp_sock, fdwatch_type_read, handle, c); return c->socket.fdw_idx; } extern void conn_close_read(t_connection *c) { assert(c); conn_set_state(c, conn_state_destroy); /* only if we still got output packets remove the read availability * from fdwatch, we are NOT allowed to remove all availability or * remove it completely from fdwatch while handling read, also * if the connection has no output packets is ok to leave it * in read availability check cause it will be closed immediately * in connlist_reap() anyway */ if (conn_peek_outqueue(c)) fdwatch_update_fd(c->socket.fdw_idx, fdwatch_type_write); } extern int conn_get_user_count_by_clienttag(t_clienttag ct) { t_connection * conn; t_elem const * curr; int clienttagusers = 0; /* Get Number of Users for client tag specific */ LIST_TRAVERSE_CONST(connlist(), curr) { conn = (t_connection*)elem_get_data(curr); if ((ct == conn->protocol.client.clienttag) && (conn->protocol.state == conn_state_loggedin)) clienttagusers++; } return clienttagusers; } extern int connlist_create(void) { conn_head = list_create(); connarray_create(); return 0; } extern int connlist_destroy(void) { if (conn_dead) list_destroy(conn_dead); conn_dead = NULL; connarray_destroy(); /* FIXME: if called with active connection, connection are not freed */ if (list_destroy(conn_head) < 0) return -1; conn_head = NULL; return 0; } extern void connlist_reap(void) { t_elem *curr; t_connection *c; if (!conn_dead || !conn_head) return; LIST_TRAVERSE(conn_dead, curr) { c = (t_connection *)elem_get_data(curr); if (!c) eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in conn_dead list"); else if (!conn_peek_outqueue(c)) { conn_destroy(c, &curr, DESTROY_FROM_DEADLIST); /* also removes from conn_dead list and fdwatch */ } } } extern t_list * connlist(void) { return conn_head; } extern t_connection * connlist_find_connection_by_accountname(char const * accountname) { t_account * temp; if (!accountname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL accountname"); return NULL; } if (!(temp = accountlist_find_account(accountname))) return NULL; return account_get_conn(temp); } extern t_connection * connlist_find_connection_by_account(t_account * account) { if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } return account_get_conn(account); } extern t_connection * connlist_find_connection_by_sessionkey(unsigned int sessionkey) { t_connection * c; t_elem const * curr; LIST_TRAVERSE_CONST(conn_head, curr) { c = (t_connection*)elem_get_data(curr); if (c->protocol.sessionkey == sessionkey) return c; } return NULL; } extern t_connection * connlist_find_connection_by_sessionnum(unsigned int sessionnum) { return connarray_get_conn(sessionnum); } extern t_connection * connlist_find_connection_by_socket(int socket) { t_connection * c; t_elem const * curr; LIST_TRAVERSE_CONST(conn_head, curr) { c = (t_connection*)elem_get_data(curr); if (c->socket.tcp_sock == socket) return c; } return NULL; } extern t_connection * connlist_find_connection_by_name(char const * name, t_realm * realm) { char charname[MAX_CHARNAME_LEN]; char const * temp; if (!name) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL name"); return NULL; } if (name[0] == '\0') { eventlog(eventlog_level_error, __FUNCTION__, "got empty name"); return NULL; } /* format: *username */ if (name[0] == '*') { name++; return connlist_find_connection_by_accountname(name); } /* If is charname@otherrealm or ch@rname@realm */ if ((temp = std::strrchr(name, '@'))) /* search from the right */ { unsigned int n; n = temp - name; if (n >= MAX_CHARNAME_LEN) { eventlog(eventlog_level_info, __FUNCTION__, "character name too long in \"{}\" (charname@otherrealm format)", name); return NULL; } std::strncpy(charname, name, n); charname[n] = '\0'; return connlist_find_connection_by_charname(name, temp + 1); } /* format: charname*username */ if ((temp = std::strchr(name, '*'))) { unsigned int n; n = temp - name; if (n >= MAX_CHARNAME_LEN) { eventlog(eventlog_level_info, __FUNCTION__, "character name too long in \"{}\" (charname*username format)", name); return NULL; } name = temp + 1; return connlist_find_connection_by_accountname(name); } /* format: charname (realm must be not NULL) */ if (realm) return connlist_find_connection_by_charname(name, realm_get_name(realm)); /* format: Simple username, clients with no realm, like starcraft or d2 open, * the format is the same of charname but is matched if realmname is NULL */ return connlist_find_connection_by_accountname(name); } extern t_connection * connlist_find_connection_by_charname(char const * charname, char const * realmname) { t_connection * c; t_elem const * curr; if (!realmname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realmname"); return NULL; } LIST_TRAVERSE_CONST(conn_head, curr) { c = (t_connection*)elem_get_data(curr); if (!c) continue; if (!c->protocol.d2.charname) continue; if (!c->protocol.d2.realm) continue; if ((strcasecmp(c->protocol.d2.charname, charname) == 0) && (strcasecmp(realm_get_name(c->protocol.d2.realm), realmname) == 0)) return c; } return NULL; } extern t_connection * connlist_find_connection_by_uid(unsigned int uid) { t_account * temp; if (!(temp = accountlist_find_account_by_uid(uid))) { return NULL; } return account_get_conn(temp); } extern int connlist_get_length(void) { return list_get_length(conn_head); } extern unsigned int connlist_login_get_length(void) { t_connection const * c; unsigned int count; t_elem const * curr; count = 0; LIST_TRAVERSE_CONST(conn_head, curr) { c = (const t_connection*)elem_get_data(curr); if ((c->protocol.state == conn_state_loggedin) && ((c->protocol.cclass == conn_class_bnet) || (c->protocol.cclass == conn_class_bot) || (c->protocol.cclass == conn_class_telnet) || (c->protocol.cclass == conn_class_irc) || (c->protocol.cclass == conn_class_wol))) count++; } return count; } extern int connlist_total_logins(void) { return totalcount; } extern unsigned int connlist_count_connections(unsigned int addr) { t_connection * c; t_elem const * curr; unsigned int count; count = 0; LIST_TRAVERSE_CONST(conn_head, curr) { c = (t_connection *)elem_get_data(curr); if (c->socket.tcp_addr == addr) count++; } return count; } /* Warcraft 3 icon that displayed in a channel */ extern int conn_update_w3_playerinfo(t_connection * c) { t_account * account; t_clienttag clienttag; t_clan * user_clan; int clantag = 0; unsigned int acctlevel; char tempplayerinfo[40]; char raceicon; /* appeared in 1.03 */ unsigned int raceiconnumber; unsigned int wins; char clantag_str_tmp[5]; const char * clantag_str = NULL; char revtag[5]; char clienttag_str[5]; if (c == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } account = conn_get_account(c); if (account == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } clienttag = c->protocol.client.clienttag; if (!((clienttag == CLIENTTAG_WARCRAFT3_UINT) || (clienttag == CLIENTTAG_WAR3XP_UINT))){ return 0; } std::strncpy(revtag, tag_uint_to_str(clienttag_str, conn_get_fake_clienttag(c)), 5); revtag[4] = '\0'; strreverse(revtag); acctlevel = account_get_highestladderlevel(account, clienttag); account_get_raceicon(account, &raceicon, &raceiconnumber, &wins, clienttag); if ((user_clan = account_get_clan(account)) != NULL) clantag = clan_get_clantag(user_clan); if (clantag) { std::sprintf(clantag_str_tmp, "%c%c%c%c", clantag & 0xff, (clantag >> 8) & 0xff, (clantag >> 16) & 0xff, clantag >> 24); clantag_str = clantag_str_tmp; while ((*clantag_str) == 0) clantag_str++; } const char* usericon = account_get_user_icon(account, clienttag); // allow set icon to a user directly from the database (override default icon always if not null) if (usericon) { if (clantag) std::sprintf(tempplayerinfo, "%s %s %u %s", revtag, usericon, acctlevel, clantag_str); else std::sprintf(tempplayerinfo, "%s %s %u", revtag, usericon, acctlevel); eventlog(eventlog_level_info, __FUNCTION__, "[{}] {} using user-selected icon [{}]", conn_get_socket(c), revtag, usericon); } // default icon "WAR3" or "W3XP" else if (acctlevel == 0) { if (clantag) std::sprintf(tempplayerinfo, "%s %s 0 %s", revtag, revtag, clantag_str); else std::strcpy(tempplayerinfo, revtag); eventlog(eventlog_level_info, __FUNCTION__, "[{}] {}", conn_get_socket(c), revtag); } // display race icon with a level number else { if (clantag) std::sprintf(tempplayerinfo, "%s %1u%c3W %u %s", revtag, raceiconnumber, raceicon, acctlevel, clantag_str); else std::sprintf(tempplayerinfo, "%s %1u%c3W %u", revtag, raceiconnumber, raceicon, acctlevel); eventlog(eventlog_level_info, __FUNCTION__, "[{}] {} using generated icon [{}{}3W]", conn_get_socket(c), revtag, raceiconnumber, raceicon); } // if custom stats is enabled then set a custom client icon by player rating // do not override user selected icon if any if (!usericon && prefs_get_custom_icons() == 1 && customicons_allowed_by_client(clienttag)) { if (t_icon_info* icon = customicons_get_icon_by_account(account, clienttag)) { if (clantag) std::sprintf(tempplayerinfo, "%s %s %u %s", revtag, icon->icon_code, 0, clantag_str); else std::sprintf(tempplayerinfo, "%s %s %u", revtag, icon->icon_code, 0); } } #ifdef WITH_LUA // change icon info from Lua if (const char * iconinfo = lua_handle_user_icon(c, tempplayerinfo)) { conn_set_w3_playerinfo(c, iconinfo); return 0; } #endif conn_set_w3_playerinfo(c, tempplayerinfo); return 0; } extern int conn_get_passfail_count(t_connection * c) { if (!c) { eventlog(eventlog_level_error, "conn_get_passfail_count", "got NULL connection"); return -1; } return c->protocol.passfail_count; } extern int conn_set_passfail_count(t_connection * c, unsigned int n) { if (c == NULL) { eventlog(eventlog_level_error, "conn_set_passfail_count", "got NULL connection"); return -1; } c->protocol.passfail_count = n; return 0; } extern int conn_increment_passfail_count(t_connection * c) { unsigned int count; if (prefs_get_passfail_count() > 0) { count = conn_get_passfail_count(c) + 1; if (count == prefs_get_passfail_count()) { ipbanlist_add(NULL, addr_num_to_ip_str(conn_get_addr(c)), now + (std::time_t)prefs_get_passfail_bantime()); eventlog(eventlog_level_info, __FUNCTION__, "[{}] failed password tries: {} (banned ip)", conn_get_socket(c), count); conn_set_state(c, conn_state_destroy); return -1; } else conn_set_passfail_count(c, count); } return 0; } extern char const * conn_get_client_proof(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return NULL; } return c->protocol.w3.client_proof; } extern int conn_set_client_proof(t_connection * c, char const * client_proof) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } if (c->protocol.w3.client_proof){ xfree((void*)c->protocol.w3.client_proof); c->protocol.w3.client_proof = NULL; } if (client_proof != NULL) { char * proof = (char *)xmalloc(20); std::memcpy(proof, client_proof, 20); c->protocol.w3.client_proof = proof; } return 0; } extern char const * conn_get_server_proof(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return NULL; } return c->protocol.w3.server_proof; } extern int conn_set_server_proof(t_connection * c, char const * server_proof) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } if (c->protocol.w3.server_proof){ xfree((void*)c->protocol.w3.server_proof); c->protocol.w3.server_proof = NULL; } if (server_proof != NULL) { char * proof = (char *)xmalloc(20); std::memcpy(proof, server_proof, 20); c->protocol.w3.server_proof = proof; } return 0; } extern int conn_set_tmpOP_channel(t_connection * c, char const * tmpOP_channel) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } if (c->protocol.chat.tmpOP_channel) { xfree((void *)c->protocol.chat.tmpOP_channel); c->protocol.chat.tmpOP_channel = NULL; } if (tmpOP_channel) c->protocol.chat.tmpOP_channel = xstrdup(tmpOP_channel); return 0; } extern char const * conn_get_tmpOP_channel(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return NULL; } return c->protocol.chat.tmpOP_channel; } extern int conn_set_tmpVOICE_channel(t_connection * c, char const * tmpVOICE_channel) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } if (c->protocol.chat.tmpVOICE_channel) { xfree((void *)c->protocol.chat.tmpVOICE_channel); c->protocol.chat.tmpVOICE_channel = NULL; } if (tmpVOICE_channel) c->protocol.chat.tmpVOICE_channel = xstrdup(tmpVOICE_channel); return 0; } extern char const * conn_get_tmpVOICE_channel(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return NULL; } return c->protocol.chat.tmpVOICE_channel; } static int connarray_create(void) { unsigned i; t_conn_entry *curr; if (connarray) connarray_destroy(); connarray = (t_conn_entry*)xmalloc(sizeof(t_conn_entry)* fdw_maxcons); elist_init(&arrayflist); /* put all elements as free */ for (i = 0, curr = connarray; i < fdw_maxcons; i++, curr++) { elist_add_tail(&arrayflist, &curr->freelist); curr->c = NULL; } return 0; } static void connarray_destroy(void) { if (connarray) xfree((void*)connarray); connarray = NULL; } static t_connection *connarray_get_conn(unsigned index) { if (index >= fdw_maxcons) return NULL; return connarray[index].c; } static unsigned connarray_add_conn(t_connection *c) { t_conn_entry *curr; assert(c); assert(!elist_empty(&arrayflist)); curr = elist_entry(elist_next(&arrayflist), t_conn_entry, freelist); assert(curr->c == NULL); /* it should never be free and != NULL */ curr->c = c; elist_del(&curr->freelist); return (curr - connarray); /* return the array index */ } static void connarray_del_conn(unsigned index) { t_conn_entry *curr; if (index >= fdw_maxcons) return; curr = connarray + index; curr->c = NULL; elist_add_tail(&arrayflist, &curr->freelist); } extern int conn_is_irc_variant(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL conn"); return -1; } if ((c->protocol.cclass == conn_class_irc) || (c->protocol.cclass == conn_class_wol) || (c->protocol.cclass == conn_class_wserv) || (c->protocol.cclass == conn_class_wladder) ) return 1; return 0; } /** * Westwood Online Extensions */ extern int conn_get_wol(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL conn"); return -1; } if (c->protocol.cclass == conn_class_wol) return 1; return 0; } extern void conn_wol_set_apgar(t_connection * c, char const * apgar) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } if (!apgar) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL WOL apgar"); return; } if (c->protocol.wol.apgar) xfree((void *)c->protocol.wol.apgar); /* avoid warning */ c->protocol.wol.apgar = xstrdup(apgar); } extern char const * conn_wol_get_apgar(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return NULL; } return c->protocol.wol.apgar; } extern void conn_wol_set_codepage(t_connection * c, int codepage) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return; } c->protocol.wol.codepage = codepage; } extern int conn_wol_get_codepage(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } return c->protocol.wol.codepage; } extern void conn_wol_set_findme(t_connection * c, bool findme) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return; } c->protocol.wol.findme = findme; } extern bool conn_wol_get_findme(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return false; } bool bRet; if (c->protocol.wol.findme == 0) bRet = false; else bRet = true; return bRet; } extern void conn_wol_set_pageme(t_connection * c, bool pageme) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return; } c->protocol.wol.pageme = pageme; } extern bool conn_wol_get_pageme(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return false; } bool bRet; if (c->protocol.wol.pageme == 0) bRet = false; else bRet = true; return bRet; } extern void conn_wol_set_anongame_player(t_connection * c, t_anongame_wol_player * anongame_player) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return; } if (!anongame_player) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL anongame_player"); return; } c->protocol.wol.anongame_player = anongame_player; } extern t_anongame_wol_player * conn_wol_get_anongame_player(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return NULL; } return c->protocol.wol.anongame_player; } extern int conn_client_readmemory(t_connection * c, unsigned int request_id, unsigned int offset, unsigned int length) { t_packet * rpacket; t_clienttag clienttag; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } clienttag = conn_get_clienttag(c); // disallow clients that doesn't support SID_READMEMORY if (clienttag != CLIENTTAG_STARCRAFT_UINT && clienttag != CLIENTTAG_BROODWARS_UINT && clienttag != CLIENTTAG_STARJAPAN_UINT && clienttag != CLIENTTAG_SHAREWARE_UINT && clienttag != CLIENTTAG_DIABLORTL_UINT && clienttag != CLIENTTAG_DIABLOSHR_UINT && clienttag != CLIENTTAG_WARCIIBNE_UINT) { return -1; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_readmemory)); packet_set_type(rpacket, SERVER_READMEMORY); bn_int_set(&rpacket->u.server_readmemory.request_id, request_id); bn_int_set(&rpacket->u.server_readmemory.address, offset); bn_int_set(&rpacket->u.server_readmemory.length, length); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } extern int conn_client_requiredwork(t_connection * c, const char * filename) { t_packet * rpacket; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_requiredwork)); packet_set_type(rpacket, SERVER_REQUIREDWORK); packet_append_string(rpacket, filename); // filename should be "IX86ExtraWork.mpq" conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/connection.h000066400000000000000000000502771462640461400204060ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_CONNECTION_TYPES #define INCLUDED_CONNECTION_TYPES #ifdef CONNECTION_INTERNAL_ACCESS #include #ifdef JUST_NEED_TYPES # include "game.h" # include "channel.h" # include "account.h" # include "quota.h" # include "character.h" # include "versioncheck.h" # include "anongame.h" # include "anongame_wol.h" # include "realm.h" # include "common/queue.h" # include "common/tag.h" # include "common/elist.h" # include "common/packet.h" # include "common/rcm.h" #else # define JUST_NEED_TYPES # include "game.h" # include "channel.h" # include "account.h" # include "quota.h" # include "character.h" # include "versioncheck.h" # include "anongame.h" # include "anongame_wol.h" # include "realm.h" # include "common/queue.h" # include "common/tag.h" # include "common/elist.h" # include "common/packet.h" # include "common/rcm.h" # undef JUST_NEED_TYPES #endif #endif namespace pvpgn { namespace bnetd { typedef enum { conn_class_init, conn_class_bnet, conn_class_file, conn_class_bot, conn_class_telnet, conn_class_ircinit, /* IRC based protocol INIT*/ conn_class_irc, /* Internet Relay Chat */ conn_class_wol, /* Westwood Chat and Game protocol (IRC based) */ conn_class_wserv, /* Westwood servserv (IRC based) */ conn_class_wgameres, /* Westwood Gameresolution */ conn_class_wladder, /* Westwood Ladder server */ conn_class_apireg, /* Westwood API Register */ conn_class_d2cs_bnetd, conn_class_w3route, conn_class_none } t_conn_class; typedef enum { conn_state_empty, conn_state_initial, conn_state_connected, conn_state_loggedin, conn_state_destroy, conn_state_bot_username, conn_state_bot_password, conn_state_untrusted, conn_state_pending_raw } t_conn_state; #ifdef CONNECTION_INTERNAL_ACCESS typedef enum { conn_flags_welcomed = 0x01, conn_flags_udpok = 0x02, conn_flags_joingamewhisper = 0x04, conn_flags_leavegamewhisper = 0x08, conn_flags_echoback = 0x10 } t_conn_flags; #endif typedef struct connection #ifdef CONNECTION_INTERNAL_ACCESS { struct { int tcp_sock; unsigned int tcp_addr; unsigned short tcp_port; int udp_sock; unsigned int udp_addr; unsigned short udp_port; unsigned int local_addr; unsigned short local_port; unsigned int real_local_addr; unsigned short real_local_port; int fdw_idx; } socket; /* IP and socket specific data */ struct { t_conn_class cclass; t_conn_state state; unsigned int sessionkey; unsigned int sessionnum; unsigned int secret; /* random number... never sent over net unencrypted */ unsigned int flags; unsigned int latency; t_account * account; struct { t_tag archtag; t_tag gamelang; t_clienttag clienttag; char const * clientver; unsigned long versionid; /* AKA bnversion */ unsigned long gameversion; unsigned long checksum; char const * country; int tzbias; char const * host; char const * user; char const * clientexe; char const * owner; char const * cdkey; const VersionCheck *versioncheck; } client; /* client program specific data */ struct { t_queue * outqueue; /* packets waiting to be sent */ unsigned int outsize; /* amount sent from the current output packet */ unsigned int outsizep; t_packet * inqueue; /* packet waiting to be processed */ unsigned int insize; /* amount received into the current input packet */ } queues; /* network queues and related data */ struct { t_channel * channel; char const * tmpOP_channel; char const * tmpVOICE_channel; char const * away; char const * dnd; t_account * * ignore_list; unsigned int ignore_count; t_quota quota; std::time_t last_message; char const * lastsender; /* last person to whisper to this connection */ struct { char const * ircline; /* line cache for IRC connections */ unsigned int ircping; /* value of last ping */ char const * ircpass; /* hashed password for PASS authentication */ } irc; /* irc chat specific data */ } chat; /* chat and messages specific data */ t_game * game; const char * loggeduser; /* username as logged in or given (not taken from account) */ struct connection * bound; /* matching Diablo II auth connection */ t_elist timers; /* cached list of timers for cleaning */ /* FIXME: this d2/w3 specific data could be unified into an union */ struct { t_realm * realm; t_rcm_regref realm_regref; t_character * character; char const * realminfo; char const * charname; } d2; struct { char const * w3_playerinfo; /* ADDED BY UNDYING SOULZZ 4/7/02 */ std::time_t anongame_search_starttime; /* [zap-zero] 20020527 - matching w3route connection for game connection / matching game connection for w3route connection */ /* FIXME: this "optimization" is so confusing leading to many possible bugs */ struct connection * routeconn; t_anongame * anongame; /* those will be filled when recieving 0x53ff and wiped out after 54ff */ char const * client_proof; char const * server_proof; } w3; struct { int ingame; /* Are we in a game channel? */ int codepage; int findme; /* Allow others to find me? */ int pageme; /* Allow others to page me? */ char const * apgar; /* WOL User Password (encrypted) */ t_anongame_wol_player * anongame_player; } wol; std::time_t cr_time; /* Pass fail count for bruteforce protection */ unsigned int passfail_count; /* connection flag substituting some other values */ unsigned int cflags; } protocol; } #endif t_connection; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_CONNECTION_PROTOS #define INCLUDED_CONNECTION_PROTOS #include #define JUST_NEED_TYPES #include "common/packet.h" #include "common/queue.h" #include "channel.h" #include "game.h" #include "account.h" #include "common/list.h" #include "character.h" #include "versioncheck.h" #include "timer.h" #include "anongame.h" #include "anongame_wol.h" #include "realm.h" #include "message.h" #include "common/tag.h" #include "common/fdwatch.h" #undef JUST_NEED_TYPES #define DESTROY_FROM_CONNLIST 0 #define DESTROY_FROM_DEADLIST 1 namespace pvpgn { namespace bnetd { extern t_anongame * conn_create_anongame(t_connection * c); extern void conn_destroy_anongame(t_connection * c); extern t_anongame * conn_get_anongame(t_connection *c); extern void conn_shutdown(t_connection * c, std::time_t now, t_timer_data foo); extern void conn_test_latency(t_connection * c, std::time_t now, t_timer_data delta); extern char const * conn_class_get_str(t_conn_class cclass); extern char const * conn_state_get_str(t_conn_state state); extern t_connection * conn_create(int tsock, int usock, unsigned int real_local_addr, unsigned short real_local_port, unsigned int local_addr, unsigned short local_port, unsigned int addr, unsigned short port); extern void conn_destroy(t_connection * c, t_elem ** elem, int conn_or_dead_list); extern int conn_match(t_connection const * c, char const * user); extern t_conn_class conn_get_class(t_connection const * c); extern void conn_set_class(t_connection * c, t_conn_class cclass); extern t_conn_state conn_get_state(t_connection const * c); extern void conn_set_state(t_connection * c, t_conn_state state); extern unsigned int conn_get_sessionkey(t_connection const * c); extern unsigned int conn_get_sessionnum(t_connection const * c); extern unsigned int conn_get_secret(t_connection const * c); extern unsigned int conn_get_addr(t_connection const * c); extern unsigned short conn_get_port(t_connection const * c); extern unsigned int conn_get_local_addr(t_connection const * c); extern unsigned short conn_get_local_port(t_connection const * c); extern unsigned int conn_get_real_local_addr(t_connection const * c); extern unsigned short conn_get_real_local_port(t_connection const * c); extern unsigned int conn_get_game_addr(t_connection const * c); extern int conn_set_game_addr(t_connection * c, unsigned int game_addr); extern unsigned short conn_get_game_port(t_connection const * c); extern int conn_set_game_port(t_connection * c, unsigned short game_port); extern void conn_set_host(t_connection * c, char const * host); extern void conn_set_user(t_connection * c, char const * user); extern const char * conn_get_user(t_connection const * c); extern void conn_set_owner(t_connection * c, char const * owner); extern const char * conn_get_owner(t_connection const * c); extern void conn_set_cdkey(t_connection * c, char const * cdkey); extern char const * conn_get_clientexe(t_connection const * c); extern void conn_set_clientexe(t_connection * c, char const * clientexe); extern t_tag conn_get_archtag(t_connection const * c); extern void conn_set_archtag(t_connection * c, t_tag archtag); extern t_tag conn_get_gamelang(t_connection const * c); extern void conn_set_gamelang(t_connection * c, t_tag gamelang); extern t_clienttag conn_get_clienttag(t_connection const * c); extern t_clienttag conn_get_fake_clienttag(t_connection const * c); extern void conn_set_clienttag(t_connection * c, t_clienttag clienttag); extern unsigned long conn_get_versionid(t_connection const * c); extern int conn_set_versionid(t_connection * c, unsigned long versionid); extern unsigned long conn_get_gameversion(t_connection const * c); extern int conn_set_gameversion(t_connection * c, unsigned long gameversion); extern unsigned long conn_get_checksum(t_connection const * c); extern int conn_set_checksum(t_connection * c, unsigned long checksum); extern char const * conn_get_clientver(t_connection const * c); extern void conn_set_clientver(t_connection * c, char const * clientver); extern int conn_get_tzbias(t_connection const * c); extern void conn_set_tzbias(t_connection * c, int tzbias); extern int conn_set_loggeduser(t_connection * c, char const * username); extern char const * conn_get_loggeduser(t_connection const * c); extern unsigned int conn_get_flags(t_connection const * c); extern int conn_set_flags(t_connection * c, unsigned int flags); extern void conn_add_flags(t_connection * c, unsigned int flags); extern void conn_del_flags(t_connection * c, unsigned int flags); extern unsigned int conn_get_latency(t_connection const * c); extern void conn_set_latency(t_connection * c, unsigned int ms); extern char const * conn_get_awaystr(t_connection const * c); extern int conn_set_awaystr(t_connection * c, char const * away); extern char const * conn_get_dndstr(t_connection const * c); extern int conn_set_dndstr(t_connection * c, char const * dnd); extern int conn_add_ignore(t_connection * c, t_account * account); extern int conn_del_ignore(t_connection * c, t_account const * account); extern int conn_add_watch(t_connection * c, t_account * account, t_clienttag clienttag); extern int conn_del_watch(t_connection * c, t_account * account, t_clienttag clienttag); extern t_channel * conn_get_channel(t_connection const * c); extern int conn_set_channel_var(t_connection * c, t_channel * channel); extern int conn_set_channel(t_connection * c, char const * channelname); extern int conn_part_channel(t_connection * c); extern int conn_kick_channel(t_connection * c, char const * text); extern int conn_quit_channel(t_connection * c, char const * text); extern t_game * conn_get_game(t_connection const * c); extern int conn_set_game(t_connection * c, char const * gamename, char const * gamepass, char const * gameinfo, t_game_type type, int version); extern unsigned int conn_get_tcpaddr(t_connection * c); extern t_packet * conn_get_in_queue(t_connection * c); extern void conn_put_in_queue(t_connection * c, t_packet *packet); extern unsigned int conn_get_in_size(t_connection const * c); extern void conn_set_in_size(t_connection * c, unsigned int size); extern unsigned int conn_get_out_size(t_connection const * c); extern void conn_set_out_size(t_connection * c, unsigned int size); extern int conn_push_outqueue(t_connection * c, t_packet * packet); extern t_packet * conn_peek_outqueue(t_connection * c); extern t_packet * conn_pull_outqueue(t_connection * c); extern int conn_clear_outqueue(t_connection * c); extern void conn_close_read(t_connection * c); extern int conn_check_ignoring(t_connection const * c, char const * me); extern t_account * conn_get_account(t_connection const * c); extern void conn_login(t_connection * c, t_account * account, const char *loggeduser); extern int conn_get_socket(t_connection const * c); extern int conn_get_game_socket(t_connection const * c); extern int conn_set_game_socket(t_connection * c, int usock); extern char const * conn_get_username_real(t_connection const * c, char const * fn, unsigned int ln); #define conn_get_username(C) conn_get_username_real(C,__FILE__,__LINE__) extern char const * conn_get_chatname(t_connection const * c); extern int conn_unget_chatname(t_connection const * c, char const * name); extern char const * conn_get_chatcharname(t_connection const * c, t_connection const * dst); extern int conn_unget_chatcharname(t_connection const * c, char const * name); extern t_message_class conn_get_message_class(t_connection const * c, t_connection const * dst); extern unsigned int conn_get_userid(t_connection const * c); extern char const * conn_get_playerinfo(t_connection const * c); extern int conn_set_playerinfo(t_connection const * c, char const * playerinfo); extern char const * conn_get_realminfo(t_connection const * c); extern int conn_set_realminfo(t_connection * c, char const * realminfo); extern char const * conn_get_charname(t_connection const * c); extern int conn_set_charname(t_connection * c, char const * charname); extern int conn_set_idletime(t_connection * c); extern unsigned int conn_get_idletime(t_connection const * c); extern t_realm * conn_get_realm(t_connection const * c); extern int conn_set_realm(t_connection * c, t_realm * realm); extern int conn_set_character(t_connection * c, t_character * ch); extern int conn_bind(t_connection * c1, t_connection * c2); extern void conn_set_country(t_connection * c, char const * country); extern char const * conn_get_country(t_connection const * c); extern int conn_quota_exceeded(t_connection * c, char const * message); extern int conn_set_lastsender(t_connection * c, char const * sender); extern char const * conn_get_lastsender(t_connection const * c); const VersionCheck *conn_get_versioncheck(t_connection *c); bool conn_set_versioncheck(t_connection *c, const VersionCheck* versioncheck); extern int conn_get_echoback(t_connection * c); extern void conn_set_echoback(t_connection * c, int echoback); extern int conn_set_ircline(t_connection * c, char const * line); extern char const * conn_get_ircline(t_connection const * c); extern int conn_set_ircpass(t_connection * c, char const * pass); extern char const * conn_get_ircpass(t_connection const * c); extern int conn_set_ircping(t_connection * c, unsigned int ping); extern unsigned int conn_get_ircping(t_connection const * c); extern int conn_set_udpok(t_connection * c); extern int conn_get_welcomed(t_connection const * c); extern void conn_set_welcomed(t_connection * c, int welcomed); extern int conn_set_w3_playerinfo(t_connection * c, char const * w3_playerinfo); extern const char * conn_get_w3_playerinfo(t_connection * c); extern int conn_get_crtime(t_connection *c); extern int conn_set_w3_loginreq(t_connection * c, char const * loginreq); extern char const * conn_get_w3_loginreq(t_connection * c); extern int conn_set_routeconn(t_connection * c, t_connection * rc); extern t_connection * conn_get_routeconn(t_connection const * c); extern int connlist_create(void); extern void connlist_reap(void); extern int connlist_destroy(void); extern t_list * connlist(void); extern t_connection * connlist_find_connection_by_sessionkey(unsigned int sessionkey); extern t_connection * connlist_find_connection_by_socket(int socket); extern t_connection * connlist_find_connection_by_sessionnum(unsigned int sessionnum); extern t_connection * connlist_find_connection_by_name(char const * name, t_realm * realm); /* any chat name format */ extern t_connection * connlist_find_connection_by_accountname(char const * username); extern t_connection * connlist_find_connection_by_charname(char const * charname, char const * realmname); extern t_connection * connlist_find_connection_by_account(t_account * account); extern t_connection * connlist_find_connection_by_uid(unsigned int uid); extern int connlist_get_length(void); extern unsigned int connlist_login_get_length(void); extern int connlist_total_logins(void); extern int conn_set_joingamewhisper_ack(t_connection * c, unsigned int value); extern int conn_get_joingamewhisper_ack(t_connection * c); extern int conn_set_leavegamewhisper_ack(t_connection * c, unsigned int value); extern int conn_get_leavegamewhisper_ack(t_connection * c); extern int conn_set_anongame_search_starttime(t_connection * c, std::time_t t); extern std::time_t conn_get_anongame_search_starttime(t_connection * c); extern int conn_get_user_count_by_clienttag(t_clienttag ct); extern unsigned int connlist_count_connections(unsigned int addr); extern int conn_update_w3_playerinfo(t_connection * c); extern int conn_get_passfail_count(t_connection * c); extern int conn_set_passfail_count(t_connection * c, unsigned int failcount); extern int conn_increment_passfail_count(t_connection * c); extern char const * conn_get_client_proof(t_connection * c); extern int conn_set_client_proof(t_connection * c, char const * client_proof); extern char const * conn_get_server_proof(t_connection * c); extern int conn_set_server_proof(t_connection * c, char const * server_proof); extern int conn_set_tmpOP_channel(t_connection * c, char const * tmpOP_channel); extern char const * conn_get_tmpOP_channel(t_connection * c); extern int conn_set_tmpVOICE_channel(t_connection * c, char const * tmpVOICE_channel); extern char const * conn_get_tmpVOICE_channel(t_connection * c); extern t_elist *conn_get_timer(t_connection * c); extern int conn_add_fdwatch(t_connection *c, fdwatch_handler handle); extern int conn_is_irc_variant(t_connection * c); /* Westwood Online Extensions */ extern int conn_get_wol(t_connection * c); extern void conn_wol_set_apgar(t_connection * c, char const * apgar); extern char const * conn_wol_get_apgar(t_connection * c); extern void conn_wol_set_codepage(t_connection * c, int codepage); extern int conn_wol_get_codepage(t_connection * c); extern void conn_wol_set_findme(t_connection * c, bool findme); extern bool conn_wol_get_findme(t_connection * c); extern void conn_wol_set_pageme(t_connection * c, bool pageme); extern bool conn_wol_get_pageme(t_connection * c); extern void conn_wol_set_anongame_player(t_connection * c, t_anongame_wol_player * anongame_player); extern t_anongame_wol_player * conn_wol_get_anongame_player(t_connection * c); extern int conn_client_readmemory(t_connection * c, unsigned int request_id, unsigned int offset, unsigned int length); extern int conn_client_requiredwork(t_connection * c, const char * filename); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/file.cpp000066400000000000000000000220461462640461400175120ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "file.h" #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "common/eventlog.h" #include "common/xalloc.h" #include "common/bnettime.h" #include "common/packet.h" #include "common/util.h" #include "common/bn_type.h" #include "common/tag.h" #include "prefs.h" #include "connection.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static std::string file_get_info(t_connection * c, char const * rawname, unsigned int * len, bn_long * modtime); /* Requested files aliases */ const char * requestfiles[] = { "termsofservice-", "termsofservice.txt", "newaccount-", "newaccount.txt", // used in warcraft 3 after agree with TOS "chathelp-war3-", "chathelp-war3.txt", "matchmaking-war3-", "matchmaking-war3.dat", // FIXME: (HarpyWar) this file should be in files, not in i18n "tos_", "termsofservice.txt", "tos-unicode_", "termsofservice.txt", NULL, NULL }; static std::string file_find_localized(t_connection * c, const char *rawname) { const char ** pattern, **alias; size_t rawnamelen = std::strlen(rawname); for (pattern = requestfiles, alias = requestfiles + 1; *pattern; pattern += 2, alias += 2) { // Check if there is an alias file available for this kind of file if (!std::strncmp(rawname, *pattern, std::strlen(*pattern))) { t_gamelang gamelang; if (gamelang = conn_get_gamelang_localized(c)) return i18n_filename(*alias, gamelang); // FIXME: when file is transferring by bnftp protofol client doesn't provide a language // but we can extract it from the filename, so do it in next code // if there is no country tag in the file (just in case to prevent crash from invalid filename) if ((strlen(*pattern) + 4) > rawnamelen) return std::string(); // get language tag from the file name (like "termsofservice-ruRU.txt") // (it used in War3) char langstr[5]; strncpy(langstr, rawname + std::strlen(*pattern), 4); langstr[4] = 0; gamelang = tag_str_to_uint(langstr); // if language is invalid then try find it by country (like "tos_USA.txt") // (it used in D1, SC, War2) bool found; language_find_by_tag(gamelang, found); if (!found) { strncpy(langstr, rawname + std::strlen(*pattern), 3); langstr[3] = 0; gamelang = gamelang_get_by_country(langstr); } return i18n_filename(*alias, gamelang); } } // if not found return source file return std::string(); } static std::string file_get_info(t_connection * c, char const * rawname, unsigned int * len, bn_long * modtime) { if (!rawname) { throw std::runtime_error("NULL rawname"); } if (!len) { throw std::runtime_error("NULL len"); } if (!modtime) { throw std::runtime_error("NULL modtime"); } if (std::strchr(rawname, '/') || std::strchr(rawname, '\\')) { throw std::runtime_error(fmt::format("got rawname containing '/' or '\\' \"{}\"", rawname)); } struct stat sfile; std::string filename = file_find_localized(c, rawname); // if localized file not found in "i18n" if (filename.empty() || stat(filename.c_str(), &sfile) != 0) { // try find it in "files" filename = fmt::format("{}/{}", prefs_get_filedir(), rawname).c_str(); if (stat(filename.c_str(), &sfile) < 0) /* try again */ { /* FIXME: check for lower-case version of filename */ throw std::runtime_error(fmt::format("file {} not found", filename)); } } *len = (unsigned int)sfile.st_size; t_bnettime bt = time_to_bnettime(sfile.st_mtime, 0); bnettime_to_bn_long(bt, modtime); return filename; } extern int file_to_mod_time(t_connection * c, char const * rawname, bn_long * modtime) { if (!rawname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL rawname"); return -1; } if (!modtime) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL modtime"); return -1; } try { unsigned int len = 0; file_get_info(c, rawname, &len, modtime); } catch (const std::runtime_error& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); return -1; } return 0; } /* Send a file. If the file doesn't exist we still need to respond * to the file request. This will set filelen to 0 and send the server * reply message and the client will be happy and not hang. */ extern int file_send(t_connection * c, char const * rawname, unsigned int adid, unsigned int etag, unsigned int startoffset, int need_header) { t_packet * rpacket; std::FILE * fp; int nbytes; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!rawname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL rawname"); return -1; } if (!(rpacket = packet_create(packet_class_file))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create file packet"); return -1; } packet_set_size(rpacket, sizeof(t_server_file_reply)); packet_set_type(rpacket, SERVER_FILE_REPLY); unsigned int filelen = 0; std::string filename; try { filename = file_get_info(c, rawname, &filelen, &rpacket->u.server_file_reply.timestamp); } catch (const std::runtime_error& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); return -1; } if (!(fp = std::fopen(filename.c_str(), "rb"))) { /* FIXME: check for lower-case version of filename */ eventlog(eventlog_level_error, __FUNCTION__, "stat() succeeded yet could not open file \"{}\" for reading (std::fopen: {})", filename.c_str(), std::strerror(errno)); filelen = 0; } if (fp) { if (startoffset < filelen) { std::fseek(fp, startoffset, SEEK_SET); } else { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] startoffset is beyond end of file ({}>{})", conn_get_socket(c), startoffset, filelen); /* Keep the real filesize. Battle.net does it the same way ... */ std::fclose(fp); fp = NULL; } } if (need_header) { /* send the header from the server with the rawname and length. */ bn_int_set(&rpacket->u.server_file_reply.filelen, filelen); bn_int_set(&rpacket->u.server_file_reply.adid, adid); bn_int_set(&rpacket->u.server_file_reply.extensiontag, etag); /* rpacket->u.server_file_reply.timestamp is set above */ packet_append_string(rpacket, rawname); conn_push_outqueue(c, rpacket); } packet_del_ref(rpacket); /* Now send the data. Since it may be longer than a packet; we use * the raw packet class. */ if (!fp) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] sending no data for file \"{}\" (\"{}\")", conn_get_socket(c), rawname, filename.c_str()); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] sending file \"{}\" (\"{}\") of length {}", conn_get_socket(c), rawname, filename.c_str(), filelen); for (;;) { if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create raw packet"); if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close file \"{}\" after reading (std::fclose: {})", filename.c_str(), std::strerror(errno)); return -1; } if ((nbytes = std::fread(packet_get_raw_data_build(rpacket, 0), 1, MAX_PACKET_SIZE, fp))<(int)MAX_PACKET_SIZE) { if (nbytes>0) /* send out last portion */ { packet_set_size(rpacket, nbytes); conn_push_outqueue(c, rpacket); } packet_del_ref(rpacket); if (std::ferror(fp)) eventlog(eventlog_level_error, __FUNCTION__, "read failed before EOF on file \"{}\" (std::fread: {})", rawname, std::strerror(errno)); break; } packet_set_size(rpacket, nbytes); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close file \"{}\" after reading (std::fclose: {})", rawname, std::strerror(errno)); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/file.h000066400000000000000000000023731462640461400171600ustar00rootroot00000000000000/* * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_FILE_PROTOS #define INCLUDED_FILE_PROTOS #define JUST_NEED_TYPES # include "common/bn_type.h" # include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int file_to_mod_time(t_connection * c, char const * rawname, bn_long * modtime); extern int file_send(t_connection * c, char const * rawname, unsigned int adid, unsigned int etag, unsigned int startoffset, int need_header); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/file_plain.cpp000066400000000000000000000130301462640461400206660ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2002,2003,2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "file_plain.h" #include #include #include "common/eventlog.h" #include "common/util.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { /* plain file storage API functions */ static t_attr * plain_read_attr(const char *filename, const char *key); static int plain_read_attrs(const char *filename, t_read_attr_func cb, void *data); static int plain_write_attrs(const char *filename, const t_hlist *attributes); /* file_engine struct populated with the functions above */ t_file_engine file_plain = { plain_read_attr, plain_read_attrs, plain_write_attrs }; static int plain_write_attrs(const char *filename, const t_hlist *attributes) { std::FILE * accountfile; t_hlist * curr; t_attr * attr; char const * key; char const * val; if (!(accountfile = std::fopen(filename, "w"))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to open file \"{}\" for writing (std::fopen: {})", filename, std::strerror(errno)); return -1; } hlist_for_each(curr, attributes) { attr = hlist_entry(curr, t_attr, link); if (attr_get_key(attr)) key = escape_chars(attr_get_key(attr), std::strlen(attr_get_key(attr))); else { eventlog(eventlog_level_error, __FUNCTION__, "attribute with NULL key in list"); key = NULL; } if (attr_get_val(attr)) val = escape_chars(attr_get_val(attr), std::strlen(attr_get_val(attr))); else { eventlog(eventlog_level_debug, __FUNCTION__, "attribute with NULL val in list key=\"{}\"", key ? key : "NULL"); val = NULL; } if (key && val) { if (std::strncmp("BNET\\CharacterDefault\\", key, 20) == 0) { eventlog(eventlog_level_debug, __FUNCTION__, "skipping attribute key=\"{}\"", key); } else { eventlog(eventlog_level_debug, __FUNCTION__, "saving attribute key=\"{}\" val=\"{}\"", key, val); std::fprintf(accountfile, "\"%s\"=\"%s\"\n", key, val); } } else eventlog(eventlog_level_debug, __FUNCTION__, "could not save attribute key=\"{}\"", key ? key : "NULL"); if (key) xfree((void *)key); /* avoid warning */ if (val) xfree((void *)val); /* avoid warning */ attr_clear_dirty(attr); } if (std::fclose(accountfile) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close account file \"{}\" after writing (std::fclose: {})", filename, std::strerror(errno)); return -1; } return 0; } static int plain_read_attrs(const char *filename, t_read_attr_func cb, void *data) { std::FILE * accountfile; unsigned int line; char const * buff; unsigned int len; char * esckey; char * escval; char * key; char * val; if (!(accountfile = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open account file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } for (line = 1; (buff = file_get_line(accountfile)); line++) { if (buff[0] == '#' || buff[0] == '\0') { continue; } if (std::strlen(buff) < 6) /* "?"="" */ { eventlog(eventlog_level_error, __FUNCTION__, "malformed line {} of account file \"{}\"", line, filename); continue; } len = std::strlen(buff) - 5 + 1; /* - ""="" + NUL */ esckey = (char*)xmalloc(len); escval = (char*)xmalloc(len); if (std::sscanf(buff, "\"%[^\"]\" = \"%[^\"]\"", esckey, escval) != 2) { if (std::sscanf(buff, "\"%[^\"]\" = \"\"", esckey) != 1) /* hack for an empty value field */ { eventlog(eventlog_level_error, __FUNCTION__, "malformed entry on line {} of account file \"{}\"", line, filename); xfree(escval); xfree(esckey); continue; } escval[0] = '\0'; } key = unescape_chars(esckey); val = unescape_chars(escval); /* eventlog(eventlog_level_debug,__FUNCTION__,"std::strlen(esckey)=%u (%c), len=%u",std::strlen(esckey),esckey[0],len);*/ xfree(esckey); xfree(escval); if (cb(key, val, data)) eventlog(eventlog_level_error, __FUNCTION__, "got error from callback (key: '{}' val:'{}')", key, val); if (key) xfree((void *)key); /* avoid warning */ if (val) xfree((void *)val); /* avoid warning */ } file_get_line(NULL); // clear file_get_line buffer if (std::fclose(accountfile) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close account file \"{}\" after reading (std::fclose: {})", filename, std::strerror(errno)); return 0; } static t_attr * plain_read_attr(const char *filename, const char *key) { /* flat file storage doesnt know to read selective attributes */ return NULL; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/file_plain.h000066400000000000000000000004221462640461400203340ustar00rootroot00000000000000#ifndef JUST_NEED_TYPES #ifndef INClUDED_FILE_PLAIN_PROTOS #define INClUDED_FILE_PLAIN_PROTOS #include "storage_file.h" namespace pvpgn { namespace bnetd { extern t_file_engine file_plain; } } #endif /* INClUDED_FILE_PLAIN_PROTOS */ #endif /* JUST_NEED_TYPES */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/friends.cpp000066400000000000000000000205071462640461400202250ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "friends.h" #include #include "compat/strcasecmp.h" #include "common/list.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern t_account * friend_get_account(t_friend * fr) { if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL friend"); return nullptr; } return fr->friendacc; } extern int friend_set_account(t_friend * fr, t_account * acc) { if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL friend"); return -1; } if (acc == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } fr->friendacc = acc; return 0; } extern char friend_get_mutual(t_friend * fr) { if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL friend"); return 0; } return fr->mutual; } extern int friend_set_mutual(t_friend * fr, int mutual) { if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL friend"); return -1; } if (mutual != FRIEND_UNLOADEDMUTUAL && mutual != FRIEND_NOTMUTUAL && mutual != FRIEND_ISMUTUAL) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid mutual status"); return -1; } fr->mutual = mutual; return 0; } extern int friendlist_unload(t_list * flist) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return -1; } t_friend * fr = nullptr; t_elem * curr = nullptr; LIST_TRAVERSE(flist, curr) { fr = (t_friend*)elem_get_data(curr); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } fr->mutual = FRIEND_UNLOADEDMUTUAL; } return 0; } extern int friendlist_close(t_list * flist) { if (flist == nullptr) { eventlog(eventlog_level_trace, __FUNCTION__, "got NULL flist"); return -1; } t_friend * fr = nullptr; t_elem * curr = nullptr; LIST_TRAVERSE(flist, curr) { fr = (t_friend*)elem_get_data(curr); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (list_remove_elem(flist, &curr) != 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove elem from flist"); delete(fr); } list_destroy(flist); return 0; } extern int friendlist_purge(t_list * flist) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return -1; } t_friend * fr = nullptr; t_elem * curr = nullptr; LIST_TRAVERSE(flist, curr) { fr = (t_friend*)elem_get_data(curr); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (fr->mutual == FRIEND_UNLOADEDMUTUAL) { if (list_remove_elem(flist, &curr) != 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); } } return 0; } extern int friendlist_add_account(t_list * flist, t_account * acc, int mutual) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return -1; } if (acc == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (mutual != FRIEND_UNLOADEDMUTUAL && mutual != FRIEND_NOTMUTUAL && mutual != FRIEND_ISMUTUAL) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid mutual status"); return -1; } t_friend * fr = new t_friend; //(t_friend*)xmalloc(sizeof(t_friend)); fr->friendacc = acc; fr->mutual = mutual; list_append_data(flist, fr); return 0; } extern int friendlist_remove_friend(t_list * flist, t_friend * fr) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return -1; } if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL friend"); return -1; } t_elem * elem = nullptr; if (list_remove_data(flist, fr, &elem) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); return -1; } delete(fr); return 0; } extern int friendlist_remove_account(t_list * flist, t_account * acc) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return -1; } if (acc == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } t_friend * fr = friendlist_find_account(flist, acc); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "could not find friend"); return -1; } t_elem * elem = nullptr; if (list_remove_data(flist, fr, &elem) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); return -1; } delete(fr); return 0; } extern int friendlist_remove_username(t_list * flist, const char * accname) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return -1; } if (accname == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account name"); return -1; } t_friend * fr = friendlist_find_username(flist, accname); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "could not find friend"); return -1; } t_elem * elem = nullptr; if (list_remove_data(flist, fr, &elem) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); return -1; } delete(fr); return 0; } extern t_friend * friendlist_find_account(t_list * flist, t_account * acc) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return nullptr; } if (acc == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return nullptr; } t_friend * fr = nullptr; t_elem * curr = nullptr; LIST_TRAVERSE(flist, curr) { fr = (t_friend*)elem_get_data(curr); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (fr->friendacc == acc) return fr; } return nullptr; } extern t_friend * friendlist_find_username(t_list * flist, const char * accname) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return nullptr; } if (accname == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account name"); return nullptr; } t_friend * fr = nullptr; t_elem * curr = nullptr; LIST_TRAVERSE(flist, curr) { fr = (t_friend*)elem_get_data(curr); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (strcasecmp(account_get_name(fr->friendacc), accname) == 0) return fr; } return nullptr; } extern t_friend * friendlist_find_uid(t_list * flist, unsigned int uid) { if (flist == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL flist"); return nullptr; } t_friend * fr = nullptr; t_elem * curr = nullptr; LIST_TRAVERSE(flist, curr) { fr = (t_friend*)elem_get_data(curr); if (fr == nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (account_get_uid(fr->friendacc) == uid) return fr; } return nullptr; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/friends.h000066400000000000000000000037371462640461400177000ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_FRIENDS_H #define INCLUDED_FRIENDS_H #define FRIEND_UNLOADEDMUTUAL -1 #define FRIEND_NOTMUTUAL 0 #define FRIEND_ISMUTUAL 1 #include "account.h" namespace pvpgn { namespace bnetd { typedef struct friend_struct { /* FRIEND_UNLOADEDMUTUAL: unloaded(used to remove deleted elems when reload) * FRIEND_NOTMUTUAL: not mutual * FRIEND_ISMUTUAL: is mutual */ int mutual; t_account *friendacc = nullptr; } t_friend; #ifndef JUST_NEED_TYPES extern t_account * friend_get_account(t_friend *); extern int friend_set_account(t_friend *, t_account *); extern char friend_get_mutual(t_friend *); extern int friend_set_mutual(t_friend *, int); extern int friendlist_unload(t_list *); extern int friendlist_close(t_list *); extern int friendlist_purge(t_list *); extern int friendlist_add_account(t_list *, t_account *, int); extern int friendlist_remove_friend(t_list *, t_friend *); extern int friendlist_remove_account(t_list *, t_account *); extern int friendlist_remove_username(t_list *, const char *); extern t_friend * friendlist_find_account(t_list *, t_account *); extern t_friend * friendlist_find_username(t_list *, const char *); extern t_friend * friendlist_find_uid(t_list *, unsigned int); #endif } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/game.cpp000066400000000000000000001761521462640461400175140ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define GAME_INTERNAL_ACCESS #include "common/setup_before.h" #include "game.h" #include #include #include #include "compat/rename.h" #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/addr.h" #include "common/bnettime.h" #include "connection.h" #include "channel.h" #include "server.h" #include "account.h" #include "account_wrap.h" #include "prefs.h" #include "watch.h" #include "realm.h" #include "ladder.h" #include "game_conv.h" #include "common/setup_after.h" #ifdef WITH_LUA #include "luainterface.h" #endif namespace pvpgn { namespace bnetd { DECLARE_ELIST_INIT(gamelist_head); static int glist_length = 0; static int totalcount = 0; static void game_choose_host(t_game * game); static void game_destroy(t_game * game); static int game_report(t_game * game); static void game_choose_host(t_game * game) { unsigned int i; if (game->count < 1) { eventlog(eventlog_level_error, __FUNCTION__, "game has had no connections?!"); return; } if (!game->connections) { eventlog(eventlog_level_error, __FUNCTION__, "game has NULL connections array"); return; } for (i = 0; i < game->count; i++) if (game->connections[i]) { game->owner = game->connections[i]; game->addr = conn_get_game_addr(game->connections[i]); game->port = conn_get_game_port(game->connections[i]); return; } eventlog(eventlog_level_warn, __FUNCTION__, "no valid connections found"); } extern char const * game_type_get_str(t_game_type type) { switch (type) { case game_type_none: return "NONE"; case game_type_melee: return "melee"; case game_type_topvbot: return "top vs bottom"; case game_type_ffa: return "free for all"; case game_type_oneonone: return "one on one"; case game_type_ctf: return "capture the flag"; case game_type_greed: return "greed"; case game_type_slaughter: return "slaughter"; case game_type_sdeath: return "sudden death"; case game_type_ladder: return "ladder"; case game_type_ironman: return "ironman"; case game_type_mapset: return "mapset"; case game_type_teammelee: return "team melee"; case game_type_teamffa: return "team free for all"; case game_type_teamctf: return "team capture the flag"; case game_type_pgl: return "PGL"; case game_type_diablo: return "Diablo"; case game_type_diablo2open: return "Diablo II (open)"; case game_type_diablo2closed: return "Diablo II (closed)"; case game_type_all: default: return "UNKNOWN"; } } extern char const * game_status_get_str(t_game_status status) { switch (status) { case game_status_started: return "started"; case game_status_full: return "full"; case game_status_open: return "open"; case game_status_done: return "done"; case game_status_loaded: return "loaded"; default: return "UNKNOWN"; } } extern char const * game_result_get_str(t_game_result result) { switch (result) { case game_result_none: return "NONE"; case game_result_win: return "WIN"; case game_result_loss: return "LOSS"; case game_result_draw: return "DRAW"; case game_result_disconnect: return "DISCONNECT"; case game_result_observer: return "OBSERVER"; default: return "UNKNOWN"; } } extern char const * game_option_get_str(t_game_option option) { switch (option) { case game_option_melee_normal: return "normal"; case game_option_ffa_normal: return "normal"; case game_option_oneonone_normal: return "normal"; case game_option_ctf_normal: return "normal"; case game_option_greed_10000: return "10000 minerals"; case game_option_greed_7500: return "7500 minerals"; case game_option_greed_5000: return "5000 minerals"; case game_option_greed_2500: return "2500 minerals"; case game_option_slaughter_60: return "60 minutes"; case game_option_slaughter_45: return "45 minutes"; case game_option_slaughter_30: return "30 minutes"; case game_option_slaughter_15: return "15 minutes"; case game_option_sdeath_normal: return "normal"; case game_option_ladder_countasloss: return "count as loss"; case game_option_ladder_nopenalty: return "no penalty"; case game_option_mapset_normal: return "normal"; case game_option_teammelee_4: return "4 teams"; case game_option_teammelee_3: return "3 teams"; case game_option_teammelee_2: return "2 teams"; case game_option_teamffa_4: return "4 teams"; case game_option_teamffa_3: return "3 teams"; case game_option_teamffa_2: return "2 teams"; case game_option_teamctf_4: return "4 teams"; case game_option_teamctf_3: return "3 teams"; case game_option_teamctf_2: return "2 teams"; case game_option_topvbot_7: return "7 vs all"; case game_option_topvbot_6: return "6 vs all"; case game_option_topvbot_5: return "5 vs all"; case game_option_topvbot_4: return "4 vs all"; case game_option_topvbot_3: return "3 vs all"; case game_option_topvbot_2: return "2 vs all"; case game_option_topvbot_1: return "1 vs all"; case game_option_none: return "none"; default: return "UNKNOWN"; } } extern char const * game_maptype_get_str(t_game_maptype maptype) { switch (maptype) { case game_maptype_selfmade: return "Self-Made"; case game_maptype_blizzard: return "Blizzard"; case game_maptype_ladder: return "Ladder"; case game_maptype_pgl: return "PGL"; case game_maptype_kbk: return "KBK"; case game_maptype_compusa: return "CompUSA"; default: return "Unknown"; } } extern char const * game_tileset_get_str(t_game_tileset tileset) { switch (tileset) { case game_tileset_badlands: return "Badlands"; case game_tileset_space: return "Space"; case game_tileset_installation: return "Installation"; case game_tileset_ashworld: return "Ash World"; case game_tileset_jungle: return "Jungle"; case game_tileset_desert: return "Desert"; case game_tileset_ice: return "Ice"; case game_tileset_twilight: return "Twilight"; default: return "Unknown"; } } extern char const * game_speed_get_str(t_game_speed speed) { switch (speed) { case game_speed_slowest: return "slowest"; case game_speed_slower: return "slower"; case game_speed_slow: return "slow"; case game_speed_normal: return "normal"; case game_speed_fast: return "fast"; case game_speed_faster: return "faster"; case game_speed_fastest: return "fastest"; default: return "unknown"; } } extern char const * game_difficulty_get_str(unsigned difficulty) { switch (difficulty) { case game_difficulty_normal: return "normal"; case game_difficulty_nightmare: return "nightmare"; case game_difficulty_hell: return "hell"; case game_difficulty_hardcore_normal: return "hardcore normal"; case game_difficulty_hardcore_nightmare: return "hardcore nightmare"; case game_difficulty_hardcore_hell: return "hardcore hell"; default: return "unknown"; } } extern t_game * game_create(char const * name, char const * pass, char const * info, t_game_type type, int startver, t_clienttag clienttag, unsigned long gameversion) { t_game * game; if (!name) { eventlog(eventlog_level_info, __FUNCTION__, "got NULL game name"); return NULL; } if (!pass) { eventlog(eventlog_level_info, __FUNCTION__, "got NULL game pass"); return NULL; } if (!info) { eventlog(eventlog_level_info, __FUNCTION__, "got NULL game info"); return NULL; } if (gamelist_find_game_available(name, clienttag, game_type_all)) { eventlog(eventlog_level_info, __FUNCTION__, "game \"{}\" not created because it already exists", name); return NULL; /* already have a game by that name */ } game = (t_game*)xmalloc(sizeof(t_game)); game->name = xstrdup(name); game->pass = xstrdup(pass); game->info = xstrdup(info); if (!(game->clienttag = clienttag)) { eventlog(eventlog_level_error, __FUNCTION__, "got UNKNOWN clienttag"); xfree((void *)game->info); /* avoid warning */ xfree((void *)game->pass); /* avoid warning */ xfree((void *)game->name); /* avoid warning */ xfree(game); return NULL; } game->type = type; game->addr = 0; /* will be set by first player */ game->port = 0; /* will be set by first player */ game->version = gameversion; game->startver = startver; /* start packet version */ game->status = game_status_open; game->realm = 0; game->realmname = NULL; game->id = ++totalcount; game->mapname = NULL; game->ref = 0; game->count = 0; game->owner = NULL; game->connections = NULL; game->players = NULL; game->results = NULL; game->reported_results = NULL; game->report_heads = NULL; game->report_bodies = NULL; game->create_time = now; game->start_time = (std::time_t)0; game->lastaccess_time = now; game->option = game_option_none; game->maptype = game_maptype_none; game->tileset = game_tileset_none; game->speed = game_speed_none; game->mapsize_x = 0; game->mapsize_y = 0; game->maxplayers = 0; game->bad = 0; game->description = NULL; game->flag = std::strcmp(pass, "") ? game_flag_private : game_flag_none; game->difficulty = game_difficulty_none; game_parse_info(game, info); elist_add(&gamelist_head, &game->glist_link); glist_length++; eventlog(eventlog_level_info, __FUNCTION__, "game \"{}\" (pass \"{}\") type {}({}) startver {} created", name, pass, (unsigned short)type, game_type_get_str(game->type), startver); return game; } static void game_destroy(t_game * game) { unsigned int i; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return; } #ifdef WITH_LUA lua_handle_game(game, NULL, luaevent_game_destroy); #endif elist_del(&game->glist_link); glist_length--; if (game->realmname) { realm_add_game_number(realmlist_find_realm(game->realmname), -1); } eventlog(eventlog_level_debug, __FUNCTION__, "game \"{}\" (count={} ref={}) removed from list...", game_get_name(game), game->count, game->ref); for (i = 0; i < game->count; i++) { if (game->report_bodies && game->report_bodies[i]) xfree((void *)game->report_bodies[i]); /* avoid warning */ if (game->report_heads && game->report_heads[i]) xfree((void *)game->report_heads[i]); /* avoid warning */ if (game->reported_results && game->reported_results[i]) xfree((void *)game->reported_results[i]); } if (game->realmname) xfree((void *)game->realmname); /* avoid warining */ if (game->report_bodies) xfree((void *)game->report_bodies); /* avoid warning */ if (game->report_heads) xfree((void *)game->report_heads); /* avoid warning */ if (game->results) xfree((void *)game->results); /* avoid warning */ if (game->reported_results) xfree((void *)game->reported_results); if (game->connections) xfree((void *)game->connections); /* avoid warning */ if (game->players) xfree((void *)game->players); /* avoid warning */ if (game->mapname) xfree((void *)game->mapname); /* avoid warning */ if (game->description) xfree((void *)game->description); /* avoid warning */ xfree((void *)game->info); /* avoid warning */ xfree((void *)game->pass); /* avoid warning */ if (game->name) xfree((void *)game->name); /* avoid warning */ xfree((void *)game); /* avoid warning */ eventlog(eventlog_level_info, __FUNCTION__, "game deleted"); return; } static int game_evaluate_results(t_game * game) { unsigned int i, j; unsigned int wins, losses, draws, disconnects, reports; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!game->results) { eventlog(eventlog_level_error, __FUNCTION__, "results array is NULL"); return -1; } if (!game->reported_results) { eventlog(eventlog_level_error, __FUNCTION__, "reported_results array is NULL"); return -1; } int discisloss = game_discisloss(game); for (i = 0; i < game->count; i++) { wins = losses = draws = disconnects = reports = 0; for (j = 0; j < game->count; j++) { if (game->reported_results[j]) { switch (game->reported_results[j][i]) { case game_result_win: wins++; reports++; break; case game_result_loss: losses++; reports++; break; case game_result_draw: draws++; reports++; break; case game_result_disconnect: disconnects++; reports++; break; default: break; } } } eventlog(eventlog_level_debug, __FUNCTION__, "wins: {} losses: {} draws: {} disconnects: {}", wins, losses, draws, disconnects); //now decide what result we give if (!(reports)) // no results at all - game canceled before starting { game->results[i] = game_result_none; eventlog(eventlog_level_debug, __FUNCTION__, "deciding to give \"none\" to player {}", i); } else if ((disconnects >= draws) && (disconnects >= losses) && (disconnects >= wins)) { if (discisloss) { game->results[i] = game_result_loss; //losses are also bad... eventlog(eventlog_level_debug, __FUNCTION__, "deciding to give \"loss\" to player {} (due to discisloss)", i); } else { game->results[i] = game_result_disconnect; //consider disconnects the worst case... eventlog(eventlog_level_debug, __FUNCTION__, "deciding to give \"disconnect\" to player {}", i); } } else if ((losses >= wins) && (losses >= draws)) { game->results[i] = game_result_loss; //losses are also bad... eventlog(eventlog_level_debug, __FUNCTION__, "deciding to give \"loss\" to player {}", i); } else if ((draws >= wins)) { game->results[i] = game_result_draw; eventlog(eventlog_level_debug, __FUNCTION__, "deciding to give \"draw\" to player {}", i); } else if (wins) { game->results[i] = game_result_win; eventlog(eventlog_level_debug, __FUNCTION__, "deciding to give \"win\" to player {}", i); } } return 0; } static int game_match_type(t_game_type type, const char *gametypes) { char *p, *q; int res; if (!gametypes || !gametypes[0]) return 0; gametypes = p = xstrdup(gametypes); res = 0; do { q = std::strchr(p, ','); if (q) *q = '\0'; if (!strcasecmp(p, "topvbot")) { if (type == game_type_topvbot) { res = 1; break; } } else if (!strcasecmp(p, "melee")) { if (type == game_type_melee) { res = 1; break; } } else if (!strcasecmp(p, "ffa")) { if (type == game_type_ffa) { res = 1; break; } } else if (!strcasecmp(p, "oneonone")) { if (type == game_type_oneonone) { res = 1; break; } } if (q) p = q + 1; } while (q); free((void*)gametypes); return res; } static int game_sanity_check(t_game_result * results, t_account * * players, unsigned int count, unsigned int discisloss) { unsigned int winners = 0; unsigned int losers = 0; unsigned int draws = 0; unsigned int discs = 0; for (unsigned int curr = 0; curr < count; curr++) { if (!players[curr]) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL player[{}] (of {})", curr, count); return -1; } switch (results[curr]) { case game_result_win: winners++; break; case game_result_loss: losers++; break; case game_result_draw: draws++; break; case game_result_disconnect: discs++; break; default: eventlog(eventlog_level_error, __FUNCTION__, "bad results[{}]={}", curr, (unsigned int)results[curr]); return -1; } } if (draws > 0) { if (draws != count) { eventlog(eventlog_level_error, __FUNCTION__, "some, but not all players had a draw count={} (winners={} losers={} draws={})", count, winners, losers, draws); return -1; } return 0; } if ((discisloss) && ((losers < 1) || (winners<1) || (winners>1 && (winners != losers)))) { eventlog(eventlog_level_info, __FUNCTION__, "missing winner or loser for count={} (winners={} losers={})", count, winners, losers); return -1; } return 0; } static int game_report(t_game * game) { std::FILE * fp; char * realname; char * tempname; unsigned int i; unsigned int realcount; t_ladder_info * ladder_info = NULL; char clienttag_str[5]; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!game->clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got UNKNOWN clienttag"); return -1; } if (!game->players) { eventlog(eventlog_level_error, __FUNCTION__, "player array is NULL"); return -1; } if (!game->reported_results) { eventlog(eventlog_level_error, __FUNCTION__, "reported_results array is NULL"); return -1; } if (!game->results) { eventlog(eventlog_level_error, __FUNCTION__, "results array is NULL"); return -1; } #ifdef WITH_LUA lua_handle_game(game, NULL, luaevent_game_end); #endif if (game->clienttag == CLIENTTAG_WARCRAFT3_UINT || game->clienttag == CLIENTTAG_WAR3XP_UINT) // war3 game reporting is done elsewhere, so we can skip this function return 0; if ((game->clienttag == CLIENTTAG_WCHAT_UINT) || (game->clienttag == CLIENTTAG_REDALERT_UINT) || (game->clienttag == CLIENTTAG_DUNE2000_UINT) || (game->clienttag == CLIENTTAG_NOX_UINT) || (game->clienttag == CLIENTTAG_NOXQUEST_UINT) || (game->clienttag == CLIENTTAG_RENEGADE_UINT) || (game->clienttag == CLIENTTAG_RENGDFDS_UINT) || (game->clienttag == CLIENTTAG_EMPERORBD_UINT) || (game->clienttag == CLIENTTAG_LOFLORE3_UINT) || (game->clienttag == CLIENTTAG_WWOL_UINT)) // PELISH: We are not supporting ladders for all WOL clients yet return 0; if (game->clienttag == CLIENTTAG_DIABLOSHR_UINT || game->clienttag == CLIENTTAG_DIABLORTL_UINT || game->clienttag == CLIENTTAG_DIABLO2ST_UINT || game->clienttag == CLIENTTAG_DIABLO2DV_UINT || game->clienttag == CLIENTTAG_DIABLO2XP_UINT) { if (prefs_get_report_diablo_games() == 1) /* diablo games have transient players and no reported winners/losers */ realcount = 0; else { eventlog(eventlog_level_info, __FUNCTION__, "diablo gamereport disabled: ignoring game"); return 0; } } else { game_evaluate_results(game); // evaluate results from the reported results /* "compact" the game; move all the real players to the top... */ realcount = 0; for (i = 0; i < game->count; i++) { if (!game->players[i]) { eventlog(eventlog_level_error, __FUNCTION__, "player slot {} has NULL account", i); continue; } if (game->results[i] != game_result_none) { game->players[realcount] = game->players[i]; game->results[realcount] = game->results[i]; game->report_heads[realcount] = game->report_heads[i]; game->report_bodies[realcount] = game->report_bodies[i]; realcount++; } } /* then nuke duplicate players after the real players */ for (i = realcount; i < game->count; i++) { game->players[i] = NULL; game->results[i] = game_result_none; game->report_heads[i] = NULL; game->report_bodies[i] = NULL; } if (realcount < 1) { eventlog(eventlog_level_info, __FUNCTION__, "ignoring game"); return -1; } if (game_sanity_check(game->results, game->players, realcount, game_is_ladder(game) && game_discisloss(game)) < 0) { eventlog(eventlog_level_info, __FUNCTION__, "game results ignored due to inconsistencies"); game->bad = 1; } if (((tag_check_wolv1(game->clienttag)) || (tag_check_wolv2(game->clienttag))) && (realcount == 2) && (game->results[0] == game_result_disconnect) && (game->results[0] == game->results[1])) { //FIXME: Find more general solution for that (we are not supporting more than 2 players now) DEBUG0("WOL Both players got game_result_disconnect - ignoring game"); game->bad = 1; } } eventlog(eventlog_level_debug, __FUNCTION__, "realcount={} count={}", realcount, game->count); if (realcount >= 1 && !game->bad) { if (game_is_ladder(game)) { t_ladder_id id; if (game_get_type(game) == game_type_ironman) id = ladder_id_ironman; else id = ladder_id_normal; for (i = 0; i < realcount; i++) { eventlog(eventlog_level_debug, __FUNCTION__, "realplayer {} result={}", i + 1, (unsigned int)game->results[i]); if ((tag_check_wolv1(game->clienttag)) || (tag_check_wolv2(game->clienttag))) { id = ladder_id_solo; ladder_init_account_wol(game->players[i], game->clienttag, id); switch (game->results[i]) { case game_result_win: account_inc_ladder_wins(game->players[i], game->clienttag, id); break; case game_result_loss: account_inc_ladder_losses(game->players[i], game->clienttag, id); break; case game_result_disconnect: account_inc_ladder_disconnects(game->players[i], game->clienttag, id); break; default: eventlog(eventlog_level_error, __FUNCTION__, "bad ladder game realplayer results[{}] = {}", i, game->results[i]); account_inc_ladder_disconnects(game->players[i], game->clienttag, id); } } else { ladder_init_account(game->players[i], game->clienttag, id); switch (game->results[i]) { case game_result_win: account_inc_ladder_wins(game->players[i], game->clienttag, id); account_set_ladder_last_result(game->players[i], game->clienttag, id, game_result_get_str(game_result_win)); break; case game_result_loss: account_inc_ladder_losses(game->players[i], game->clienttag, id); account_set_ladder_last_result(game->players[i], game->clienttag, id, game_result_get_str(game_result_loss)); break; case game_result_draw: account_inc_ladder_draws(game->players[i], game->clienttag, id); account_set_ladder_last_result(game->players[i], game->clienttag, id, game_result_get_str(game_result_draw)); break; case game_result_disconnect: account_inc_ladder_disconnects(game->players[i], game->clienttag, id); account_set_ladder_last_result(game->players[i], game->clienttag, id, game_result_get_str(game_result_disconnect)); break; default: eventlog(eventlog_level_error, __FUNCTION__, "bad ladder game realplayer results[{}] = {}", i, game->results[i]); account_inc_ladder_disconnects(game->players[i], game->clienttag, id); account_set_ladder_last_result(game->players[i], game->clienttag, id, game_result_get_str(game_result_disconnect)); } account_set_ladder_last_time(game->players[i], game->clienttag, id, bnettime()); } } if ((tag_check_wolv1(game->clienttag)) || (tag_check_wolv2(game->clienttag))) { id = ladder_id_solo; ladder_update_wol(game->clienttag, id, game->players, game->results); } else { ladder_info = (t_ladder_info*)xmalloc(sizeof(t_ladder_info)*realcount); if (ladder_update(game->clienttag, id, realcount, game->players, game->results, ladder_info) < 0) { eventlog(eventlog_level_info, __FUNCTION__, "unable to update ladder stats"); xfree(ladder_info); ladder_info = NULL; } } } else { if (!(tag_check_wolv1(game->clienttag)) || !(tag_check_wolv2(game->clienttag))) { for (i = 0; i < realcount; i++) { switch (game->results[i]) { case game_result_win: account_inc_normal_wins(game->players[i], game->clienttag); account_set_normal_last_result(game->players[i], game->clienttag, game_result_get_str(game_result_win)); break; case game_result_loss: account_inc_normal_losses(game->players[i], game->clienttag); account_set_normal_last_result(game->players[i], game->clienttag, game_result_get_str(game_result_loss)); break; case game_result_draw: account_inc_normal_draws(game->players[i], game->clienttag); account_set_normal_last_result(game->players[i], game->clienttag, game_result_get_str(game_result_draw)); break; case game_result_disconnect: account_inc_normal_disconnects(game->players[i], game->clienttag); account_set_normal_last_result(game->players[i], game->clienttag, game_result_get_str(game_result_disconnect)); break; default: eventlog(eventlog_level_error, __FUNCTION__, "bad normal game realplayer results[{}] = {}", i, game->results[i]); account_inc_normal_disconnects(game->players[i], game->clienttag); account_set_normal_last_result(game->players[i], game->clienttag, game_result_get_str(game_result_disconnect)); } account_set_normal_last_time(game->players[i], game->clienttag, bnettime()); } } } } if (game_get_type(game) != game_type_ladder && prefs_get_report_all_games() != 1) { eventlog(eventlog_level_debug, __FUNCTION__, "not reporting normal games"); if (ladder_info) { xfree(ladder_info); } return 0; } { struct std::tm * tmval; char dstr[64]; if (!(tmval = std::localtime(&now))) dstr[0] = '\0'; else std::sprintf(dstr, "%04d%02d%02d%02d%02d%02d", 1900 + tmval->tm_year, tmval->tm_mon + 1, tmval->tm_mday, tmval->tm_hour, tmval->tm_min, tmval->tm_sec); tempname = (char*)xmalloc(std::strlen(prefs_get_reportdir()) + 1 + 1 + 5 + 1 + 2 + 1 + std::strlen(dstr) + 1 + 6 + 1); std::sprintf(tempname, "%s/_bnetd-gr_%s_%06u", prefs_get_reportdir(), dstr, game->id); realname = (char*)xmalloc(std::strlen(prefs_get_reportdir()) + 1 + 2 + 1 + std::strlen(dstr) + 1 + 6 + 1); std::sprintf(realname, "%s/gr_%s_%06u", prefs_get_reportdir(), dstr, game->id); } if (!(fp = std::fopen(tempname, "w"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open report file \"{}\" for writing (std::fopen: {})", tempname, std::strerror(errno)); if (ladder_info) xfree(ladder_info); xfree(realname); xfree(tempname); return -1; } if (game->bad) std::fprintf(fp, "[ game results ignored due to inconsistencies ]\n\n"); std::fprintf(fp, "name=\"%s\" id=" GAMEID_FORMATF "\n", game_get_name(game), game->id); std::fprintf(fp, "clienttag=%4s type=\"%s\" option=\"%s\"\n", tag_uint_to_str(clienttag_str, game->clienttag), game_type_get_str(game->type), game_option_get_str(game->option)); { struct std::tm * gametime; char timetemp[GAME_TIME_MAXLEN]; if (!(gametime = std::localtime(&game->create_time))) std::strcpy(timetemp, "?"); else std::strftime(timetemp, sizeof(timetemp), GAME_TIME_FORMAT, gametime); std::fprintf(fp, "created=\"%s\" ", timetemp); if (!(gametime = std::localtime(&game->start_time))) std::strcpy(timetemp, "?"); else std::strftime(timetemp, sizeof(timetemp), GAME_TIME_FORMAT, gametime); std::fprintf(fp, "started=\"%s\" ", timetemp); if (!(gametime = std::localtime(&now))) std::strcpy(timetemp, "?"); else std::strftime(timetemp, sizeof(timetemp), GAME_TIME_FORMAT, gametime); std::fprintf(fp, "ended=\"%s\"\n", timetemp); } { char const * mapname; if (!(mapname = game_get_mapname(game))) mapname = "?"; std::fprintf(fp, "mapfile=\"%s\" mapauth=\"%s\" mapsize=%ux%u tileset=\"%s\"\n", mapname, game_maptype_get_str(game_get_maptype(game)), game_get_mapsize_x(game), game_get_mapsize_y(game), game_tileset_get_str(game_get_tileset(game))); } std::fprintf(fp, "joins=%u maxplayers=%u\n", game_get_count(game), game_get_maxplayers(game)); if (!prefs_get_hide_addr()) std::fprintf(fp, "host=%s\n", addr_num_to_addr_str(game_get_addr(game), game_get_port(game))); std::fprintf(fp, "\n\n"); if (game->clienttag == CLIENTTAG_DIABLORTL_UINT) for (i = 0; i < game->count; i++) std::fprintf(fp, "%-16s JOINED\n", account_get_name(game->players[i])); else if (ladder_info) for (i = 0; i < realcount; i++) std::fprintf(fp, "%-16s %-8s rating=%u [#%05u] prob=%4.1f%% K=%2u adj=%+d\n", account_get_name(game->players[i]), game_result_get_str(game->results[i]), ladder_info[i].oldrating, ladder_info[i].oldrank, ladder_info[i].prob*100.0, ladder_info[i].k, ladder_info[i].adj); else for (i = 0; i < realcount; i++) std::fprintf(fp, "%-16s %-8s\n", account_get_name(game->players[i]), game_result_get_str(game->results[i])); std::fprintf(fp, "\n\n"); if (ladder_info) xfree(ladder_info); for (i = 0; i < realcount; i++) { if (game->report_heads[i]) std::fprintf(fp, "%s\n", game->report_heads[i]); else std::fprintf(fp, "[ game report header not available for player %u (\"%s\") ]\n", i + 1, account_get_name(game->players[i])); if (game->report_bodies[i]) std::fprintf(fp, "%s\n", game->report_bodies[i]); else std::fprintf(fp, "[ game report body not available for player %u (\"%s\") ]\n\n", i + 1, account_get_name(game->players[i])); } std::fprintf(fp, "\n\n"); if (game->clienttag == CLIENTTAG_STARCRAFT_UINT || game->clienttag == CLIENTTAG_SHAREWARE_UINT || game->clienttag == CLIENTTAG_BROODWARS_UINT || game->clienttag == CLIENTTAG_WARCIIBNE_UINT) { for (i = 0; i < realcount; i++) std::fprintf(fp, "%s's normal record is now %u/%u/%u (%u draws)\n", account_get_name(game->players[i]), account_get_normal_wins(game->players[i], game->clienttag), account_get_normal_losses(game->players[i], game->clienttag), account_get_normal_disconnects(game->players[i], game->clienttag), account_get_normal_draws(game->players[i], game->clienttag)); } if (game->clienttag == CLIENTTAG_STARCRAFT_UINT || game->clienttag == CLIENTTAG_BROODWARS_UINT || game->clienttag == CLIENTTAG_WARCIIBNE_UINT) { std::fprintf(fp, "\n"); for (i = 0; i < realcount; i++) std::fprintf(fp, "%s's standard ladder record is now %u/%u/%u (rating %u [#%05d]) (%u draws)\n", account_get_name(game->players[i]), account_get_ladder_wins(game->players[i], game->clienttag, ladder_id_normal), account_get_ladder_losses(game->players[i], game->clienttag, ladder_id_normal), account_get_ladder_disconnects(game->players[i], game->clienttag, ladder_id_normal), account_get_ladder_rating(game->players[i], game->clienttag, ladder_id_normal), account_get_ladder_rank(game->players[i], game->clienttag, ladder_id_normal), account_get_ladder_draws(game->players[i], game->clienttag, ladder_id_normal)); } if (game->clienttag == CLIENTTAG_WARCIIBNE_UINT) { std::fprintf(fp, "\n"); for (i = 0; i < realcount; i++) std::fprintf(fp, "%s's ironman ladder record is now %u/%u/%u (rating %u [#%05d]) (%u draws)\n", account_get_name(game->players[i]), account_get_ladder_wins(game->players[i], game->clienttag, ladder_id_ironman), account_get_ladder_losses(game->players[i], game->clienttag, ladder_id_ironman), account_get_ladder_disconnects(game->players[i], game->clienttag, ladder_id_ironman), account_get_ladder_rating(game->players[i], game->clienttag, ladder_id_ironman), account_get_ladder_rank(game->players[i], game->clienttag, ladder_id_ironman), account_get_ladder_draws(game->players[i], game->clienttag, ladder_id_ironman)); } std::fprintf(fp, "\nThis game lasted %lu minutes (elapsed).\n", ((unsigned long int)std::difftime(now, game->start_time)) / 60); #ifdef WITH_LUA lua_handle_game(game, NULL, luaevent_game_report); #endif if (std::fclose(fp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close report file \"{}\" after writing (std::fclose: {})", tempname, std::strerror(errno)); xfree(realname); xfree(tempname); return -1; } if (p_rename(tempname, realname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not std::rename report file to \"{}\" (std::rename: {})", realname, std::strerror(errno)); xfree(realname); xfree(tempname); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "game report saved as \"{}\"", realname); xfree(realname); xfree(tempname); return 0; } extern unsigned int game_get_id(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->id; } extern char const * game_get_name(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->name ? game->name : "BNet"; } extern t_game_type game_get_type(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return game_type_none; } return game->type; } extern t_game_maptype game_get_maptype(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return game_maptype_none; } return game->maptype; } extern int game_set_maptype(t_game * game, t_game_maptype maptype) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->maptype = maptype; return 0; } extern t_game_tileset game_get_tileset(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return game_tileset_none; } return game->tileset; } extern int game_set_tileset(t_game * game, t_game_tileset tileset) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->tileset = tileset; return 0; } extern t_game_speed game_get_speed(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return game_speed_none; } return game->speed; } extern int game_set_speed(t_game * game, t_game_speed speed) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->speed = speed; return 0; } extern unsigned int game_get_mapsize_x(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->mapsize_x; } extern int game_set_mapsize_x(t_game * game, unsigned int x) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->mapsize_x = x; return 0; } extern unsigned int game_get_mapsize_y(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->mapsize_y; } extern int game_set_mapsize_y(t_game * game, unsigned int y) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->mapsize_y = y; return 0; } extern unsigned int game_get_maxplayers(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->maxplayers; } extern int game_set_maxplayers(t_game * game, unsigned int maxplayers) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->maxplayers = maxplayers; return 0; } extern unsigned int game_get_difficulty(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->difficulty; } extern int game_set_difficulty(t_game * game, unsigned int difficulty) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->difficulty = difficulty; return 0; } extern char const * game_get_description(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->description; } extern int game_set_description(t_game * game, char const * description) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!description) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL description"); return -1; } if (game->description != NULL) xfree((void *)game->description); game->description = xstrdup(description); return 0; } extern char const * game_get_pass(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->pass; } extern char const * game_get_info(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->info; } extern int game_get_startver(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->startver; } extern unsigned long game_get_version(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->version; } extern unsigned int game_get_ref(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->ref; } extern unsigned int game_get_count(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->count; } extern void game_set_status(t_game * game, t_game_status status) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return; } // [quetzal] 20020829 - this should prevent invalid status changes // its like started game cant become open and so on if (game->status == game_status_started && (status == game_status_open || status == game_status_full || status == game_status_loaded)) { eventlog(eventlog_level_error, "game_set_status", "attempting to set status '{}' ({}) to started game", game_status_get_str(status), status); return; } if (game->status == game_status_done && status != game_status_done) { eventlog(eventlog_level_error, "game_set_status", "attempting to set status '{}' ({}) to done game", game_status_get_str(status), status); return; } if (status == game_status_started && game->start_time == (std::time_t)0) game->start_time = now; game->status = status; #ifdef WITH_LUA lua_handle_game(game, NULL, luaevent_game_changestatus); #endif } extern t_game_status game_get_status(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return game_status_started; } return game->status; } extern unsigned int game_get_addr(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->addr; /* host byte order */ } extern unsigned short game_get_port(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->port; /* host byte order */ } extern unsigned int game_get_latency(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } if (game->ref < 1) { eventlog(eventlog_level_error, __FUNCTION__, "game \"{}\" has no players", game_get_name(game)); return 0; } if (!game->players) { eventlog(eventlog_level_error, __FUNCTION__, "game \"{}\" has NULL players array (ref={})", game_get_name(game), game->ref); return 0; } if (!game->players[0]) { eventlog(eventlog_level_error, __FUNCTION__, "game \"{}\" has NULL players[0] entry (ref={})", game_get_name(game), game->ref); return 0; } return 0; /* conn_get_latency(game->players[0]); */ } extern t_connection * game_get_player_conn(t_game const * game, unsigned int i) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } if (game->ref < 1) { eventlog(eventlog_level_error, __FUNCTION__, "game \"{}\" has no players", game_get_name(game)); return NULL; } if (!game->players) { eventlog(eventlog_level_error, __FUNCTION__, "game \"{}\" has NULL player array (ref={})", game_get_name(game), game->ref); return NULL; } if (!game->players[i]) { eventlog(eventlog_level_error, __FUNCTION__, "game \"{}\" has NULL players[i] entry (ref={})", game_get_name(game), game->ref); return NULL; } return game->connections[i]; } extern t_clienttag game_get_clienttag(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->clienttag; } extern int game_add_player(t_game * game, char const * pass, int startver, t_connection * c) { t_connection * * tempc; t_account * * tempp; t_game_result * tempr; t_game_result ** temprr; char const * * temprh; char const * * temprb; unsigned int i = 0; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!pass) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL password"); return -1; } if (startver != STARTVER_UNKNOWN && startver != STARTVER_GW1 && startver != STARTVER_GW3 && startver != STARTVER_GW4 && startver != STARTVER_REALM1) { eventlog(eventlog_level_error, __FUNCTION__, "got bad game startver {}", startver); return -1; } if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (game->type == game_type_ladder && (account_get_normal_wins(conn_get_account(c), conn_get_clienttag(c)) < 10 && conn_get_wol(c) == 0)) /* if () ... */ { eventlog(eventlog_level_error, __FUNCTION__, "can not join ladder game without 10 normal wins"); return -1; } { t_clienttag gt; if (!(gt = game_get_clienttag(game))) { eventlog(eventlog_level_error, __FUNCTION__, "could not get clienttag for game"); return -1; } } if (game->pass[0] != '\0' && strcasecmp(game->pass, pass) != 0) { eventlog(eventlog_level_debug, __FUNCTION__, "game \"{}\" password mismatch \"{}\"!=\"{}\"", game_get_name(game), game->pass, pass); return -1; } if (game->connections && (game->count > 0)) { for (i = 0; i < game->count; i++) { if (game->connections[i] == NULL) { game->connections[i] = c; game->players[i] = conn_get_account(c); game->results[i] = game_result_none; game->reported_results[i] = NULL; game->report_heads[i] = NULL; game->report_bodies[i] = NULL; game->ref++; game->lastaccess_time = now; break; } } } // first player will be added here if ((i == game->count) || (game->count == 0)) { if (!game->connections) /* some std::realloc()s are broken */ tempc = (t_connection**)xmalloc((game->count + 1)*sizeof(t_connection *)); else tempc = (t_connection**)xrealloc(game->connections, (game->count + 1)*sizeof(t_connection *)); game->connections = tempc; if (!game->players) /* some std::realloc()s are broken */ tempp = (t_account**)xmalloc((game->count + 1)*sizeof(t_account *)); else tempp = (t_account**)xrealloc(game->players, (game->count + 1)*sizeof(t_account *)); game->players = tempp; if (!game->results) /* some std::realloc()s are broken */ tempr = (t_game_result*)xmalloc((game->count + 1)*sizeof(t_game_result)); else tempr = (t_game_result*)xrealloc(game->results, (game->count + 1)*sizeof(t_game_result)); game->results = tempr; if (!game->reported_results) temprr = (t_game_result**)xmalloc((game->count + 1)*sizeof(t_game_result *)); else temprr = (t_game_result**)xrealloc(game->reported_results, (game->count + 1)*sizeof(t_game_result *)); game->reported_results = temprr; if (!game->report_heads) /* some xrealloc()s are broken */ temprh = (const char**)xmalloc((game->count + 1)*sizeof(char const *)); else temprh = (const char**)xrealloc((void *)game->report_heads, (game->count + 1)*sizeof(char const *)); /* avoid compiler warning */ game->report_heads = temprh; if (!game->report_bodies) /* some xrealloc()s are broken */ temprb = (const char**)xmalloc((game->count + 1)*sizeof(char const *)); else temprb = (const char**)xrealloc((void *)game->report_bodies, (game->count + 1)*sizeof(char const *)); /* avoid compiler warning */ game->report_bodies = temprb; game->connections[game->count] = c; game->players[game->count] = conn_get_account(c); game->results[game->count] = game_result_none; game->reported_results[game->count] = NULL; game->report_heads[game->count] = NULL; game->report_bodies[game->count] = NULL; game->count++; game->ref++; game->lastaccess_time = now; } // end of "if ((i == game->count) || (game->count == 0))" if (game->startver != startver && startver != STARTVER_UNKNOWN) /* with join startver ALWAYS unknown [KWS] */ eventlog(eventlog_level_error, __FUNCTION__, "player \"{}\" client \"{}\" startver {} joining game startver {} (count={} ref={})", account_get_name(conn_get_account(c)), clienttag_uint_to_str(conn_get_clienttag(c)), startver, game->startver, game->count, game->ref); game_choose_host(game); return 0; } extern int game_del_player(t_game * game, t_connection * c) { char const * tname; unsigned int i; t_account * account; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!game->players) { eventlog(eventlog_level_error, __FUNCTION__, "player array is NULL"); return -1; } if (!game->reported_results) { eventlog(eventlog_level_error, __FUNCTION__, "reported results array is NULL"); return -1; } account = conn_get_account(c); if (conn_get_leavegamewhisper_ack(c) == 0) { watchlist->dispatch(conn_get_account(c), NULL, conn_get_clienttag(c), Watch::ET_leavegame); conn_set_leavegamewhisper_ack(c, 1); //1 = already whispered. We reset this each std::time user joins a channel } eventlog(eventlog_level_debug, __FUNCTION__, "game \"{}\" has ref={}, count={}; trying to remove player \"{}\"", game_get_name(game), game->ref, game->count, account_get_name(account)); for (i = 0; i < game->count; i++) if (game->players[i] == account && game->connections[i]) { eventlog(eventlog_level_debug, __FUNCTION__, "removing player #{} \"{}\" from \"{}\", {} players left", i, (tname = account_get_name(account)), game_get_name(game), game->ref - 1); game->connections[i] = NULL; if (!(game->reported_results[i])) eventlog(eventlog_level_debug, __FUNCTION__, "player \"{}\" left without reporting (valid) results", tname); eventlog(eventlog_level_debug, __FUNCTION__, "player deleted... (ref={})", game->ref); if (game->ref < 2) { eventlog(eventlog_level_debug, __FUNCTION__, "no more players, reporting game"); game_report(game); eventlog(eventlog_level_debug, __FUNCTION__, "no more players, destroying game"); game_destroy(game); return 0; } game->ref--; game->lastaccess_time = now; game_choose_host(game); #ifdef WITH_LUA lua_handle_game(game, c, luaevent_game_userleft); #endif return 0; } eventlog(eventlog_level_error, __FUNCTION__, "player \"{}\" was not in the game", account_get_name(account)); return -1; } extern t_account * game_get_player(t_game * game, unsigned int i) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } if (!(i < game->count)) { eventlog(eventlog_level_error, __FUNCTION__, "requested illegal player id {}", i); return NULL; } return game->players[i]; } extern int game_set_report(t_game * game, t_account * account, char const * rephead, char const * repbody) { unsigned int pos; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!game->players) { eventlog(eventlog_level_error, __FUNCTION__, "player array is NULL"); return -1; } if (!game->report_heads) { eventlog(eventlog_level_error, __FUNCTION__, "report_heads array is NULL"); return -1; } if (!game->report_bodies) { eventlog(eventlog_level_error, __FUNCTION__, "report_bodies array is NULL"); return -1; } if (!rephead) { eventlog(eventlog_level_error, __FUNCTION__, "report head is NULL"); return -1; } if (!repbody) { eventlog(eventlog_level_error, __FUNCTION__, "report body is NULL"); return -1; } { unsigned int i; pos = game->count; for (i = 0; i < game->count; i++) if (game->players[i] == account) pos = i; } if (pos == game->count) { eventlog(eventlog_level_error, __FUNCTION__, "could not find player \"{}\" to set result", account_get_name(account)); return -1; } game->report_heads[pos] = xstrdup(rephead); game->report_bodies[pos] = xstrdup(repbody); return 0; } extern int game_set_reported_results(t_game * game, t_account * account, t_game_result * results) { unsigned int i, j; t_game_result result; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!results) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL results"); return -1; } if (!game->players) { eventlog(eventlog_level_error, __FUNCTION__, "player array is NULL"); return -1; } if (!game->reported_results) { eventlog(eventlog_level_error, __FUNCTION__, "reported_results array is NULL"); return -1; } for (i = 0; i < game->count; i++) { if ((game->players[i] == account)) break; } if (i == game->count) { eventlog(eventlog_level_error, __FUNCTION__, "could not find player \"{}\" to set reported results", account_get_name(account)); return -1; } if (game->reported_results[i]) { eventlog(eventlog_level_error, __FUNCTION__, "player \"{}\" already reported results - skipping this report", account_get_name(account)); return -1; } for (j = 0; j < game->count; j++) { result = results[j]; switch (result) { case game_result_win: case game_result_loss: case game_result_draw: case game_result_observer: case game_result_disconnect: break; case game_result_none: case game_result_playing: if (i != j) break; /* accept none/playing only from "others" */ default: /* result is invalid */ if (i != j) { eventlog(eventlog_level_error, __FUNCTION__, "ignoring bad reported result {} for player \"{}\"", (unsigned int)result, account_get_name(game->players[j])); results[i] = game_result_none; } else { eventlog(eventlog_level_error, __FUNCTION__, "got bad reported result {} for self - skipping results", (unsigned int)result); return -1; } } } game->reported_results[i] = results; return 0; } extern int game_set_self_report(t_game * game, t_account * account, t_game_result result) { unsigned int i; t_game_result * results; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!game->players) { eventlog(eventlog_level_error, __FUNCTION__, "player array is NULL"); return -1; } if (!game->reported_results) { eventlog(eventlog_level_error, __FUNCTION__, "reported_results array is NULL"); return -1; } results = (t_game_result*)xmalloc(sizeof(t_game_result)*game->count); for (i = 0; i < game->count; i++) { if ((game->players[i] == account)) results[i] = result; else results[i] = game_result_none; } game_set_reported_results(game, account, results); return 0; } extern t_game_result * game_get_reported_results(t_game * game, t_account * account) { unsigned int i; if (!(game)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } if (!(account)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return NULL; } if (!(game->players)) { eventlog(eventlog_level_error, __FUNCTION__, "player array is NULL"); return NULL; } if (!(game->reported_results)) { eventlog(eventlog_level_error, __FUNCTION__, "reported_results array is NULL"); return NULL; } for (i = 0; i < game->count; i++) { if ((game->players[i] == account)) break; } if (i == game->count) { eventlog(eventlog_level_error, __FUNCTION__, "could not find player \"{}\" to set reported results", account_get_name(account)); return NULL; } if (!(game->reported_results[i])) { eventlog(eventlog_level_error, __FUNCTION__, "player \"{}\" has not reported any results", account_get_name(account)); return NULL; } return game->reported_results[i]; } extern char const * game_get_mapname(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->mapname; } extern int game_set_mapname(t_game * game, char const * mapname) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!mapname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL mapname"); return -1; } if (game->mapname != NULL) xfree((void *)game->mapname); game->mapname = xstrdup(mapname); return 0; } extern t_connection * game_get_owner(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->owner; } extern std::time_t game_get_create_time(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return (std::time_t)0; } return game->create_time; } extern std::time_t game_get_start_time(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return (std::time_t)0; } return game->start_time; } extern int game_set_option(t_game * game, t_game_option option) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->option = option; return 0; } extern t_game_option game_get_option(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return game_option_none; } return game->option; } extern int gamelist_create(void) { elist_init(&gamelist_head); glist_length = 0; return 0; } extern int gamelist_destroy(void) { /* FIXME: if called with active games, games are not freed */ elist_init(&gamelist_head); glist_length = 0; return 0; } extern t_elist * gamelist(void) { return &gamelist_head; } extern int gamelist_get_length(void) { return glist_length; } extern t_game * gamelist_find_game(char const * name, t_clienttag ctag, t_game_type type) { t_elist *curr; t_game *game; elist_for_each(curr, &gamelist_head) { game = elist_entry(curr, t_game, glist_link); if ((type == game_type_all || game->type == type) && ctag == game->clienttag && game->name && !strcasecmp(name, game->name)) return game; } return NULL; } extern t_game * gamelist_find_game_available(char const * name, t_clienttag ctag, t_game_type type) { t_elist *curr; t_game *game; t_game_status status; elist_for_each(curr, &gamelist_head) { game = elist_entry(curr, t_game, glist_link); status = game->status; if ((type == game_type_all || game->type == type) && (ctag == game->clienttag) && (game->name) && (!strcasecmp(name, game->name)) && (game->status != game_status_started) && (game->status != game_status_done)) return game; } return NULL; } extern t_game * gamelist_find_game_byid(unsigned int id) { t_elist *curr; t_game *game; elist_for_each(curr, &gamelist_head) { game = elist_entry(curr, t_game, glist_link); if (game->id == id) return game; } return NULL; } extern void gamelist_traverse(t_glist_func cb, void *data, t_gamelist_source_type gamelist_source) { t_elist *curr; #ifdef WITH_LUA t_game *game; if (gamelist_source == gamelist_source_joinbutton) { struct glist_cbdata *cbdata = (struct glist_cbdata*)data; // get gamelist from Lua script: pair(gameid=gamename) std::vector gamelist = lua_handle_game_list(cbdata->c); if (gamelist.size() > 0) { // display games to a user according to the given order for (std::vector::size_type i = 0; i != gamelist.size(); i++) { if (game = gamelist_find_game_byid(gamelist[i]->id)) { if (gamelist[i]->name) game->name = xstrdup(gamelist[i]->name); // override game name cb(game, data); // display game item } } // break next execution (override of display below gamelist) return; } } #endif elist_for_each(curr, &gamelist_head) { if (cb(elist_entry(curr, t_game, glist_link), data) < 0) return; } } extern int gamelist_total_games(void) { return totalcount; } extern int game_set_realm(t_game * game, unsigned int realm) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } game->realm = realm; return 0; } extern unsigned int game_get_realm(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return 0; } return game->realm; } extern int game_set_realmname(t_game * game, char const * realmname) { char const * temp; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (realmname) temp = xstrdup(realmname); else temp = NULL; if (game->realmname) xfree((void *)game->realmname); /* avoid warning */ game->realmname = temp; return 0; } extern char const * game_get_realmname(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->realmname; } extern void gamelist_check_voidgame(void) { t_elist *curr, *save; t_game *game; elist_for_each_safe(curr, &gamelist_head, save) { game = elist_entry(curr, t_game, glist_link); if (!game->realm) continue; if (game->ref >= 1) continue; if ((now - game->lastaccess_time) > MAX_GAME_EMPTY_TIME) game_destroy(game); } } extern void game_set_flag(t_game * game, t_game_flag flag) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return; } game->flag = flag; } extern t_game_flag game_get_flag(t_game const * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return game_flag_none; } return game->flag; } extern int game_get_count_by_clienttag(t_clienttag ct) { t_game *game; t_elist *curr; int clienttaggames = 0; if (!ct) { eventlog(eventlog_level_error, __FUNCTION__, "got UNKNOWN clienttag"); return 0; } /* Get number of games for client tag specific */ elist_for_each(curr, &gamelist_head) { game = elist_entry(curr, t_game, glist_link); if (game_get_clienttag(game) == ct) clienttaggames++; } return clienttaggames; } static int game_match_name(const char *name, const char *prefix) { /* the easy cases */ if (!name || !*name) return 1; if (!prefix || !*prefix) return 1; if (!std::strncmp(name, prefix, std::strlen(prefix))) return 1; return 0; } extern int game_is_ladder(t_game *game) { assert(game); /* all normal ladder games are still counted as ladder games */ if (game->type == game_type_ladder || game->type == game_type_ironman) return 1; /* addition game types are also checked against gamename prefix if set */ if (game_match_type(game_get_type(game), prefs_get_ladder_games()) && game_match_name(game_get_name(game), prefs_get_ladder_prefix())) return 1; return 0; } extern int game_discisloss(t_game *game) { assert(game); if (prefs_get_discisloss()) return 1; /* all normal ladder games provide discisloss option themselves */ if (game->type == game_type_ladder || game->type == game_type_ironman) return game->option == game_option_ladder_countasloss; /* additional game types that are consideres ladder are always considered discasloss */ if (game_match_type(game_get_type(game), prefs_get_ladder_games()) && game_match_name(game_get_name(game), prefs_get_ladder_prefix())) return 1; /* all other games are handled as usual */ return game->option == game_option_ladder_countasloss; } extern int game_set_channel(t_game * game, t_channel * channel) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } game->channel = channel; return 0; } extern t_channel * game_get_channel(t_game * game) { if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return NULL; } return game->channel; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/game.h000066400000000000000000000263421462640461400171540ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_GAME_TYPES #define INCLUDED_GAME_TYPES #ifdef GAME_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES # include "account.h" # include "connection.h" # include "channel.h" # include "common/tag.h" # include "common/elist.h" #else # define JUST_NEED_TYPES # include "account.h" # include "connection.h" # include "channel.h" # include "common/tag.h" # include "common/elist.h" # undef JUST_NEED_TYPES #endif #endif namespace pvpgn { namespace bnetd { typedef enum { game_type_none, game_type_all, game_type_topvbot, game_type_melee, game_type_ffa, game_type_oneonone, game_type_ctf, game_type_greed, game_type_slaughter, game_type_sdeath, game_type_ladder, game_type_ironman, game_type_mapset, game_type_teammelee, game_type_teamffa, game_type_teamctf, game_type_pgl, game_type_diablo, game_type_diablo2open, game_type_diablo2closed, game_type_anongame } t_game_type; typedef enum { game_status_started, game_status_full, game_status_open, game_status_loaded, game_status_done } t_game_status; typedef enum { game_result_none, game_result_win, game_result_loss, game_result_draw, game_result_disconnect, game_result_observer, game_result_playing } t_game_result; typedef enum { game_option_none, game_option_melee_normal, game_option_ffa_normal, game_option_oneonone_normal, game_option_ctf_normal, game_option_greed_10000, game_option_greed_7500, game_option_greed_5000, game_option_greed_2500, game_option_slaughter_60, game_option_slaughter_45, game_option_slaughter_30, game_option_slaughter_15, game_option_sdeath_normal, game_option_ladder_countasloss, game_option_ladder_nopenalty, game_option_mapset_normal, game_option_teammelee_4, game_option_teammelee_3, game_option_teammelee_2, game_option_teamffa_4, game_option_teamffa_3, game_option_teamffa_2, game_option_teamctf_4, game_option_teamctf_3, game_option_teamctf_2, game_option_topvbot_7, game_option_topvbot_6, game_option_topvbot_5, game_option_topvbot_4, game_option_topvbot_3, game_option_topvbot_2, game_option_topvbot_1 } t_game_option; typedef enum { game_maptype_none, game_maptype_selfmade, game_maptype_blizzard, game_maptype_ladder, game_maptype_pgl, game_maptype_kbk, game_maptype_compusa } t_game_maptype; typedef enum { game_tileset_none, game_tileset_badlands, game_tileset_space, game_tileset_installation, game_tileset_ashworld, game_tileset_jungle, game_tileset_desert, game_tileset_ice, game_tileset_twilight } t_game_tileset; typedef enum { game_speed_none, game_speed_slowest, game_speed_slower, game_speed_slow, game_speed_normal, game_speed_fast, game_speed_faster, game_speed_fastest } t_game_speed; typedef enum { game_difficulty_none, game_difficulty_normal, game_difficulty_nightmare, game_difficulty_hell, game_difficulty_hardcore_normal, game_difficulty_hardcore_nightmare, game_difficulty_hardcore_hell } t_game_difficulty; typedef enum { game_flag_none, game_flag_private } t_game_flag; typedef enum { gamelist_source_none, gamelist_source_joinbutton /* show gamelist when user clicks "Join" button */ } t_gamelist_source_type; typedef struct game #ifdef GAME_INTERNAL_ACCESS { char const * name; char const * pass; char const * info; t_game_type type; unsigned int realm; char const * realmname; t_clienttag clienttag; /* type of client (STAR, SEXP, etc) */ unsigned int addr; /* host IP */ unsigned short port; /* host port */ int startver; unsigned long version; t_game_status status; unsigned int ref; /* current number of players */ unsigned int count; /* max number of players */ unsigned int id; char const * mapname; t_game_option option; t_game_maptype maptype; t_game_tileset tileset; t_game_speed speed; unsigned int mapsize_x; unsigned int mapsize_y; unsigned int maxplayers; t_connection * owner; t_connection * * connections; t_account * * players; t_game_result * results; t_game_result * * reported_results; char const * * report_heads; char const * * report_bodies; t_channel * channel; /* For Games with server-side chat support */ std::time_t create_time; std::time_t start_time; std::time_t lastaccess_time; int bad; /* if 1, then the results will be ignored */ unsigned difficulty; char const * description; t_game_flag flag; t_elist glist_link; } #endif t_game; typedef int(*t_glist_func)(t_game *, void *); } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_GAME_PROTOS #define INCLUDED_GAME_PROTOS #define MAX_GAME_EMPTY_TIME 300 #define STARTVER_UNKNOWN 0 #define STARTVER_GW1 1 #define STARTVER_GW3 3 #define STARTVER_GW4 4 #define STARTVER_REALM1 104 #define JUST_NEED_TYPES #include "account.h" #include "connection.h" #include "channel.h" #include "common/list.h" #include "common/tag.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { // game list structure with custom data struct glist_cbdata { unsigned tcount, counter; t_connection *c; t_game_type gtype; t_packet *rpacket; }; extern char const * game_type_get_str(t_game_type type); extern char const * game_status_get_str(t_game_status status); extern char const * game_result_get_str(t_game_result result); extern char const * game_option_get_str(t_game_option option); extern char const * game_maptype_get_str(t_game_maptype maptype); extern char const * game_tileset_get_str(t_game_tileset tileset); extern char const * game_speed_get_str(t_game_speed speed); extern char const * game_difficulty_get_str(unsigned difficulty); extern t_game * game_create(char const * name, char const * pass, char const * info, t_game_type type, int startver, t_clienttag clienttag, unsigned long gameversion); extern unsigned int game_get_id(t_game const * game); extern char const * game_get_name(t_game const * game); extern t_game_type game_get_type(t_game const * game); extern t_game_maptype game_get_maptype(t_game const * game); extern int game_set_maptype(t_game * game, t_game_maptype maptype); extern t_game_tileset game_get_tileset(t_game const * game); extern int game_set_tileset(t_game * game, t_game_tileset tileset); extern t_game_speed game_get_speed(t_game const * game); extern int game_set_speed(t_game * game, t_game_speed speed); extern unsigned int game_get_mapsize_x(t_game const * game); extern int game_set_mapsize_x(t_game * game, unsigned int x); extern unsigned int game_get_mapsize_y(t_game const * game); extern int game_set_mapsize_y(t_game * game, unsigned int y); extern unsigned int game_get_maxplayers(t_game const * game); extern int game_set_maxplayers(t_game * game, unsigned int maxplayers); extern unsigned int game_get_difficulty(t_game const * game); extern int game_set_difficulty(t_game * game, unsigned int difficulty); extern char const * game_get_description(t_game const * game); extern int game_set_description(t_game * game, char const * description); extern char const * game_get_pass(t_game const * game); extern char const * game_get_info(t_game const * game); extern unsigned long game_get_version(t_game const * game); extern int game_get_startver(t_game const * game); extern unsigned int game_get_ref(t_game const * game); extern unsigned int game_get_count(t_game const * game); extern void game_set_status(t_game * game, t_game_status status); extern t_game_status game_get_status(t_game const * game); extern unsigned int game_get_addr(t_game const * game); extern unsigned short game_get_port(t_game const * game); extern unsigned int game_get_latency(t_game const * game); extern t_connection * game_get_player_conn(t_game const * game, unsigned int i); extern t_clienttag game_get_clienttag(t_game const * game); extern int game_add_player(t_game * game, char const * pass, int startver, t_connection * c); extern int game_del_player(t_game * game, t_connection * c); extern t_account * game_get_player(t_game * game, unsigned int i); extern int game_set_report(t_game * game, t_account * account, char const * head, char const * body); extern int game_set_reported_results(t_game * game, t_account * account, t_game_result * results); extern int game_set_self_report(t_game * game, t_account * account, t_game_result result); extern t_game_result * game_get_reported_results(t_game * game, t_account * account); extern char const * game_get_mapname(t_game const * game); extern int game_set_mapname(t_game * game, char const * mapname); extern t_connection * game_get_owner(t_game const * game); extern std::time_t game_get_create_time(t_game const * game); extern std::time_t game_get_start_time(t_game const * game); extern int game_set_option(t_game * game, t_game_option option); extern t_game_option game_get_option(t_game const * game); extern int gamelist_create(void); extern int gamelist_destroy(void); extern t_elist * gamelist(void); extern int gamelist_get_length(void); extern t_game * gamelist_find_game(char const * name, t_clienttag ctag, t_game_type type); extern t_game * gamelist_find_game_available(char const * name, t_clienttag ctag, t_game_type type); extern t_game * gamelist_find_game_byid(unsigned int id); extern void gamelist_traverse(t_glist_func cb, void *data, t_gamelist_source_type); extern int gamelist_total_games(void); extern int game_set_realm(t_game * game, unsigned int realm); extern unsigned int game_get_realm(t_game const * game); extern char const * game_get_realmname(t_game const * game); extern int game_set_realmname(t_game * game, char const * realmname); extern void gamelist_check_voidgame(void); extern void game_set_flag(t_game * game, t_game_flag flag); extern t_game_flag game_get_flag(t_game const * game); extern int game_get_count_by_clienttag(t_clienttag ct); extern int game_is_ladder(t_game *game); extern int game_discisloss(t_game *game); extern int game_set_channel(t_game * game, t_channel * channel); extern t_channel * game_get_channel(t_game * game); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/game_conv.cpp000066400000000000000000001226071462640461400205350ustar00rootroot00000000000000/* * Copyright (C) 1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999,2000 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define GAME_INTERNAL_ACCESS #include "common/setup_before.h" #include "game_conv.h" #include #include "common/eventlog.h" #include "common/tag.h" #include "common/bnet_protocol.h" #include "common/bn_type.h" #include "common/util.h" #include "compat/strsep.h" #include "game.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern t_game_type bngreqtype_to_gtype(t_clienttag clienttag, unsigned short bngtype) { char clienttag_str[5]; if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clienttag"); return game_type_none; } if (clienttag == CLIENTTAG_WARCIIBNE_UINT) { switch (bngtype) { case CLIENT_GAMELISTREQ_ALL: return game_type_all; case CLIENT_GAMELISTREQ_TOPVBOT: return game_type_topvbot; case CLIENT_GAMELISTREQ_MELEE: return game_type_melee; case CLIENT_GAMELISTREQ_FFA: return game_type_ffa; case CLIENT_GAMELISTREQ_ONEONONE: return game_type_oneonone; case CLIENT_GAMELISTREQ_LADDER: return game_type_ladder; case CLIENT_GAMELISTREQ_IRONMAN: return game_type_ironman; case CLIENT_GAMELISTREQ_MAPSET: return game_type_mapset; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT) { switch (bngtype) { case CLIENT_GAMELISTREQ_ALL: return game_type_diablo2open; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo II bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_DIABLORTL_UINT || clienttag == CLIENTTAG_DIABLOSHR_UINT) { switch (bngtype) { case CLIENT_GAMETYPE_DIABLO_0: case CLIENT_GAMETYPE_DIABLO_1: case CLIENT_GAMETYPE_DIABLO_2: case CLIENT_GAMETYPE_DIABLO_3: case CLIENT_GAMETYPE_DIABLO_4: case CLIENT_GAMETYPE_DIABLO_5: case CLIENT_GAMETYPE_DIABLO_6: case CLIENT_GAMETYPE_DIABLO_7: case CLIENT_GAMETYPE_DIABLO_8: case CLIENT_GAMETYPE_DIABLO_9: case CLIENT_GAMETYPE_DIABLO_a: case CLIENT_GAMETYPE_DIABLO_b: case CLIENT_GAMETYPE_DIABLO_c: case CLIENT_GAMETYPE_DIABLO_d: return game_type_diablo; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_STARCRAFT_UINT || clienttag == CLIENTTAG_BROODWARS_UINT || clienttag == CLIENTTAG_SHAREWARE_UINT) { switch (bngtype) { case CLIENT_GAMELISTREQ_ALL: return game_type_all; case CLIENT_GAMELISTREQ_MELEE: return game_type_melee; case CLIENT_GAMELISTREQ_FFA: return game_type_ffa; case CLIENT_GAMELISTREQ_ONEONONE: return game_type_oneonone; case CLIENT_GAMELISTREQ_CTF: return game_type_ctf; case CLIENT_GAMELISTREQ_GREED: return game_type_greed; case CLIENT_GAMELISTREQ_SLAUGHTER: return game_type_slaughter; case CLIENT_GAMELISTREQ_SDEATH: return game_type_sdeath; case CLIENT_GAMELISTREQ_LADDER: return game_type_ladder; case CLIENT_GAMELISTREQ_MAPSET: return game_type_mapset; case CLIENT_GAMELISTREQ_TEAMMELEE: return game_type_teammelee; case CLIENT_GAMELISTREQ_TEAMFFA: return game_type_teamffa; case CLIENT_GAMELISTREQ_TEAMCTF: return game_type_teamctf; case CLIENT_GAMELISTREQ_PGL: return game_type_pgl; case CLIENT_GAMELISTREQ_TOPVBOT: return game_type_topvbot; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_WARCRAFT3_UINT || clienttag == CLIENTTAG_WAR3XP_UINT) { return game_type_all; } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown game clienttag \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } extern t_game_type bngtype_to_gtype(t_clienttag clienttag, unsigned short bngtype) { char clienttag_str[5]; if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clienttag"); return game_type_none; } if (clienttag == CLIENTTAG_WARCIIBNE_UINT) { switch (bngtype) { case CLIENT_GAMELISTREQ_TOPVBOT: return game_type_topvbot; case CLIENT_GAMELISTREQ_MELEE: return game_type_melee; case CLIENT_GAMELISTREQ_FFA: return game_type_ffa; case CLIENT_GAMELISTREQ_ONEONONE: return game_type_oneonone; case CLIENT_GAMELISTREQ_LADDER: return game_type_ladder; case CLIENT_GAMELISTREQ_IRONMAN: return game_type_ironman; case CLIENT_GAMELISTREQ_MAPSET: return game_type_mapset; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT) { switch (bngtype) { case CLIENT_GAMETYPE_DIABLO2_OPEN_NORMAL: case CLIENT_GAMETYPE_DIABLO2_OPEN_NIGHTMARE: case CLIENT_GAMETYPE_DIABLO2_OPEN_HELL: case CLIENT_GAMETYPE_DIABLO2_OPEN_HARDCORE_NORMAL: case CLIENT_GAMETYPE_DIABLO2_OPEN_HARDCORE_NIGHTMARE: case CLIENT_GAMETYPE_DIABLO2_OPEN_HARDCORE_HELL: return game_type_diablo2open; case CLIENT_GAMETYPE_DIABLO2_CLOSE: return game_type_diablo2closed; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo II bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_DIABLORTL_UINT || clienttag == CLIENTTAG_DIABLOSHR_UINT) { switch (bngtype) { case CLIENT_GAMETYPE_DIABLO_0: case CLIENT_GAMETYPE_DIABLO_1: case CLIENT_GAMETYPE_DIABLO_2: case CLIENT_GAMETYPE_DIABLO_3: case CLIENT_GAMETYPE_DIABLO_4: case CLIENT_GAMETYPE_DIABLO_5: case CLIENT_GAMETYPE_DIABLO_6: case CLIENT_GAMETYPE_DIABLO_7: case CLIENT_GAMETYPE_DIABLO_8: case CLIENT_GAMETYPE_DIABLO_9: case CLIENT_GAMETYPE_DIABLO_a: case CLIENT_GAMETYPE_DIABLO_b: case CLIENT_GAMETYPE_DIABLO_c: return game_type_diablo; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_STARCRAFT_UINT || clienttag == CLIENTTAG_BROODWARS_UINT || clienttag == CLIENTTAG_SHAREWARE_UINT) { switch (bngtype) { case CLIENT_GAMELISTREQ_ALL: return game_type_all; case CLIENT_GAMELISTREQ_MELEE: return game_type_melee; case CLIENT_GAMELISTREQ_FFA: return game_type_ffa; case CLIENT_GAMELISTREQ_ONEONONE: return game_type_oneonone; case CLIENT_GAMELISTREQ_CTF: return game_type_ctf; case CLIENT_GAMELISTREQ_GREED: return game_type_greed; case CLIENT_GAMELISTREQ_SLAUGHTER: return game_type_slaughter; case CLIENT_GAMELISTREQ_SDEATH: return game_type_sdeath; case CLIENT_GAMELISTREQ_LADDER: return game_type_ladder; case CLIENT_GAMELISTREQ_MAPSET: return game_type_mapset; case CLIENT_GAMELISTREQ_TEAMMELEE: return game_type_teammelee; case CLIENT_GAMELISTREQ_TEAMFFA: return game_type_teamffa; case CLIENT_GAMELISTREQ_TEAMCTF: return game_type_teamctf; case CLIENT_GAMELISTREQ_PGL: return game_type_pgl; case CLIENT_GAMELISTREQ_TOPVBOT: return game_type_topvbot; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } else if (clienttag == CLIENTTAG_WARCRAFT3_UINT || clienttag == CLIENTTAG_WAR3XP_UINT) { return game_type_all; } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown game clienttag \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), bngtype); return game_type_none; } } extern unsigned short gtype_to_bngtype(t_game_type gtype) { switch (gtype) { case game_type_all: return CLIENT_GAMELISTREQ_ALL; case game_type_melee: return CLIENT_GAMELISTREQ_MELEE; case game_type_ffa: return CLIENT_GAMELISTREQ_FFA; case game_type_oneonone: return CLIENT_GAMELISTREQ_ONEONONE; case game_type_ctf: return CLIENT_GAMELISTREQ_CTF; case game_type_greed: return CLIENT_GAMELISTREQ_GREED; case game_type_slaughter: return CLIENT_GAMELISTREQ_SLAUGHTER; case game_type_sdeath: return CLIENT_GAMELISTREQ_SDEATH; case game_type_ladder: return CLIENT_GAMELISTREQ_LADDER; case game_type_mapset: return CLIENT_GAMELISTREQ_MAPSET; case game_type_teammelee: return CLIENT_GAMELISTREQ_TEAMMELEE; case game_type_teamffa: return CLIENT_GAMELISTREQ_TEAMFFA; case game_type_teamctf: return CLIENT_GAMELISTREQ_TEAMCTF; case game_type_pgl: return CLIENT_GAMELISTREQ_PGL; case game_type_topvbot: return CLIENT_GAMELISTREQ_TOPVBOT; case game_type_diablo: return CLIENT_GAMELISTREQ_DIABLO; case game_type_diablo2open: return SERVER_GAMELISTREPLY_TYPE_DIABLO2_OPEN; case game_type_diablo2closed: eventlog(eventlog_level_error, __FUNCTION__, "don't know how to list Diablo II"); return 0; case game_type_anongame: return 0; case game_type_none: default: eventlog(eventlog_level_error, __FUNCTION__, "unknown game type {}", (unsigned int)gtype); return 0xffff; } } extern t_game_option bngoption_to_goption(t_clienttag clienttag, t_game_type gtype, unsigned short bngoption) { char clienttag_str[5]; if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clienttag"); return game_option_none; } if (clienttag == CLIENTTAG_WARCIIBNE_UINT) { switch (gtype) { case game_type_topvbot: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_TOPVBOT_1: return game_option_topvbot_1; case CLIENT_STARTGAME4_OPTION_TOPVBOT_2: return game_option_topvbot_2; case CLIENT_STARTGAME4_OPTION_TOPVBOT_3: return game_option_topvbot_3; case CLIENT_STARTGAME4_OPTION_TOPVBOT_4: return game_option_topvbot_4; case CLIENT_STARTGAME4_OPTION_TOPVBOT_5: return game_option_topvbot_5; case CLIENT_STARTGAME4_OPTION_TOPVBOT_6: return game_option_topvbot_6; case CLIENT_STARTGAME4_OPTION_TOPVBOT_7: return game_option_topvbot_7; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_melee: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_MELEE_NORMAL: return game_option_melee_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_ffa: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_FFA_NORMAL: return game_option_ffa_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_oneonone: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_ONEONONE_NORMAL: return game_option_oneonone_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_ladder: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_LADDER_COUNTASLOSS: return game_option_ladder_countasloss; case CLIENT_STARTGAME4_OPTION_LADDER_NOPENALTY: return game_option_ladder_nopenalty; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_ironman: switch (bngoption) { default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_mapset: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_MAPSET_NORMAL: return game_option_mapset_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Warcraft II game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), (unsigned int)gtype); return game_option_none; } } else if (clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT) { switch (gtype) { case game_type_diablo2open: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_NONE: /* FIXME: really? */ return game_option_none; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_diablo2closed: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_NONE: /* FIXME: really? */ return game_option_none; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo II bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo II game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), (unsigned int)gtype); return game_option_none; } } else if (clienttag == CLIENTTAG_DIABLORTL_UINT || clienttag == CLIENTTAG_DIABLOSHR_UINT) { switch (gtype) { case game_type_diablo: switch (bngoption) { default: /* diablo doesn't use any options */ return game_option_none; } default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Diablo game type \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), (unsigned int)gtype); return game_option_none; } } else if (clienttag == CLIENTTAG_STARCRAFT_UINT || clienttag == CLIENTTAG_BROODWARS_UINT || clienttag == CLIENTTAG_SHAREWARE_UINT) { switch (gtype) { case game_type_melee: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_MELEE_NORMAL: return game_option_melee_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_ffa: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_FFA_NORMAL: return game_option_ffa_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_oneonone: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_ONEONONE_NORMAL: return game_option_oneonone_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_ctf: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_CTF_NORMAL: return game_option_ctf_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_greed: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_GREED_10000: return game_option_greed_10000; case CLIENT_STARTGAME4_OPTION_GREED_7500: return game_option_greed_7500; case CLIENT_STARTGAME4_OPTION_GREED_5000: return game_option_greed_5000; case CLIENT_STARTGAME4_OPTION_GREED_2500: return game_option_greed_2500; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_slaughter: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_SLAUGHTER_60: return game_option_slaughter_60; case CLIENT_STARTGAME4_OPTION_SLAUGHTER_45: return game_option_slaughter_45; case CLIENT_STARTGAME4_OPTION_SLAUGHTER_30: return game_option_slaughter_30; case CLIENT_STARTGAME4_OPTION_SLAUGHTER_15: return game_option_slaughter_15; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_sdeath: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_SDEATH_NORMAL: return game_option_sdeath_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_ladder: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_LADDER_COUNTASLOSS: return game_option_ladder_countasloss; case CLIENT_STARTGAME4_OPTION_LADDER_NOPENALTY: return game_option_ladder_nopenalty; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_mapset: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_MAPSET_NORMAL: return game_option_mapset_normal; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_teammelee: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_TEAMMELEE_4: return game_option_teammelee_4; case CLIENT_STARTGAME4_OPTION_TEAMMELEE_3: return game_option_teammelee_3; case CLIENT_STARTGAME4_OPTION_TEAMMELEE_2: return game_option_teammelee_2; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_teamffa: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_TEAMFFA_4: return game_option_teamffa_4; case CLIENT_STARTGAME4_OPTION_TEAMFFA_3: return game_option_teamffa_3; case CLIENT_STARTGAME4_OPTION_TEAMFFA_2: return game_option_teamffa_2; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_teamctf: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_TEAMCTF_4: return game_option_teamctf_4; case CLIENT_STARTGAME4_OPTION_TEAMCTF_3: return game_option_teamctf_3; case CLIENT_STARTGAME4_OPTION_TEAMCTF_2: return game_option_teamctf_2; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_pgl: switch (bngoption) { default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_topvbot: switch (bngoption) { case CLIENT_STARTGAME4_OPTION_TOPVBOT_1: return game_option_topvbot_1; case CLIENT_STARTGAME4_OPTION_TOPVBOT_2: return game_option_topvbot_2; case CLIENT_STARTGAME4_OPTION_TOPVBOT_3: return game_option_topvbot_3; case CLIENT_STARTGAME4_OPTION_TOPVBOT_4: return game_option_topvbot_4; case CLIENT_STARTGAME4_OPTION_TOPVBOT_5: return game_option_topvbot_5; case CLIENT_STARTGAME4_OPTION_TOPVBOT_6: return game_option_topvbot_6; case CLIENT_STARTGAME4_OPTION_TOPVBOT_7: return game_option_topvbot_7; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft bnet game option for \"{}\" game \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), game_type_get_str(gtype), bngoption); return game_option_none; } case game_type_none: default: eventlog(eventlog_level_error, __FUNCTION__, "unknown Starcraft game type \"{}\" {}({})", tag_uint_to_str(clienttag_str, clienttag), (unsigned int)gtype, game_type_get_str(gtype)); return game_option_none; } } else if (clienttag == CLIENTTAG_WARCRAFT3_UINT || clienttag == CLIENTTAG_WAR3XP_UINT) { return game_option_none; } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown game clienttag \"{}\" {}", tag_uint_to_str(clienttag_str, clienttag), (unsigned int)gtype); return game_option_none; } } extern t_game_result bngresult_to_gresult(unsigned int bngresult) { switch (bngresult) { case CLIENT_GAME_REPORT_RESULT_PLAYING: return game_result_playing; case CLIENT_GAME_REPORT_RESULT_WIN: return game_result_win; case CLIENT_GAME_REPORT_RESULT_LOSS: return game_result_loss; case CLIENT_GAME_REPORT_RESULT_DRAW: return game_result_draw; case CLIENT_GAME_REPORT_RESULT_DISCONNECT: return game_result_disconnect; case CLIENT_GAME_REPORT_RESULT_OBSERVER: return game_result_observer; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown bnet game result {}", bngresult); return game_result_disconnect; /* bad packet? */ } } extern t_game_maptype bngmaptype_to_gmaptype(unsigned int bngmaptype) { switch (bngmaptype) { case CLIENT_MAPTYPE_SELFMADE: return game_maptype_selfmade; case CLIENT_MAPTYPE_BLIZZARD: return game_maptype_blizzard; case CLIENT_MAPTYPE_LADDER: return game_maptype_ladder; case CLIENT_MAPTYPE_PGL: return game_maptype_pgl; case CLIENT_MAPTYPE_KBK: return game_maptype_kbk; case CLIENT_MAPTYPE_CompUSA: return game_maptype_compusa; default: return game_maptype_none; } } extern t_game_tileset bngtileset_to_gtileset(unsigned int bngtileset) { switch (bngtileset) { case CLIENT_TILESET_BADLANDS: return game_tileset_badlands; case CLIENT_TILESET_SPACE: return game_tileset_space; case CLIENT_TILESET_INSTALLATION: return game_tileset_installation; case CLIENT_TILESET_ASHWORLD: return game_tileset_ashworld; case CLIENT_TILESET_JUNGLE: return game_tileset_jungle; case CLIENT_TILESET_DESERT: return game_tileset_desert; case CLIENT_TILESET_ICE: return game_tileset_ice; case CLIENT_TILESET_TWILIGHT: return game_tileset_twilight; default: return game_tileset_none; } } extern t_game_speed bngspeed_to_gspeed(unsigned int bngspeed) { switch (bngspeed) { case CLIENT_GAMESPEED_SLOWEST: return game_speed_slowest; case CLIENT_GAMESPEED_SLOWER: return game_speed_slower; case CLIENT_GAMESPEED_SLOW: return game_speed_slow; case CLIENT_GAMESPEED_NORMAL: return game_speed_normal; case CLIENT_GAMESPEED_FAST: return game_speed_fast; case CLIENT_GAMESPEED_FASTER: return game_speed_faster; case CLIENT_GAMESPEED_FASTEST: return game_speed_fastest; default: return game_speed_none; } } t_game_speed w3speed_to_gspeed(unsigned int w3speed) { switch (w3speed) { case 0: return game_speed_slow; case 1: return game_speed_normal; case 2: return game_speed_fast; default: return game_speed_none; } } extern t_game_difficulty bngdifficulty_to_gdifficulty(unsigned int bngdifficulty) { switch (bngdifficulty) { case CLIENT_DIFFICULTY_NORMAL: return game_difficulty_normal; case CLIENT_DIFFICULTY_NIGHTMARE: return game_difficulty_nightmare; case CLIENT_DIFFICULTY_HELL: return game_difficulty_hell; case CLIENT_DIFFICULTY_HARDCORE_NORMAL: return game_difficulty_hardcore_normal; case CLIENT_DIFFICULTY_HARDCORE_NIGHTMARE: return game_difficulty_hardcore_nightmare; case CLIENT_DIFFICULTY_HARDCORE_HELL: return game_difficulty_hardcore_hell; default: return game_difficulty_none; } } static const char * _w3_decrypt_mapinfo(const char *enc) { char *mapinfo; char *dec; unsigned pos; unsigned char bitmask; if (!(mapinfo = xstrdup(enc))) { eventlog(eventlog_level_error, __FUNCTION__, "not enough memory to setup temporary buffer"); return NULL; } dec = mapinfo; pos = 0; bitmask = 0; /* stupid gcc warning */ while (*enc) { if (pos % 8) { *dec = *enc; if (!(bitmask & 0x1)) (*dec)--; dec++; bitmask >>= 1; } else bitmask = *enc >> 1; enc++; pos++; } *dec = '\0'; return mapinfo; } extern int game_parse_info(t_game * game, char const * gameinfo) { t_clienttag clienttag; char * save; char * line1; char * line2; char * currtok; char const * unknown; char const * mapsize; char const * maxplayers; char const * speed; char const * maptype; char const * gametype; char const * option; char const * checksum; char const * tileset; char const * player; char const * mapname; unsigned int bngmapsize; unsigned int bngmaxplayers; unsigned int bngspeed; unsigned int bngmaptype; unsigned int bngtileset; if (!game) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game"); return -1; } if (!gameinfo) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL gameinfo"); return -1; } /* BW 104: 9: recv class=bnet[0x01] type=CLIENT_STARTGAME4[0x1cff] length=74 0000: FF 1C 4A 00 00 00 00 00 00 00 00 00 09 00 01 00 ..J............. 0010: 00 00 00 00 01 00 00 00 4E 4F 50 45 4E 41 4C 54 ........NOPENALT 0020: 59 00 00 2C 34 34 2C 31 34 2C 2C 32 2C 39 2C 31 Y..,44,14,,2,9,1 0030: 2C 33 65 33 37 61 38 34 63 2C 33 2C 52 6F 73 73 ,3e37a84c,3,Ross 0040: 0D 41 73 68 72 69 67 6F 0D 00 .Ashrigo.. Diablo: 7: cli class=bnet[0x01] type=CLIENT_STARTGAME3[0x1aff] length=105 0000: FF 1A 69 00 01 00 00 00 00 00 00 00 00 00 00 00 ..i............. 0010: 0F 00 00 00 00 00 00 00 E0 17 00 00 61 73 64 66 ............asdf 0020: 61 73 64 66 61 73 64 66 61 73 64 66 32 00 61 73 asdfasdfasdf2.as 0030: 64 66 61 73 64 66 73 61 64 66 61 73 64 66 32 00 dfasdfsadfasdf2. 0040: 30 0D 7A 62 6E 7A 62 6E 7A 62 6E 0D 4C 54 52 44 0.zbnzbnzbn.LTRD 0050: 20 31 20 30 20 30 20 33 30 20 31 30 20 32 30 20 1 0 0 30 10 20 0060: 32 35 20 31 30 30 20 30 00 25 100 0. 10: recv class=bnet[0x01] type=CLIENT_STARTGAME3[0x1aff] length=72 0000: FF 1A 48 00 00 00 00 00 00 00 00 00 00 00 00 00 ..H............. 0010: 0F 00 00 00 00 00 00 00 E0 17 00 00 61 6E 73 00 ............ans. 0020: 00 30 0D 77 61 72 72 69 6F 72 0D 4C 54 52 44 20 .0.warrior.LTRD 0030: 31 20 30 20 30 20 33 30 20 31 30 20 32 30 20 32 1 0 0 30 10 20 2 0040: 35 20 31 30 30 20 30 00 5 100 0. Warcraft: 4: srv class=bnet[0x01] type=SERVER_GAMELISTREPLY[0x09ff] length=524 0000: FF 09 0C 02 05 00 00 00 02 00 01 00 09 04 00 00 ................ 0010: 02 00 17 E0 D0 C6 66 F0 00 00 00 00 00 00 00 00 ......f......... 0020: 04 00 00 00 74 00 00 00 48 65 79 77 6F 6F 64 73 ....t...Heywoods 0030: 20 47 4F 57 32 76 32 00 00 2C 2C 2C 36 2C 32 2C GOW2v2..,,,6,2, 0040: 32 2C 31 2C 31 39 37 33 64 35 66 32 2C 33 30 30 2,1,1973d5f2,300 0050: 30 2C 48 65 79 77 6F 6F 64 0D 47 61 72 64 65 6E 0,Heywood.Garden 0060: 20 6F 66 20 77 61 72 20 42 4E 45 2E 70 75 64 0D of war BNE.pud. 0070: 00 09 00 02 00 09 04 00 00 02 00 17 E0 CF 45 3E ..............E> 0080: 65 00 00 00 00 00 00 00 00 10 00 00 00 11 00 00 e............... 0090: 00 67 6F 77 20 6C 61 64 64 65 72 20 31 76 31 00 .gow ladder 1v1. 00A0: 00 2C 2C 2C 36 2C 32 2C 39 2C 32 2C 65 66 38 34 .,,,6,2,9,2,ef84 00B0: 33 61 35 33 2C 2C 77 63 73 63 6D 61 73 74 65 72 3a53,,wcscmaster 00C0: 0D 47 61 72 64 65 6E 20 6F 66 20 77 61 72 20 42 .Garden of war B 00D0: 4E 45 2E 70 75 64 0D 00 09 00 01 00 09 04 00 00 NE.pud.......... 00E0: 02 00 17 E0 98 CE 7C E2 00 00 00 00 00 00 00 00 ......|......... 00F0: 04 00 00 00 4D 00 00 00 4C 61 64 64 65 72 20 46 ....M...Ladder F 0100: 46 41 20 6F 6E 6C 79 00 00 2C 2C 2C 36 2C 32 2C FA only..,,,6,2, 0110: 39 2C 31 2C 61 65 32 66 61 61 62 37 2C 2C 6B 69 9,1,ae2faab7,,ki 0120: 6C 6F 67 72 61 6D 0D 50 6C 61 69 6E 73 20 6F 66 logram.Plains of 0130: 20 73 6E 6F 77 20 42 4E 45 2E 70 75 64 0D 00 0F snow BNE.pud... 0140: 00 04 00 09 04 00 00 02 00 17 E0 C6 0B 13 3C 00 ..............<. 0150: 00 00 00 00 00 00 00 04 00 00 00 BE 00 00 00 4C ...............L 0160: 61 64 64 65 72 20 31 20 6F 6E 20 31 00 00 2C 2C adder 1 on 1..,, 0170: 2C 36 2C 32 2C 66 2C 34 2C 66 63 63 35 38 65 34 ,6,2,f,4,fcc58e4 0180: 61 2C 37 32 30 30 2C 49 63 65 36 39 62 75 72 67 a,7200,Ice69burg 0190: 0D 46 6F 72 65 73 74 20 54 72 61 69 6C 20 42 4E .Forest Trail BN 01A0: 45 2E 70 75 64 0D 00 0F 00 04 00 09 04 00 00 02 E.pud........... 01B0: 00 04 15 D1 F4 B6 6A 00 00 00 00 00 00 00 00 04 ......j......... 01C0: 00 00 00 C8 03 00 00 52 6F 63 6B 20 49 74 21 21 .......Rock It!! 01D0: 20 32 76 32 2B 00 00 2C 2C 2C 38 2C 31 2C 66 2C 2v2+..,,,8,1,f, 01E0: 34 2C 62 30 36 38 63 62 34 62 2C 66 30 30 30 2C 4,b068cb4b,f000, 01F0: 6E 75 66 74 63 72 6F 77 0D 43 72 6F 73 73 68 61 nuftcrow.Crossha 0200: 69 72 20 42 4E 45 2E 70 75 64 0D 00 ir BNE.pud.. FIXME: Note the map size and max players fields are empty. Does WCII have different map sizes and starting positions? If so, are they reported here? Also note the map tileset is "3000". Maybe this isn't the tileset for WCII but instead the starting gold or something. FIXME: Also, what is the upper player limit on WCII... 8 like on Starcraft? */ if (!(clienttag = game_get_clienttag(game))) { eventlog(eventlog_level_error, __FUNCTION__, "NULL clienttag for game?"); return -1; } if (clienttag == CLIENTTAG_DIABLORTL_UINT || clienttag == CLIENTTAG_DIABLOSHR_UINT) { game_set_maxplayers(game, 4); eventlog(eventlog_level_debug, __FUNCTION__, "no gameinfo for Diablo"); return 0; } else if (clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT) { if ((game->type == game_type_diablo2closed) && (!std::strlen(gameinfo))) { /* D2 closed games are handled by d2cs so we can have only have a generic startgame4 without any info :(, this fix also a memory leak for description allocation */ game_set_difficulty(game, game_difficulty_none); game_set_description(game, ""); } else { char difficulty[2]; unsigned int bngdifficulty; if (!std::strlen(gameinfo)) { eventlog(eventlog_level_info, __FUNCTION__, "got empty gameinfo (from D2 client)"); return -1; } difficulty[0] = gameinfo[0]; difficulty[1] = '\0'; if (str_to_uint(difficulty, &bngdifficulty) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing difficulty) \"{}\"", gameinfo); return -1; } game_set_difficulty(game, bngdifficulty); game_set_description(game, &gameinfo[1]); if ((game->type == game_type_diablo2closed)) { eventlog(eventlog_level_debug, __FUNCTION__, "D2 bug workarround needed (open games tagged as closed)"); game->type = game_type_diablo2open; } } return 0; } else if (clienttag == CLIENTTAG_WARCRAFT3_UINT || clienttag == CLIENTTAG_WAR3XP_UINT) { /* Warcraft 3 game info format -- by Soar 0x00 -- 1 byte (char, Empty slots) 0x01 -- 8 bytes (char[8], Count of games created) From offset 0x11 there is a bitmask byte before every 7 bytes, offset 0x09 to 0x10 also has bitmask 0. Bit 0 corresponds to byte 0, bit 1 to byte 1, and so on. Except offset 0x09, only Byte 1-7 contribute to the info data, bit 0 seems to be always 1; Decoding these bytes works as follows: If the corresponding bit is a '1' then the character is moved over directly. If the corresponding bit is a '0' then subtract 1 from the character. (We decode info data and remove the bitmask bytes from info data in following description) 0x09 -- 5 bytes (char[5], map options) 0x0e -- 1 bytes (0, seems to be a seperate sign) 0x0f -- 2 bytes (short, mapsize x) 0x11 -- 2 bytes (short, mapsize y) 0x13 -- 4 bytes (long, unknown, map checksum ?) 0x17 -- n bytes (string, mapname \0 terminated) 0x17+n -- m bytes (string, game creator \0 terminated, but we already set this to game before, :P) 0x17+n+m -- 2 bytes: \0 \0 (first \0 might be a reserved string for password, but blizzard didn't implement it, second \0 is the info string end sign) */ const char *pstr; if (!std::strlen(gameinfo)) { eventlog(eventlog_level_info, __FUNCTION__, "got empty gameinfo (from W3 client)"); return -1; } if (std::strlen(gameinfo) < 0xf + 2 + 1 + 2 + 4) { eventlog(eventlog_level_error, __FUNCTION__, "got too short W3 mapinfo"); return -1; } pstr = gameinfo + 9; pstr = _w3_decrypt_mapinfo(pstr); if (!pstr) return -1; /* after decryption we dont have the mask bytes anymore so offsets need * to be adjusted acordingly */ game_set_speed(game, w3speed_to_gspeed(bn_byte_get(*((bn_byte*)(pstr))))); game_set_mapsize_x(game, bn_short_get(*((bn_short*)(pstr + 5)))); game_set_mapsize_y(game, bn_short_get(*((bn_short*)(pstr + 7)))); game_set_mapname(game, pstr + 13); xfree((void*)pstr); return 0; } /* otherwise it's Starcraft, Brood War, or Warcraft II */ if (!(save = xstrdup(gameinfo))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for save"); return -1; } if (!(line1 = std::strtok(save, "\r"))) /* actual game info fields */ { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing line1) \"{}\"", gameinfo); xfree(save); return -1; } if (!(line2 = std::strtok(NULL, "\r"))) { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing player) \"{}\"", gameinfo); xfree(save); return -1; } /* is there room for another field after that? */ /* * This is the same as the normal std::strtok() function but it doesn't skip over * empty entries. The C-library std::strtok() will skip past entries like 12,,3 * and rather than you being able to get "12", "", "3" you get "12", "3". * Since some values returned by the client contain these empty entries we * need this. Unlike std::strtok() all state is recorded in the first argument. */ currtok = line1; if (!(unknown = strsep(&currtok, ","))) /* skip past first field (always empty?) */ { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing unknown)"); xfree(save); return -1; } if (!(mapsize = strsep(&currtok, ","))) { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing mapsize)"); xfree(save); return -1; } if (!(maxplayers = strsep(&currtok, ","))) /* for later use (FIXME: what is upper field, max observers?) */ { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing maxplayers)"); xfree(save); return -1; } if (!(speed = strsep(&currtok, ","))) { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing speed)"); xfree(save); return -1; } if (!(maptype = strsep(&currtok, ","))) { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing maptype)"); xfree(save); return -1; } if (!(gametype = strsep(&currtok, ","))) /* this is set from another field */ { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing gametype)"); xfree(save); return -1; } if (!(option = strsep(&currtok, ","))) /* this is set from another field */ { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing option)"); xfree(save); return -1; } if (!(checksum = strsep(&currtok, ","))) /* FIXME */ { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing checksum)"); xfree(save); return -1; } if (!(tileset = strsep(&currtok, ","))) { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing tileset)"); xfree(save); return -1; } if (!(player = strsep(&currtok, ","))) { eventlog(eventlog_level_error, __FUNCTION__, "bad gameinfo format (missing player)"); xfree(save); return -1; } mapname = line2; /* only one item on this line */ eventlog(eventlog_level_debug, __FUNCTION__, "got info \"{}\" \"{}\" \"{}\" \"{}\" \"{}\" \"{}\" \"{}\" \"{}\" \"{}\" \"{}\"", mapsize, maxplayers, speed, maptype, gametype, option, checksum, tileset, player, mapname); /* The map size is determined by breaking the number into two pieces and * multiplying each piece by 32. * for example, 34 = (32*3) x (32*4) = 96 x 128 */ /* special handling for mapsize. empty is 256x256 */ if ((mapsize[0] == '\0') || (str_to_uint(mapsize, &bngmapsize) < 0)) bngmapsize = 88; /* 256x256 */ game_set_mapsize_x(game, (bngmapsize / 10) * 32); game_set_mapsize_y(game, (bngmapsize % 10) * 32); /* special handling for maxplayers, empty is 8 */ if ((maxplayers[0] == '\0') || (str_to_uint(maxplayers, &bngmaxplayers) < 0)) bngmaxplayers = 8; game_set_maxplayers(game, (bngmaxplayers % 10)); /* special handling for gamespeed. empty is fast */ if ((speed[0] == '\0') || (str_to_uint(speed, &bngspeed) < 0)) bngspeed = CLIENT_GAMESPEED_FAST; game_set_speed(game, bngspeed_to_gspeed(bngspeed)); /* special handling for maptype. empty is self-made */ if ((maptype[0] == '\0') || (str_to_uint(maptype, &bngmaptype) < 0)) bngmaptype = CLIENT_MAPTYPE_SELFMADE; game_set_maptype(game, bngmaptype_to_gmaptype(bngmaptype)); /* special handling for tileset. empty is badlands */ if ((tileset[0] == '\0') || (str_to_uint(tileset, &bngtileset) < 0)) bngtileset = CLIENT_TILESET_BADLANDS; game_set_tileset(game, bngtileset_to_gtileset(bngtileset)); game_set_mapname(game, mapname); xfree(save); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/game_conv.h000066400000000000000000000034141462640461400201740ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_GAME_CONV_PROTOS #define INCLUDED_GAME_CONV_PROTOS #define JUST_NEED_TYPES #include "game.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern t_game_type bngreqtype_to_gtype(t_clienttag clienttag, unsigned short bngtype); extern t_game_type bngtype_to_gtype(t_clienttag clienttag, unsigned short bngtype); extern unsigned short gtype_to_bngtype(t_game_type gtype); extern t_game_option bngoption_to_goption(t_clienttag clienttag, t_game_type gtype, unsigned short bngoption); extern t_game_result bngresult_to_gresult(unsigned int bngresult); extern t_game_maptype bngmaptype_to_gmaptype(unsigned int bngmaptype); extern t_game_tileset bngtileset_to_gtileset(unsigned int bngtileset); extern t_game_speed bngspeed_to_gspeed(unsigned int bngspeed); extern t_game_difficulty bngdifficulty_to_gdifficulty(unsigned int bngdifficulty); extern int game_parse_info(t_game * game, char const * gameinfo); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_anongame.cpp000066400000000000000000001273241462640461400217000ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_anongame.h" #include #include #include "common/eventlog.h" #include "common/bn_type.h" #include "common/bnettime.h" #include "common/tag.h" #include "common/list.h" #include "clan.h" #include "account.h" #include "account_wrap.h" #include "ladder.h" #include "team.h" #include "anongame.h" #include "anongame_infos.h" #include "server.h" #include "tournament.h" #include "channel.h" #include "common/setup_after.h" #include "icons.h" namespace pvpgn { namespace bnetd { /* option - handling function */ /* 0x00 */ /* PG style search - handle_anongame_search() in anongame.c */ /* 0x01 */ /* server side packet sent from handle_anongame_search() in anongame.c */ /* 0x02 */ static int _client_anongame_infos(t_connection * c, t_packet const * const packet); /* 0x03 */ static int _client_anongame_cancel(t_connection * c); /* 0x04 */ static int _client_anongame_profile(t_connection * c, t_packet const * const packet); /* 0x05 */ /* AT style search - handle_anongame_search() in anongame.c */ /* 0x06 */ /* AT style search (Inviter) handle_anongame_search() in anongame.c */ /* 0x07 */ static int _client_anongame_tournament(t_connection * c, t_packet const * const packet); /* 0x08 */ static int _client_anongame_profile_clan(t_connection * c, t_packet const * const packet); /* 0x09 */ static int _client_anongame_get_icon(t_connection * c, t_packet const * const packet); /* 0x0A */ static int _client_anongame_set_icon(t_connection * c, t_packet const * const packet); static int check_user_icon(t_account * account, const char * user_icon); /* misc functions used by _client_anongame_tournament() */ static unsigned int _tournament_time_convert(unsigned int time); /* and now the functions */ static int _client_anongame_profile_clan(t_connection * c, t_packet const * const packet) { t_packet * rpacket; int clantag; int clienttag; int count; int temp; t_clan * clan; unsigned char rescount; if (packet_get_size(packet) < sizeof(t_client_findanongame_profile_clan)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ANONGAME_PROFILE_CLAN packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_findanongame_profile_clan), packet_get_size(packet)); return -1; } clantag = bn_int_get(packet->u.client_findanongame_profile_clan.clantag); clienttag = bn_int_get(packet->u.client_findanongame_profile_clan.clienttag); count = bn_int_get(packet->u.server_findanongame_profile_clan.count); clan = clanlist_find_clan_by_clantag(clantag); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_findanongame_profile_clan)); packet_set_type(rpacket, SERVER_FINDANONGAME_PROFILE_CLAN); bn_byte_set(&rpacket->u.server_findanongame_profile_clan.option, CLIENT_FINDANONGAME_PROFILE_CLAN); bn_int_set(&rpacket->u.server_findanongame_profile_clan.count, count); rescount = 0; temp = 0; packet_append_data(rpacket, &temp, 1); /* if (!(clan)) { temp = 0; packet_append_data(rpacket, &temp, 1); } else { temp = 0; packet_append_data(rpacket, &temp, 1); /* UNDONE: need to add clan stuff here: format: bn_int ladder_tag (SNLC, 2NLC, 3NLC, 4NLC) bn_int wins bn_int losses bn_byte rank bn_byte progess bar bn_int xp bn_int rank bn_byte 0x06 <-- random + 5 races 6 times: bn_int wins bn_int losses / } */ bn_byte_set(&rpacket->u.server_findanongame_profile_clan.rescount, rescount); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_anongame_profile(t_connection * c, t_packet const * const packet) { t_packet * rpacket; char const * username; int Count, i; int temp; t_account * account; t_connection * dest_c; t_clienttag ctag; char clienttag_str[5]; t_list * teamlist; unsigned char teamcount; unsigned char *atcountp; t_elem * curr; t_team * team; t_bnettime bn_time; bn_long ltime; Count = bn_int_get(packet->u.client_findanongame.count); eventlog(eventlog_level_info, __FUNCTION__, "[{}] got a FINDANONGAME PROFILE packet", conn_get_socket(c)); if (!(username = packet_get_str_const(packet, sizeof(t_client_findanongame_profile), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad FINDANONGAME_PROFILE (missing or too long username)", conn_get_socket(c)); return -1; } //If no account is found then break if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_error, __FUNCTION__, "Could not get account - PROFILE"); return -1; } if (!(dest_c = connlist_find_connection_by_accountname(username))) { eventlog(eventlog_level_debug, __FUNCTION__, "account is offline - try ll_clienttag"); if (!(ctag = account_get_ll_clienttag(account))) return -1; } else ctag = conn_get_clienttag(dest_c); eventlog(eventlog_level_info, __FUNCTION__, "Looking up {}'s {} Stats.", username, tag_uint_to_str(clienttag_str, ctag)); if (account_get_ladder_level(account, ctag, ladder_id_solo) <= 0 && account_get_ladder_level(account, ctag, ladder_id_team) <= 0 && account_get_ladder_level(account, ctag, ladder_id_ffa) <= 0 && account_get_teams(account) == NULL) { eventlog(eventlog_level_info, __FUNCTION__, "{} does not have WAR3 Stats.", username); if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_findanongame_profile2)); packet_set_type(rpacket, SERVER_FINDANONGAME_PROFILE); bn_byte_set(&rpacket->u.server_findanongame_profile2.option, CLIENT_FINDANONGAME_PROFILE); bn_int_set(&rpacket->u.server_findanongame_profile2.count, Count); bn_int_set(&rpacket->u.server_findanongame_profile2.icon, account_icon_to_profile_icon(account_get_user_icon(account, ctag), account, ctag)); bn_byte_set(&rpacket->u.server_findanongame_profile2.rescount, 0); temp = 0; packet_append_data(rpacket, &temp, 2); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } else // If they do have a profile then: { int solowins = account_get_ladder_wins(account, ctag, ladder_id_solo); int sololoss = account_get_ladder_losses(account, ctag, ladder_id_solo); int soloxp = account_get_ladder_xp(account, ctag, ladder_id_solo); int sololevel = account_get_ladder_level(account, ctag, ladder_id_solo); int solorank = account_get_ladder_rank(account, ctag, ladder_id_solo); int teamwins = account_get_ladder_wins(account, ctag, ladder_id_team); int teamloss = account_get_ladder_losses(account, ctag, ladder_id_team); int teamxp = account_get_ladder_xp(account, ctag, ladder_id_team); int teamlevel = account_get_ladder_level(account, ctag, ladder_id_team); int teamrank = account_get_ladder_rank(account, ctag, ladder_id_team); int ffawins = account_get_ladder_wins(account, ctag, ladder_id_ffa); int ffaloss = account_get_ladder_losses(account, ctag, ladder_id_ffa); int ffaxp = account_get_ladder_xp(account, ctag, ladder_id_ffa); int ffalevel = account_get_ladder_level(account, ctag, ladder_id_ffa); int ffarank = account_get_ladder_rank(account, ctag, ladder_id_ffa); int humanwins = account_get_racewins(account, W3_RACE_HUMANS, ctag); int humanlosses = account_get_racelosses(account, W3_RACE_HUMANS, ctag); int orcwins = account_get_racewins(account, W3_RACE_ORCS, ctag); int orclosses = account_get_racelosses(account, W3_RACE_ORCS, ctag); int undeadwins = account_get_racewins(account, W3_RACE_UNDEAD, ctag); int undeadlosses = account_get_racelosses(account, W3_RACE_UNDEAD, ctag); int nightelfwins = account_get_racewins(account, W3_RACE_NIGHTELVES, ctag); int nightelflosses = account_get_racelosses(account, W3_RACE_NIGHTELVES, ctag); int randomwins = account_get_racewins(account, W3_RACE_RANDOM, ctag); int randomlosses = account_get_racelosses(account, W3_RACE_RANDOM, ctag); int tourneywins = account_get_racewins(account, W3_RACE_DEMONS, ctag); int tourneylosses = account_get_racelosses(account, W3_RACE_DEMONS, ctag); unsigned char rescount; if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_findanongame_profile2)); packet_set_type(rpacket, SERVER_FINDANONGAME_PROFILE); bn_byte_set(&rpacket->u.server_findanongame_profile2.option, CLIENT_FINDANONGAME_PROFILE); bn_int_set(&rpacket->u.server_findanongame_profile2.count, Count); //job count bn_int_set(&rpacket->u.server_findanongame_profile2.icon, account_icon_to_profile_icon(account_get_user_icon(account, ctag), account, ctag)); rescount = 0; if (sololevel > 0) { bn_int_set((bn_int*)&temp, 0x534F4C4F); // SOLO backwards packet_append_data(rpacket, &temp, 4); temp = 0; bn_int_set((bn_int*)&temp, solowins); packet_append_data(rpacket, &temp, 2); //SOLO WINS bn_int_set((bn_int*)&temp, sololoss); packet_append_data(rpacket, &temp, 2); // SOLO LOSSES bn_int_set((bn_int*)&temp, sololevel); packet_append_data(rpacket, &temp, 1); // SOLO LEVEL bn_int_set((bn_int*)&temp, account_get_profile_calcs(account, soloxp, sololevel)); packet_append_data(rpacket, &temp, 1); // SOLO PROFILE CALC bn_int_set((bn_int *)&temp, soloxp); packet_append_data(rpacket, &temp, 2); // SOLO XP bn_int_set((bn_int *)&temp, solorank); packet_append_data(rpacket, &temp, 4); // SOLO LADDER RANK rescount++; } if (teamlevel > 0) { //below is for team records. Add this after 2v2,3v3,4v4 are done bn_int_set((bn_int*)&temp, 0x5445414D); packet_append_data(rpacket, &temp, 4); bn_int_set((bn_int*)&temp, teamwins); packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, teamloss); packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, teamlevel); packet_append_data(rpacket, &temp, 1); bn_int_set((bn_int*)&temp, account_get_profile_calcs(account, teamxp, teamlevel)); packet_append_data(rpacket, &temp, 1); bn_int_set((bn_int*)&temp, teamxp); packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, teamrank); packet_append_data(rpacket, &temp, 4); //done of team game stats rescount++; } if (ffalevel > 0) { bn_int_set((bn_int*)&temp, 0x46464120); packet_append_data(rpacket, &temp, 4); bn_int_set((bn_int*)&temp, ffawins); packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, ffaloss); packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, ffalevel); packet_append_data(rpacket, &temp, 1); bn_int_set((bn_int*)&temp, account_get_profile_calcs(account, ffaxp, ffalevel)); packet_append_data(rpacket, &temp, 1); bn_int_set((bn_int*)&temp, ffaxp); packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, ffarank); packet_append_data(rpacket, &temp, 4); //End of FFA Stats rescount++; } /* set result count */ bn_byte_set(&rpacket->u.server_findanongame_profile2.rescount, rescount); bn_int_set((bn_int*)&temp, 0x06); //start of race stats packet_append_data(rpacket, &temp, 1); bn_int_set((bn_int*)&temp, randomwins); packet_append_data(rpacket, &temp, 2); //random wins bn_int_set((bn_int*)&temp, randomlosses); packet_append_data(rpacket, &temp, 2); //random losses bn_int_set((bn_int*)&temp, humanwins); packet_append_data(rpacket, &temp, 2); //human wins bn_int_set((bn_int*)&temp, humanlosses); packet_append_data(rpacket, &temp, 2); //human losses bn_int_set((bn_int*)&temp, orcwins); packet_append_data(rpacket, &temp, 2); //orc wins bn_int_set((bn_int*)&temp, orclosses); packet_append_data(rpacket, &temp, 2); //orc losses bn_int_set((bn_int*)&temp, undeadwins); packet_append_data(rpacket, &temp, 2); //undead wins bn_int_set((bn_int*)&temp, undeadlosses); packet_append_data(rpacket, &temp, 2); //undead losses bn_int_set((bn_int*)&temp, nightelfwins); packet_append_data(rpacket, &temp, 2); //elf wins bn_int_set((bn_int*)&temp, nightelflosses); packet_append_data(rpacket, &temp, 2); //elf losses bn_int_set((bn_int*)&temp, tourneywins); packet_append_data(rpacket, &temp, 2); //tourney wins bn_int_set((bn_int*)&temp, tourneylosses); packet_append_data(rpacket, &temp, 2); //tourney losses //end of normal stats - Start of AT stats /* 1 byte team count place holder, set later */ packet_append_data(rpacket, &temp, 1); /* we need to store the AT team count but we dont know yet the no * of stored teams so we cache the pointer for later use */ atcountp = (unsigned char *)packet_get_raw_data(rpacket, packet_get_size(rpacket) - 1); teamlist = account_get_teams(account); teamcount = 0; if (teamlist) { int teamtype[] = { 0, 0x32565332, 0x33565333, 0x34565334, 0x35565335, 0x36565336 }; LIST_TRAVERSE(teamlist, curr) { if (!(team = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (team_get_clienttag(team) != ctag) continue; bn_int_set((bn_int*)&temp, teamtype[team_get_size(team) - 1]); packet_append_data(rpacket, &temp, 4); bn_int_set((bn_int*)&temp, team_get_wins(team)); //at team wins packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, team_get_losses(team)); //at team losses packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, team_get_level(team)); packet_append_data(rpacket, &temp, 1); bn_int_set((bn_int*)&temp, account_get_profile_calcs(account, team_get_xp(team), team_get_level(team))); // xp bar calc packet_append_data(rpacket, &temp, 1); bn_int_set((bn_int*)&temp, team_get_xp(team)); packet_append_data(rpacket, &temp, 2); bn_int_set((bn_int*)&temp, team_get_rank(team)); //rank on AT ladder packet_append_data(rpacket, &temp, 4); bn_time = time_to_bnettime(temp, team_get_lastgame(team)); bnettime_to_bn_long(bn_time, <ime); packet_append_data(rpacket, <ime, 8); bn_int_set((bn_int*)&temp, team_get_size(team) - 1); packet_append_data(rpacket, &temp, 1); for (i = 0; i < team_get_size(team); i++) { if ((team_get_memberuid(team, i) != account_get_uid(account))) packet_append_string(rpacket, account_get_name(team_get_member(team, i))); //now attach the names to the packet - not including yourself // [quetzal] 20020826 } teamcount++; if ((teamcount >= 16)) break; } } *atcountp = (unsigned char)teamcount; conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); eventlog(eventlog_level_info, __FUNCTION__, "Sent {}'s WAR3 Stats (including {} teams) to requestor.", username, teamcount); } return 0; } static int _client_anongame_cancel(t_connection * c) { t_packet * rpacket; t_connection * tc[ANONGAME_MAX_GAMECOUNT / 2]; // [quetzal] 20020809 - added a_count, so we dont refer to already destroyed anongame t_anongame *a = conn_get_anongame(c); int a_count, i; eventlog(eventlog_level_info, __FUNCTION__, "[{}] got FINDANONGAME CANCEL packet", conn_get_socket(c)); if (!a) return -1; a_count = anongame_get_count(a); // anongame_unqueue(c, anongame_get_queue(a)); // -- already doing unqueue in conn_destroy_anongame for (i = 0; i < ANONGAME_MAX_GAMECOUNT / 2; i++) tc[i] = anongame_get_tc(a, i); for (i = 0; i < ANONGAME_MAX_GAMECOUNT / 2; i++) { if (tc[i] == NULL) continue; conn_set_routeconn(tc[i], NULL); conn_destroy_anongame(tc[i]); } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_findanongame_playgame_cancel)); packet_set_type(rpacket, SERVER_FINDANONGAME_PLAYGAME_CANCEL); bn_byte_set(&rpacket->u.server_findanongame_playgame_cancel.cancel, SERVER_FINDANONGAME_CANCEL); bn_int_set(&rpacket->u.server_findanongame_playgame_cancel.count, a_count); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } /* Open portrait in Warcraft 3 user profile */ static int _client_anongame_get_icon(t_connection * c, t_packet const * const packet) { t_packet * rpacket; //BlacKDicK 04/20/2003 Need some huge re-work on this. { struct { char icon_code[4]; unsigned int portrait_code; char race; bn_short required_wins; char client_enabled; } tempicon; //FIXME: Add those to the prefs and also merge them on accoun_wrap; // FIXED BY DJP 07/16/2003 FOR 110 CHANGE ( TOURNEY & RACE WINS ) + Table_witdh short icon_req_race_wins; short icon_req_tourney_wins; int race[] = { W3_RACE_RANDOM, W3_RACE_HUMANS, W3_RACE_ORCS, W3_RACE_UNDEAD, W3_RACE_NIGHTELVES, W3_RACE_DEMONS }; char race_char[6] = { 'R', 'H', 'O', 'U', 'N', 'D' }; char icon_pos[5] = { '2', '3', '4', '5', '6', }; char table_width = 6; char table_height = 5; int i, j; char rico; unsigned int rlvl, rwins; t_clienttag clienttag; t_account * acc; char user_icon[5]; char const * uicon; clienttag = conn_get_clienttag(c); acc = conn_get_account(c); /* WAR3 uses a different table size, might change if blizzard add tournament support to RoC */ if (clienttag == CLIENTTAG_WARCRAFT3_UINT) { table_width = 5; table_height = 4; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] got FINDANONGAME Get Icons packet", conn_get_socket(c)); if ((rpacket = packet_create(packet_class_bnet)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new packet"); return -1; } packet_set_size(rpacket, sizeof(t_server_findanongame_iconreply)); packet_set_type(rpacket, SERVER_FINDANONGAME_ICONREPLY); bn_int_set(&rpacket->u.server_findanongame_iconreply.count, bn_int_get(packet->u.client_findanongame_inforeq.count)); bn_byte_set(&rpacket->u.server_findanongame_iconreply.option, CLIENT_FINDANONGAME_GET_ICON); if (uicon = account_get_user_icon(acc, clienttag)) { std::memcpy(&rpacket->u.server_findanongame_iconreply.curricon, uicon, 4); } else { account_get_raceicon(acc, &rico, &rlvl, &rwins, clienttag); std::sprintf(user_icon, "%1d%c3W", rlvl, rico); std::memcpy(&rpacket->u.server_findanongame_iconreply.curricon, user_icon, 4); } // if custom stats is enabled then set a custom client icon by player rating // do not override user selected icon if any bool assignedCustomIcon = false; if (!uicon && prefs_get_custom_icons() == 1 && customicons_allowed_by_client(clienttag)) { if (t_icon_info * icon = customicons_get_icon_by_account(acc, clienttag)) { assignedCustomIcon = true; std::memcpy(&rpacket->u.server_findanongame_iconreply.curricon, icon->icon_code, 4); } } bn_byte_set(&rpacket->u.server_findanongame_iconreply.table_width, table_width); bn_byte_set(&rpacket->u.server_findanongame_iconreply.table_size, table_width*table_height); for (j = 0; j < table_height; j++){ icon_req_race_wins = anongame_infos_get_ICON_REQ(j + 1, clienttag); for (i = 0; i < table_width; i++){ tempicon.race = i; tempicon.icon_code[0] = icon_pos[j]; tempicon.icon_code[1] = race_char[i]; tempicon.icon_code[2] = '3'; tempicon.icon_code[3] = 'W'; tempicon.portrait_code = (account_icon_to_profile_icon(tempicon.icon_code, acc, clienttag)); if (i <= 4) { //Building the icon for the races bn_short_set(&tempicon.required_wins, icon_req_race_wins); if (assignedCustomIcon || account_get_racewins(acc, race[i], clienttag) < icon_req_race_wins) tempicon.client_enabled = 0; else tempicon.client_enabled = 1; } else { //Building the icon for the tourney icon_req_tourney_wins = anongame_infos_get_ICON_REQ_TOURNEY(j + 1); bn_short_set(&tempicon.required_wins, icon_req_tourney_wins); if (assignedCustomIcon || account_get_racewins(acc, race[i], clienttag) < icon_req_tourney_wins) tempicon.client_enabled = 0; else tempicon.client_enabled = 1; } packet_append_data(rpacket, &tempicon, sizeof(tempicon)); } } //Go,go,go conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } /* Choose icon by user from profile > portrait */ static int _client_anongame_set_icon(t_connection * c, t_packet const * const packet) { //BlacKDicK 04/20/2003 // Modified by aancw 16/12/2014 unsigned int desired_icon; char user_icon[5]; t_account * account = conn_get_account(c); t_clienttag clienttag = conn_get_clienttag(c); // do nothing when custom icon enabled and exists if (prefs_get_custom_icons() == 1 && customicons_allowed_by_client(clienttag) && customicons_get_icon_by_account(account, clienttag)) return 0; /*FIXME: In this case we do not get a 'count' but insted of it we get the icon that the client wants to set.'W3H2' for an example. For now it is ok, since they share the same position on the packet*/ desired_icon = bn_int_get(packet->u.client_findanongame.count); //user_icon[4]=0; if (desired_icon == 0){ std::strcpy(user_icon, "1O3W"); // 103W is equal to Default Icon eventlog(eventlog_level_info, __FUNCTION__, "[{}] Set icon packet to DEFAULT ICON [{}]", conn_get_socket(c), user_icon); } else{ std::memcpy(user_icon, &desired_icon, 4); eventlog(eventlog_level_info, __FUNCTION__, "[{}] Set icon packet to ICON [{}]", conn_get_socket(c), user_icon); } // ICON SWITCH HACK PROTECTION if (check_user_icon(account, user_icon) == 0) { std::strcpy(user_icon, "1O3W"); // set icon to default eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" ICON SWITCH hack attempt, icon set to default ", conn_get_username(c), user_icon); //conn_set_state(c,conn_state_destroy); // dont kill user session } account_set_user_icon(account, clienttag, user_icon); //FIXME: Still need a way to 'refresh the user/channel' //_handle_rejoin_command(conn_get_account(c),""); /* ??? channel_update_userflags() */ conn_update_w3_playerinfo(c); channel_rejoin(c); return 0; } /* Check user choice for illegal icon */ // check user for illegal icon // Modified by aancw 16/12/2014 static int check_user_icon(t_account * account, const char * user_icon) { unsigned int i, len; char temp_str[2]; char user_race; int number; len = std::strlen(user_icon); if (len != 4) eventlog(eventlog_level_error, __FUNCTION__, "got invalid user icon '{}'", user_icon); for (i = 0; i < len && i < 2; i++) temp_str[i] = user_icon[i]; number = temp_str[0] - '0'; user_race = temp_str[1]; int race[] = { W3_RACE_RANDOM, W3_RACE_HUMANS, W3_RACE_ORCS, W3_RACE_UNDEAD, W3_RACE_NIGHTELVES, W3_RACE_DEMONS }; char race_char[6] = { 'R', 'H', 'O', 'U', 'N', 'D' }; int icon_pos[5] = { 2, 3, 4, 5, 6 }; int icon_req_wins[5] = { 25, 150, 350, 750, 1500 }; int icon_req_wins_tourney[5] = { 10, 75, 150, 250, 500}; for (int i = 0; i < sizeof(race_char); i++) { if (user_race == race_char[i]) { // Client will got DCed because of different req win normal and tournament // Check if race is tournament or not // Tournament req wins is different than normal icon if(race_char[i] == 'D') { for (int j = 0; j < sizeof(icon_pos); j++) { if (number == icon_pos[j]) { // compare account race wins and require wins for tournament icon if (account_get_racewins( account, race[i], account_get_ll_clienttag(account) ) >= icon_req_wins_tourney[j]) return 1; return 0; } } }else { // When normal icon for (int j = 0; j < sizeof(icon_pos); j++) { if (number == icon_pos[j]) { // compare account race wins and require wins if (account_get_racewins( account, race[i], account_get_ll_clienttag(account) ) >= icon_req_wins[j]) return 1; return 0; } } } } } return 0; } static int _client_anongame_infos(t_connection * c, t_packet const * const packet) { t_packet * rpacket; if (bn_int_get(packet->u.client_findanongame_inforeq.count) > 1) { /* reply with 0 entries found */ int temp = 0; if ((rpacket = packet_create(packet_class_bnet)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new packet"); return -1; } packet_set_size(rpacket, sizeof(t_server_findanongame_inforeply)); packet_set_type(rpacket, SERVER_FINDANONGAME_INFOREPLY); bn_byte_set(&rpacket->u.server_findanongame_inforeply.option, CLIENT_FINDANONGAME_INFOS); bn_int_set(&rpacket->u.server_findanongame_inforeply.count, bn_int_get(packet->u.client_findanongame_inforeq.count)); bn_byte_set(&rpacket->u.server_findanongame_inforeply.noitems, 0); packet_append_data(rpacket, &temp, 1); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } else { int i; int client_tag; int server_tag_count = 0; int client_tag_unk; int server_tag_unk; bn_int temp; char noitems; char * tmpdata; int tmplen; t_clienttag clienttag = conn_get_clienttag(c); char last_packet = 0x00; char other_packet = 0x01; char langstr[5]; t_gamelang gamelang = conn_get_gamelang(c); bn_int_tag_get((bn_int const *)&gamelang, langstr, 5); /* Send seperate packet for each item requested * sending all at once overloaded w3xp * [Omega] */ for (i = 0; i < bn_byte_get(packet->u.client_findanongame_inforeq.noitems); i++){ noitems = 0; if ((rpacket = packet_create(packet_class_bnet)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new packet"); return -1; } /* Starting the packet stuff */ packet_set_size(rpacket, sizeof(t_server_findanongame_inforeply)); packet_set_type(rpacket, SERVER_FINDANONGAME_INFOREPLY); bn_byte_set(&rpacket->u.server_findanongame_inforeply.option, CLIENT_FINDANONGAME_INFOS); bn_int_set(&rpacket->u.server_findanongame_inforeply.count, 1); std::memcpy(&temp, (packet_get_data_const(packet, 10 + (i * 8), 4)), sizeof(int)); client_tag = bn_int_get(temp); std::memcpy(&temp, packet_get_data_const(packet, 14 + (i * 8), 4), sizeof(int)); client_tag_unk = bn_int_get(temp); switch (client_tag){ case CLIENT_FINDANONGAME_INFOTAG_URL: bn_int_set((bn_int*)&server_tag_unk, 0xBF1F1047); packet_append_data(rpacket, "LRU\0", 4); packet_append_data(rpacket, &server_tag_unk, 4); // FIXME: Maybe need do do some checks to avoid prefs empty strings. tmpdata = anongame_infos_data_get_url(clienttag, conn_get_versionid(c), &tmplen); packet_append_data(rpacket, tmpdata, tmplen); noitems++; server_tag_count++; eventlog(eventlog_level_debug, __FUNCTION__, "client_tag request tagid=(0x{:01x}) tag=({}) tag_unk=(0x{:04x})", i, "CLIENT_FINDANONGAME_INFOTAG_URL", client_tag_unk); break; case CLIENT_FINDANONGAME_INFOTAG_MAP: bn_int_set((bn_int*)&server_tag_unk, 0x70E2E0D5); packet_append_data(rpacket, "PAM\0", 4); packet_append_data(rpacket, &server_tag_unk, 4); tmpdata = anongame_infos_data_get_map(clienttag, conn_get_versionid(c), &tmplen); packet_append_data(rpacket, tmpdata, tmplen); noitems++; server_tag_count++; eventlog(eventlog_level_debug, __FUNCTION__, "client_tag request tagid=(0x{:01x}) tag=({}) tag_unk=(0x{:04x})", i, "CLIENT_FINDANONGAME_INFOTAG_MAP", client_tag_unk); break; case CLIENT_FINDANONGAME_INFOTAG_TYPE: bn_int_set((bn_int*)&server_tag_unk, 0x7C87DEEE); packet_append_data(rpacket, "EPYT", 4); packet_append_data(rpacket, &server_tag_unk, 4); tmpdata = anongame_infos_data_get_type(clienttag, conn_get_versionid(c), &tmplen); packet_append_data(rpacket, tmpdata, tmplen); noitems++; server_tag_count++; eventlog(eventlog_level_debug, __FUNCTION__, "client_tag request tagid=(0x{:01x}) tag=({}) tag_unk=(0x{:04x})", i, "CLIENT_FINDANONGAME_INFOTAG_TYPE", client_tag_unk); break; case CLIENT_FINDANONGAME_INFOTAG_DESC: bn_int_set((bn_int*)&server_tag_unk, 0xA4F0A22F); packet_append_data(rpacket, "CSED", 4); packet_append_data(rpacket, &server_tag_unk, 4); tmpdata = anongame_infos_data_get_desc(langstr, clienttag, conn_get_versionid(c), &tmplen); packet_append_data(rpacket, tmpdata, tmplen); eventlog(eventlog_level_debug, __FUNCTION__, "client_tag request tagid=(0x{:01x}) tag=({}) tag_unk=(0x{:04x})", i, "CLIENT_FINDANONGAME_INFOTAG_DESC", client_tag_unk); noitems++; server_tag_count++; break; case CLIENT_FINDANONGAME_INFOTAG_LADR: bn_int_set((bn_int*)&server_tag_unk, 0x3BADE25A); packet_append_data(rpacket, "RDAL", 4); packet_append_data(rpacket, &server_tag_unk, 4); tmpdata = anongame_infos_data_get_ladr(langstr, clienttag, conn_get_versionid(c), &tmplen); packet_append_data(rpacket, tmpdata, tmplen); noitems++; server_tag_count++; eventlog(eventlog_level_debug, __FUNCTION__, "client_tag request tagid=(0x{:01x}) tag=({}) tag_unk=(0x{:04x})", i, "CLIENT_FINDANONGAME_INFOTAG_LADR", client_tag_unk); break; default: eventlog(eventlog_level_debug, __FUNCTION__, "unrec client_tag request tagid=(0x{:01x}) tag=(0x{:04x})", i, client_tag); } //Adding a last padding null-byte if (server_tag_count == bn_byte_get(packet->u.client_findanongame_inforeq.noitems)) packet_append_data(rpacket, &last_packet, 1); /* only last packet in group gets 0x00 */ else packet_append_data(rpacket, &other_packet, 1); /* the rest get 0x01 */ //Go,go,go bn_byte_set(&rpacket->u.server_findanongame_inforeply.noitems, noitems); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } /* tournament notice disabled at this time, but responce is sent to cleint */ static int _client_anongame_tournament(t_connection * c, t_packet const * const packet) { t_packet * rpacket; t_account * account = conn_get_account(c); t_clienttag clienttag = conn_get_clienttag(c); unsigned int start_prelim = tournament_get_start_preliminary(); unsigned int end_signup = tournament_get_end_signup(); unsigned int end_prelim = tournament_get_end_preliminary(); unsigned int start_r1 = tournament_get_start_round_1(); if ((rpacket = packet_create(packet_class_bnet)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new packet"); return -1; } packet_set_size(rpacket, sizeof(t_server_anongame_tournament_reply)); packet_set_type(rpacket, SERVER_FINDANONGAME_TOURNAMENT_REPLY); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.option, 7); bn_int_set(&rpacket->u.server_anongame_tournament_reply.count, bn_int_get(packet->u.client_anongame_tournament_request.count)); if (!start_prelim || (end_signup <= now && tournament_user_signed_up(account) < 0) || tournament_check_client(clienttag) < 0) { /* No Tournament Notice */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0); bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } else if (start_prelim >= now) { /* Tournament Notice */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 1); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0x0000); /* random */ bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, _tournament_time_convert(start_prelim)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0x01); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, start_prelim - now); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0x00); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } else if (end_signup >= now) { /* Tournament Signup Notice - Play Game Active */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0x0828); /* random */ bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, _tournament_time_convert(end_signup)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0x01); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, end_signup - now); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, tournament_get_stat(account, 1)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, tournament_get_stat(account, 2)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, tournament_get_stat(account, 3)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0x08); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } else if (end_prelim >= now) { /* Tournament Prelim Period - Play Game Active */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 3); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0x0828); /* random */ bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, _tournament_time_convert(end_prelim)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0x01); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, end_prelim - now); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, tournament_get_stat(account, 1)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, tournament_get_stat(account, 2)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, tournament_get_stat(account, 3)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0x08); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } else if (start_r1 >= now && (tournament_get_game_in_progress())) { /* Prelim Period Over - Shows user stats (not all prelim games finished) */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 4); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0x0000); /* random */ bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, _tournament_time_convert(start_r1)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0x01); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, start_r1 - now); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0); /* 00 00 */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, tournament_get_stat(account, 1)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, tournament_get_stat(account, 2)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, tournament_get_stat(account, 3)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0x08); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } else if (!(tournament_get_in_finals_status(account))) { /* Prelim Period Over - user did not make finals - Shows user stats */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 5); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0); bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, tournament_get_stat(account, 1)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, tournament_get_stat(account, 2)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, tournament_get_stat(account, 3)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0x04); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } /* cycle through [type-6] & [type-7] packets * * use [type-6] to show client "eliminated" or "continue" * timestamp , countdown & round number (of next round) must be set if clinet continues * * use [type-7] to make cleint wait for 44FF packet option 1 to start game (A guess, not tested) * * not sure if there is overall winner packet sent at end of last final round */ // UNDONE: next two conditions never executed else if ((0)) { /* User in finals - Shows user stats and start of next round*/ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 6); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0x0000); bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, _tournament_time_convert(start_r1)); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0x01); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, start_r1 - now); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0x0000); /* 00 00 */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, 4); /* round number */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, 0); /* 0 = continue , 1= eliminated */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0x04); /* number of rounds in finals */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } else if ((0)) { /* user waiting for match to be made */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.type, 7); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown4, 0); bn_int_set(&rpacket->u.server_anongame_tournament_reply.timestamp, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown5, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.countdown, 0); bn_short_set(&rpacket->u.server_anongame_tournament_reply.unknown2, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.wins, 1); /* round number */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.losses, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.ties, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.unknown3, 0x04); /* number of finals */ bn_byte_set(&rpacket->u.server_anongame_tournament_reply.selection, 2); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.descnum, 0); bn_byte_set(&rpacket->u.server_anongame_tournament_reply.nulltag, 0); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static unsigned int _tournament_time_convert(unsigned int time) { /* it works, don't ask me how */ /* some time drift reportd by testers */ unsigned int tmp1, tmp2, tmp3; tmp1 = time - 1059179400; /* 0x3F21CB88 */ tmp2 = static_cast(tmp1*0.59604645); tmp3 = tmp2 + 3276999960U; /*eventlog(eventlog_level_trace,__FUNCTION__,"time: 0x%08x, tmp1: 0x%08x, tmp2 0x%08x, tmp3 0x%08x",time,tmp1,tmp2,tmp3);*/ return tmp3; } extern int handle_anongame_packet(t_connection * c, t_packet const * const packet) { switch (bn_byte_get(packet->u.client_anongame.option)) { case CLIENT_FINDANONGAME_PROFILE: return _client_anongame_profile(c, packet); case CLIENT_FINDANONGAME_CANCEL: return _client_anongame_cancel(c); case CLIENT_FINDANONGAME_SEARCH: case CLIENT_FINDANONGAME_AT_INVITER_SEARCH: case CLIENT_FINDANONGAME_AT_SEARCH: return handle_anongame_search(c, packet); /* located in anongame.c */ case CLIENT_FINDANONGAME_GET_ICON: return _client_anongame_get_icon(c, packet); case CLIENT_FINDANONGAME_SET_ICON: return _client_anongame_set_icon(c, packet); case CLIENT_FINDANONGAME_INFOS: return _client_anongame_infos(c, packet); case CLIENT_ANONGAME_TOURNAMENT: return _client_anongame_tournament(c, packet); case CLIENT_FINDANONGAME_PROFILE_CLAN: return _client_anongame_profile_clan(c, packet); default: eventlog(eventlog_level_error, __FUNCTION__, "got unhandled option {}", bn_byte_get(packet->u.client_findanongame.option)); return -1; } } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_anongame.h000066400000000000000000000017441462640461400213420ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_ANONGAME_PROTOS #define INCLUDED_HANDLE_ANONGAME_PROTOS #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace bnetd { extern int handle_anongame_packet(t_connection * c, t_packet const * const packet); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_apireg.cpp000066400000000000000000000604711462640461400213610ustar00rootroot00000000000000/* * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define APIREGISTER_INTERNAL_ACCESS #include "common/setup_before.h" #include "handle_apireg.h" #include #include #include #include "common/eventlog.h" #include "common/bnethash.h" #include "common/wolhash.h" #include "common/list.h" #include "common/packet.h" #include "compat/strcasecmp.h" #include "prefs.h" #include "irc.h" #include "account.h" #include "account_wrap.h" #include "message.h" #include "server.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef int(*t_apireg_tag)(t_apiregmember * apiregmember, char * param); static t_list * apireglist_head = NULL; typedef struct { const char * apireg_tag_string; t_apireg_tag apireg_tag_handler; } t_apireg_tag_table_row; static int _handle_email_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_bmonth_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_bday_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_byear_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_langcode_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_sku_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_ver_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_serial_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_sysid_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_syscheck_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_oldnick_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_oldpass_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_newnick_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_newpass_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_newpass2_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_parentemail_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_newsletter_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_shareinfo_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_request_apiregtag(t_apiregmember * apiregmember, char * param); static int _handle_end_apiregtag(t_apiregmember * apiregmember, char * param); static const t_apireg_tag_table_row apireg_tag_table[] = { { "EMAIL", _handle_email_apiregtag }, { "BMONTH", _handle_bmonth_apiregtag }, { "BDAY", _handle_bday_apiregtag }, { "BYEAR", _handle_byear_apiregtag }, { "LANGCODE", _handle_langcode_apiregtag }, { "SKU", _handle_sku_apiregtag }, { "VER", _handle_ver_apiregtag }, { "SERIAL", _handle_serial_apiregtag }, { "SYSID", _handle_sysid_apiregtag }, { "SYSCHECK", _handle_syscheck_apiregtag }, { "OLDNICK", _handle_oldnick_apiregtag }, { "OLDPASS", _handle_oldpass_apiregtag }, { "NEWNICK", _handle_newnick_apiregtag }, { "NEWPASS", _handle_newpass_apiregtag }, { "NEWPASS2", _handle_newpass2_apiregtag }, { "PARENTEMAIL", _handle_parentemail_apiregtag }, { "NEWSLETTER", _handle_newsletter_apiregtag }, { "SHAREINFO", _handle_shareinfo_apiregtag }, { "REQUEST", _handle_request_apiregtag }, { "END", _handle_end_apiregtag }, { NULL, NULL } }; static t_apiregmember * apiregmember_create(t_connection * conn) { t_apiregmember * temp; if (!conn) { ERROR0("got NULL conn"); return NULL; } temp = (t_apiregmember*)xmalloc(sizeof(t_apiregmember)); eventlog(eventlog_level_info, __FUNCTION__, "creating apiregmember"); temp->conn = conn; temp->email = NULL; temp->bday = NULL; temp->bmonth = NULL; temp->byear = NULL; temp->langcode = NULL; //"0"; temp->sku = NULL; temp->ver = NULL; temp->serial = NULL; temp->sysid = NULL; //"((SysID))"; temp->syscheck = NULL; //"((SysCheck))"; temp->oldnick = NULL; temp->oldpass = NULL; temp->newnick = NULL; //"((NewPass))"; temp->newpass = NULL; //"((NewPass))"; temp->newpass2 = NULL; //"((NewPass2))"; temp->parentemail = NULL; //"((ParentEmail))"; temp->newsletter = false; temp->shareinfo = false; temp->request = NULL; //"((Request))"; list_append_data(apireglist_head, temp); return temp; } static int apiregmember_destroy(t_apiregmember * apiregmember, t_elem ** curr) { eventlog(eventlog_level_info, __FUNCTION__, "destroying apiregmember"); if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (list_remove_data(apireglist_head, apiregmember, curr) < 0){ ERROR0("could not remove item from list"); return -1; } if (apiregmember->email) xfree((void *)apiregmember->email); /* avoid warning */ if (apiregmember->bday) xfree((void *)apiregmember->bday); /* avoid warning */ if (apiregmember->bmonth) xfree((void *)apiregmember->bmonth); /* avoid warning */ if (apiregmember->byear) xfree((void *)apiregmember->byear); /* avoid warning */ if (apiregmember->langcode) xfree((void *)apiregmember->langcode); /* avoid warning */ if (apiregmember->sku) xfree((void *)apiregmember->sku); /* avoid warning */ if (apiregmember->ver) xfree((void *)apiregmember->ver); /* avoid warning */ if (apiregmember->serial) xfree((void *)apiregmember->serial); /* avoid warning */ if (apiregmember->sysid) xfree((void *)apiregmember->sysid); /* avoid warning */ if (apiregmember->syscheck) xfree((void *)apiregmember->syscheck); /* avoid warning */ if (apiregmember->oldnick) xfree((void *)apiregmember->oldnick); /* avoid warning */ if (apiregmember->oldpass) xfree((void *)apiregmember->oldpass); /* avoid warning */ if (apiregmember->newnick) xfree((void *)apiregmember->newnick); /* avoid warning */ if (apiregmember->newpass) xfree((void *)apiregmember->newpass); /* avoid warning */ if (apiregmember->newpass2) xfree((void *)apiregmember->newpass2); /* avoid warning */ if (apiregmember->parentemail) xfree((void *)apiregmember->parentemail); /* avoid warning */ // if (apiregmember->newsletter) // xfree((void *)apiregmember->newsletter); /* avoid warning */ // if (apiregmember->shareinfo) // xfree((void *)apiregmember->shareinfo); /* avoid warning */ if (apiregmember->request) xfree((void *)apiregmember->request); /* avoid warning */ xfree(apiregmember); return 0; } static t_connection * apiregmember_get_conn(t_apiregmember const * apiregmember) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return NULL; } return apiregmember->conn; } static char const * apiregmember_get_email(t_apiregmember const * apiregmember) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return NULL; } return apiregmember->email; } static char const * apiregmember_get_bday(t_apiregmember const * apiregmember) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return NULL; } return apiregmember->bday; } static char const * apiregmember_get_bmonth(t_apiregmember const * apiregmember) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return NULL; } return apiregmember->bmonth; } static char const * apiregmember_get_newnick(t_apiregmember const * apiregmember) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return NULL; } return apiregmember->newnick; } static char const * apiregmember_get_newpass(t_apiregmember const * apiregmember) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return NULL; } return apiregmember->newpass; } static char const * apiregmember_get_request(t_apiregmember const * apiregmember) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return NULL; } return apiregmember->request; } extern int apireglist_create(void) { apireglist_head = list_create(); return 0; } extern int apireglist_destroy(void) { t_apiregmember * apiregmember; t_elem * curr; if (apireglist_head) { LIST_TRAVERSE(apireglist_head, curr) { if (!(apiregmember = (t_apiregmember*)elem_get_data(curr))) { ERROR0("channel list contains NULL item"); continue; } apiregmember_destroy(apiregmember, &curr); } if (list_destroy(apireglist_head) < 0) return -1; apireglist_head = NULL; } return 0; } extern t_list * apireglist(void) { return apireglist_head; } static t_apiregmember * apireglist_find_apiregmember_by_conn(t_connection * conn) { t_elem * curr; if (!conn) { ERROR0("got NULL conn"); return NULL; } LIST_TRAVERSE(apireglist(), curr) { t_apiregmember * apiregmember = (t_apiregmember *)elem_get_data(curr); if (conn == apiregmember_get_conn(apiregmember)) { return apiregmember; } } return NULL; } static int handle_apireg_tag(t_apiregmember * apiregmember, char const * tag, char * param) { t_apireg_tag_table_row const *p; for (p = apireg_tag_table; p->apireg_tag_string != NULL; p++) { if (strcasecmp(tag, p->apireg_tag_string) == 0) { if (p->apireg_tag_handler != NULL) return ((p->apireg_tag_handler)(apiregmember, param)); } } return -1; } static int handle_apireg_line(t_connection * conn, char const * apiregline) { /* =[param] */ char * line; /* copy of apiregline */ char * tag = NULL; /* mandatory */ char * param = NULL; /* param of tag */ t_apiregmember * apiregmember = apireglist_find_apiregmember_by_conn(conn); if (!conn) { ERROR0("got NULL connection"); return -1; } if (!apiregline) { ERROR0("got NULL apiregline"); return -1; } if (apiregline[0] == '\0') { ERROR0("got empty apiregline"); return -1; } if (std::strlen(apiregline) > 254) { char * tmp = (char *)apiregline; WARN0("line to long, truncation..."); tmp[254] = '\0'; } if (!apiregmember) { apiregmember = apiregmember_create(conn); } line = xstrdup(apiregline); /* split the line */ tag = line; param = std::strchr(tag, '='); if (param) *param++ = '\0'; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got \"{}\" [{}]", conn_get_socket(conn), tag, ((param) ? (param) : (""))); if (handle_apireg_tag(apiregmember, tag, param) != -1) {} xfree(line); return 0; } extern int handle_apireg_packet(t_connection * conn, t_packet const * const packet) { unsigned int i; char apiregline[MAX_IRC_MESSAGE_LEN]; char const * data; if (!packet) { ERROR0("got NULL packet"); return -1; } if (conn_get_class(conn) != conn_class_apireg) { ERROR0("FIXME: handle_apireg_packet without any reason (conn->class != conn_class_apireg)"); return -1; } /* eventlog(eventlog_level_debug,__FUNCTION__,"got \"%s\"",packet_get_raw_data_const(packet,0)); */ std::memset(apiregline, 0, sizeof(apiregline)); data = conn_get_ircline(conn); /* fetch current status */ if (data) { std::snprintf(apiregline, sizeof apiregline, "%s", data); } unsigned apiregpos = std::strlen(apiregline); data = (const char *)packet_get_raw_data_const(packet, 0); for (i = 0; i < packet_get_size(packet); i++) { if ((data[i] == '\r') || (data[i] == '\0')) { /* kindly ignore \r and NUL ... */ } else if (data[i] == '\n') { /* end of line */ handle_apireg_line(conn, apiregline); std::memset(apiregline, 0, sizeof(apiregline)); apiregpos = 0; } else { if (apiregpos < MAX_IRC_MESSAGE_LEN - 1) apiregline[apiregpos++] = data[i]; else { apiregpos++; /* for the statistic :) */ WARN2("[{}] client exceeded maximum allowed message length by {} characters", conn_get_socket(conn), apiregpos - MAX_IRC_MESSAGE_LEN); if (apiregpos > 100 + MAX_IRC_MESSAGE_LEN) { /* automatic flood protection */ ERROR1("[{}] excess flood", conn_get_socket(conn)); return -1; } } } } conn_set_ircline(conn, apiregline); /* write back current status */ return 0; } static int apireg_send(t_connection * conn, char const * command) { char data[MAX_IRC_MESSAGE_LEN + 1]; unsigned len = 0; t_elem * curr; if (command) len = (std::strlen(command)); if (len > MAX_IRC_MESSAGE_LEN) { ERROR1("message to send is too large ({} bytes)", len); return -1; } else { std::sprintf(data, "%s", command); } { t_packet* const p = packet_create(packet_class_raw); packet_set_size(p, 0); packet_append_data(p, data, len); DEBUG2("[{}] sent \"{}\"", conn_get_socket(conn), data); conn_push_outqueue(conn, p); packet_del_ref(p); } /* In apiregister server we must destroy apiregmember and connection after send packet */ LIST_TRAVERSE(apireglist(), curr) { t_apiregmember * tempapireg = (t_apiregmember*)elem_get_data(curr); if (conn == apiregmember_get_conn(tempapireg)) apiregmember_destroy(tempapireg, &curr); } conn_set_state(conn, conn_state_destroy); return 0; } static int _handle_email_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->email) xfree((void *)apiregmember->email); /* avoid warning */ if (param) apiregmember->email = xstrdup(param); return 0; } static int _handle_bmonth_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->bmonth) xfree((void *)apiregmember->bmonth); /* avoid warning */ if (param) apiregmember->bmonth = xstrdup(param); return 0; } static int _handle_bday_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->bday) xfree((void *)apiregmember->bday); /* avoid warning */ if (param) apiregmember->bday = xstrdup(param); return 0; } static int _handle_byear_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->byear) xfree((void *)apiregmember->byear); /* avoid warning */ if (param) apiregmember->byear = xstrdup(param); return 0; } static int _handle_langcode_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->langcode) xfree((void *)apiregmember->langcode); /* avoid warning */ if (param) apiregmember->langcode = xstrdup(param); return 0; } static int _handle_sku_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } /* We have SKUs */ return 0; } static int _handle_ver_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } /* We have VERs */ return 0; } static int _handle_serial_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } /* We have SERIALs */ return 0; } static int _handle_sysid_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->sysid) xfree((void *)apiregmember->sysid); /* avoid warning */ if (param) apiregmember->sysid = xstrdup(param); return 0; } static int _handle_syscheck_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->syscheck) xfree((void *)apiregmember->syscheck); /* avoid warning */ if (param) apiregmember->syscheck = xstrdup(param); return 0; } static int _handle_oldnick_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } /* We have OLDNICKs */ return 0; } static int _handle_oldpass_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } /* We have OLDPASSs */ return 0; } static int _handle_newnick_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->newnick) xfree((void *)apiregmember->newnick); if (param) apiregmember->newnick = xstrdup(param); return 0; } static int _handle_newpass_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->newpass) xfree((void *)apiregmember->newpass); /* avoid warning */ if (param) apiregmember->newpass = xstrdup(param); return 0; } static int _handle_newpass2_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->newpass2) xfree((void *)apiregmember->newpass2); /* avoid warning */ if (param) apiregmember->newpass2 = xstrdup(param); return 0; } static int _handle_parentemail_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->parentemail) xfree((void *)apiregmember->parentemail); /* avoid warning */ if (param) apiregmember->parentemail = xstrdup(param); return 0; } static int _handle_newsletter_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } /* if (param) { if (std::strcmp(param, "1") == 0) apiregmember->newsletter = true; else apiregmember->newsletter = false; }*/ return 0; } static int _handle_shareinfo_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } /* if (param) { if (std::strcmp(param, "1") == 0) apiregmember->shareinfo = true; else apiregmember->shareinfo = false; }*/ return 0; } static int _handle_request_apiregtag(t_apiregmember * apiregmember, char * param) { if (!apiregmember) { ERROR0("got NULL apiregmember"); return -1; } if (apiregmember->request) xfree((void *)apiregmember->request); /* avoid warning */ if (param) apiregmember->request = xstrdup(param); return 0; } /*static char _handle_apireg_message_format(char const * hresult, char const * message, char const * newnick, char const * newpass, char const * age, char const * consent) { }*/ static int _handle_end_apiregtag(t_apiregmember * apiregmember, char * param) { char data[MAX_IRC_MESSAGE_LEN]; char temp[MAX_IRC_MESSAGE_LEN]; t_connection * conn = apiregmember_get_conn(apiregmember); t_elem * curr; t_account * account; char const * newnick = apiregmember_get_newnick(apiregmember); char const * newpass = apiregmember_get_newpass(apiregmember); char const * email = apiregmember_get_email(apiregmember); char const * request = apiregmember_get_request(apiregmember); // char const * newpass2 = apiregmember_get_newpass2(apiregmember); char hresult[12]; char message[MAX_IRC_MESSAGE_LEN]; char age[8]; char consent[12]; std::memset(data, 0, sizeof(data)); std::memset(temp, 0, sizeof(temp)); std::memset(hresult, 0, sizeof(hresult)); std::memset(message, 0, sizeof(message)); std::memset(age, 0, sizeof(age)); std::memset(consent, 0, sizeof(consent)); std::snprintf(hresult, sizeof(hresult), "0"); std::snprintf(message, sizeof(message), "((Message))"); std::snprintf(age, sizeof(age), "((Age))"); std::snprintf(consent, sizeof(consent), "((Consent))"); // if (!newnick) // snprintf(newnick,sizeof(newnick),"((NewNick))"); // if (!newpass) // snprintf(newpass,sizeof(newpass),"((NewPass))"); // if (!email) // snprintf(email,sizeof(email),"((Email))"); DEBUG3("APIREG:/{}/{}/{}/", apiregmember_get_request(apiregmember), apiregmember_get_newnick(apiregmember), apiregmember_get_newpass(apiregmember)); if ((request) && (std::strcmp(apiregmember_get_request(apiregmember), REQUEST_AGEVERIFY) == 0)) { std::snprintf(data, sizeof(data), "HRESULT=%s\nMessage=%s\nNewNick=((NewNick))\nNewPass=((NewPass))\n", hresult, message); /* FIXME: Count real age here! */ std::snprintf(age, sizeof(age), "28"); /* FIXME: Here must be counted age */ std::snprintf(temp, sizeof(temp), "Age=%s\nConsent=((Consent))\nEND\r", age); std::strcat(data, temp); apireg_send(apiregmember_get_conn(apiregmember), data); return 0; } else if ((request) && (std::strcmp(apiregmember_get_request(apiregmember), REQUEST_GETNICK) == 0)) { if (!prefs_get_allow_new_accounts()){ std::snprintf(message, sizeof(message), "Account creation is not allowed"); std::snprintf(hresult, sizeof(hresult), "-2147221248"); } else { if (!newnick) { std::snprintf(message, sizeof(message), "Nick must be specifed!"); std::snprintf(hresult, sizeof(hresult), "-2147221248"); } else if (!newpass) { std::snprintf(message, sizeof(message), "Pussword must be specifed!"); std::snprintf(hresult, sizeof(hresult), "-2147221248"); } else if (account = accountlist_find_account(newnick)) { std::snprintf(message, sizeof(message), "That login is already in use! Please try another NICK name."); std::snprintf(hresult, sizeof(hresult), "-2147221248"); } else { /* done, we can create new account */ t_account * tempacct; t_hash bnet_pass_hash; t_wolhash wol_pass_hash; /* Here we can also check serials and/or emails... */ bnet_hash(&bnet_pass_hash, std::strlen(newpass), newpass); wol_hash(&wol_pass_hash, std::strlen(newpass), newpass); tempacct = accountlist_create_account(newnick, hash_get_str(bnet_pass_hash)); if (!tempacct) { // ERROR: Account is not created! - Why? :) return 0; } else { eventlog(eventlog_level_debug, __FUNCTION__, "WOLHASH: {}", wol_pass_hash); account_set_wol_apgar(tempacct, wol_pass_hash); if (apiregmember_get_email(apiregmember)) account_set_email(tempacct, apiregmember_get_email(apiregmember)); std::snprintf(message, sizeof(message), "Welcome in the amazing world of PvPGN! Your login can be used for all PvPGN Supported games!"); std::snprintf(hresult, sizeof(hresult), "0"); } } } std::snprintf(data, sizeof(data), "HRESULT=%s\nMessage=%s\nNewNick=%s\nNewPass=%s\nAge=%s\nConsent=%s\nEND\r", hresult, message, newnick, newpass, age, consent); apireg_send(apiregmember_get_conn(apiregmember), data); return 0; } else { /* Error: Unknown request - closing connection */ ERROR1("got UNKNOWN request /{}/ closing connection", apiregmember->request); LIST_TRAVERSE(apireglist(), curr) { t_apiregmember * apiregmemberlist = (t_apiregmember*)elem_get_data(curr); if (conn == apiregmember_get_conn(apiregmemberlist)) { apiregmember_destroy(apiregmember, &curr); break; } } conn_set_state(conn, conn_state_destroy); return 0; } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_apireg.h000066400000000000000000000046031462640461400210210ustar00rootroot00000000000000/* * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_APIREGISTER_TYPES #define INCLUDED_APIREGISTER_TYPES #include "common/packet.h" #include "connection.h" #endif namespace pvpgn { namespace bnetd { typedef struct apiregmember #ifdef APIREGISTER_INTERNAL_ACCESS { t_connection * conn; char const * email; char const * bday; char const * bmonth; char const * byear; char const * langcode; char const * sku; /* here are SKUs of all installed games */ char const * ver; /* same as with SKU - versions of all installed games */ char const * serial; /* also serials of all installed games */ char const * sysid; char const * syscheck; char const * oldnick; /* client send also all nicks that was registerd in the past */ char const * oldpass; /* and passwords for oldnicks */ char const * newnick; char const * newpass; char const * newpass2; char const * parentemail; bool newsletter; /* do user want to sending news by e-mail? */ bool shareinfo; /* can EA/Westwood shared e-mail contact for sending news? :) */ char const * request; /* API Register request (knowed requests are defined below) */ } #endif t_apiregmember; #ifndef INCLUDED_HANDLE_APIREG_PROTOS #define INCLUDED_HANDLE_APIREG_PROTOS #define REQUEST_AGEVERIFY "apireg_ageverify" #define REQUEST_GETNICK "apireg_getnick" extern int apireglist_create(void); extern int apireglist_destroy(void); extern int handle_apireg_packet(t_connection * conn, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_bnet.cpp000066400000000000000000007014441462640461400210440ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999,2000 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_bnet.h" #include #include #include #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "compat/strncasecmp.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/tag.h" #include "common/bn_type.h" #include "common/addr.h" #include "common/bnettime.h" #include "common/trans.h" #include "common/list.h" #include "common/bnethash.h" #include "common/bnethashconv.h" #include "common/proginfo.h" #include "common/util.h" #include "common/bnetsrp3.h" #include "common/xstring.h" #include "handlers.h" #include "connection.h" #include "prefs.h" #include "versioncheck.h" #include "handle_anongame.h" #include "account.h" #include "account_wrap.h" #include "clan.h" #include "channel.h" #include "game.h" #include "game_conv.h" #include "ladder.h" #include "watch.h" #include "realm.h" #include "adbanner.h" #include "character.h" #include "command.h" #include "tick.h" #include "message.h" #include "file.h" #include "news.h" #include "team.h" #include "server.h" #include "friends.h" #include "autoupdate.h" #include "anongame.h" #include "i18n.h" #ifdef WIN32_GUI #include #endif #include "common/setup_after.h" #ifdef WITH_LUA #include "luainterface.h" #endif namespace pvpgn { namespace bnetd { extern int last_news; extern int first_news; /* handlers prototypes */ static int _client_unknown_1b(t_connection * c, t_packet const *const packet); static int _client_compinfo1(t_connection * c, t_packet const *const packet); static int _client_compinfo2(t_connection * c, t_packet const *const packet); static int _client_countryinfo1(t_connection * c, t_packet const *const packet); static int _client_auth_info(t_connection * c, t_packet const *const packet); static int _client_unknown2b(t_connection * c, t_packet const *const packet); static int _client_progident(t_connection * c, t_packet const *const packet); static int _client_createaccountw3(t_connection * c, t_packet const *const packet); static int _client_createacctreq1(t_connection * c, t_packet const *const packet); static int _client_createacctreq2(t_connection * c, t_packet const *const packet); static int _client_changepassreq(t_connection * c, t_packet const *const packet); static int _client_echoreply(t_connection * c, t_packet const *const packet); static int _client_authreq1(t_connection * c, t_packet const *const packet); static int _client_authreq109(t_connection * c, t_packet const *const packet); static int _client_regsnoopreply(t_connection * c, t_packet const *const packet); static int _client_iconreq(t_connection * c, t_packet const *const packet); static int _client_cdkey(t_connection * c, t_packet const *const packet); static int _client_cdkey2(t_connection * c, t_packet const *const packet); static int _client_cdkey3(t_connection * c, t_packet const *const packet); static int _client_udpok(t_connection * c, t_packet const *const packet); static int _client_fileinforeq(t_connection * c, t_packet const *const packet); static int _client_statsreq(t_connection * c, t_packet const *const packet); static int _client_loginreq1(t_connection * c, t_packet const *const packet); static int _client_loginreq2(t_connection * c, t_packet const *const packet); static int _client_loginreqw3(t_connection * c, t_packet const *const packet); static int _client_passchangereq(t_connection * c, t_packet const *const packet); static int _client_passchangeproofreq(t_connection * c, t_packet const *const packet); static int _client_pingreq(t_connection * c, t_packet const *const packet); static int _client_logonproofreq(t_connection * c, t_packet const *const packet); static int _client_changegameport(t_connection * c, t_packet const *const packet); static int _client_friendslistreq(t_connection * c, t_packet const *const packet); static int _client_friendinforeq(t_connection * c, t_packet const *const packet); static int _client_atfriendscreen(t_connection * c, t_packet const *const packet); static int _client_atinvitefriend(t_connection * c, t_packet const *const packet); static int _client_atacceptinvite(t_connection * c, t_packet const *const packet); static int _client_atacceptdeclineinvite(t_connection * c, t_packet const *const packet); static int _client_motdw3(t_connection * c, t_packet const *const packet); static int _client_realmlistreq(t_connection * c, t_packet const *const packet); static int _client_realmlistreq110(t_connection * c, t_packet const *const packet); static int _client_profilereq(t_connection * c, t_packet const *const packet); static int _client_realmjoinreq109(t_connection * c, t_packet const *const packet); static int _client_unknown39(t_connection * c, t_packet const *const packet); static int _client_charlistreq(t_connection * c, t_packet const *const packet); static int _client_adreq(t_connection * c, t_packet const *const packet); static int _client_adack(t_connection * c, t_packet const *const packet); static int _client_adclick(t_connection * c, t_packet const *const packet); static int _client_adclick2(t_connection * c, t_packet const *const packet); static int _client_readmemory(t_connection * c, t_packet const *const packet); static int _client_statsupdate(t_connection * c, t_packet const *const packet); static int _client_playerinforeq(t_connection * c, t_packet const *const packet); static int _client_progident2(t_connection * c, t_packet const *const packet); static int _client_joinchannel(t_connection * c, t_packet const *const packet); static int _client_message(t_connection * c, t_packet const *const packet); static int _client_gamelistreq(t_connection * c, t_packet const *const packet); static int _client_joingame(t_connection * c, t_packet const *const packet); static int _client_startgame1(t_connection * c, t_packet const *const packet); static int _client_startgame3(t_connection * c, t_packet const *const packet); static int _client_startgame4(t_connection * c, t_packet const *const packet); static int _client_closegame(t_connection * c, t_packet const *const packet); static int _client_gamereport(t_connection * c, t_packet const *const packet); static int _client_leavechannel(t_connection * c, t_packet const *const packet); static int _client_ladderreq(t_connection * c, t_packet const *const packet); static int _client_laddersearchreq(t_connection * c, t_packet const *const packet); static int _client_mapauthreq1(t_connection * c, t_packet const *const packet); static int _client_mapauthreq2(t_connection * c, t_packet const *const packet); static int _client_changeclient(t_connection * c, t_packet const *const packet); static int _client_clanmemberlistreq(t_connection * c, t_packet const *const packet); static int _client_clan_motdreq(t_connection * c, t_packet const *const packet); static int _client_clan_motdchg(t_connection * c, t_packet const *const packet); static int _client_clan_createreq(t_connection * c, t_packet const *const packet); static int _client_clan_createinvitereq(t_connection * c, t_packet const *const packet); static int _client_clan_createinvitereply(t_connection * c, t_packet const *const packet); static int _client_clan_disbandreq(t_connection * c, t_packet const *const packet); static int _client_clanmember_rankupdatereq(t_connection * c, t_packet const *const packet); static int _client_clanmember_removereq(t_connection * c, t_packet const *const packet); static int _client_clan_membernewchiefreq(t_connection * c, t_packet const *const packet); static int _client_clan_invitereq(t_connection * c, t_packet const *const packet); static int _client_clan_invitereply(t_connection * c, t_packet const *const packet); static int _client_crashdump(t_connection * c, t_packet const *const packet); static int _client_setemailreply(t_connection * c, t_packet const *const packet); static int _client_changeemailreq(t_connection * c, t_packet const *const packet); static int _client_getpasswordreq(t_connection * c, t_packet const *const packet); static int _client_claninforeq(t_connection * c, t_packet const *const packet); static int _client_extrawork(t_connection * c, t_packet const *const packet); /* connection state connected handler table */ static const t_htable_row bnet_htable_con[] = { { CLIENT_UNKNOWN_1B, _client_unknown_1b }, { CLIENT_COMPINFO1, _client_compinfo1 }, { CLIENT_COMPINFO2, _client_compinfo2 }, { CLIENT_COUNTRYINFO1, _client_countryinfo1 }, { CLIENT_AUTH_INFO, _client_auth_info }, { CLIENT_UNKNOWN_2B, _client_unknown2b }, { CLIENT_PROGIDENT, _client_progident }, { CLIENT_CLOSEGAME, NULL }, { CLIENT_CREATEACCOUNT_W3, _client_createaccountw3 }, { CLIENT_CREATEACCTREQ1, _client_createacctreq1 }, { CLIENT_CREATEACCTREQ2, _client_createacctreq2 }, { CLIENT_CHANGEPASSREQ, _client_changepassreq }, { CLIENT_ECHOREPLY, _client_echoreply }, { CLIENT_AUTHREQ1, _client_authreq1 }, { CLIENT_AUTHREQ_109, _client_authreq109 }, { CLIENT_REGSNOOPREPLY, _client_regsnoopreply }, { CLIENT_ICONREQ, _client_iconreq }, { CLIENT_CDKEY, _client_cdkey }, { CLIENT_CDKEY2, _client_cdkey2 }, { CLIENT_CDKEY3, _client_cdkey3 }, { CLIENT_UDPOK, _client_udpok }, { CLIENT_FILEINFOREQ, _client_fileinforeq }, { CLIENT_STATSREQ, _client_statsreq }, { CLIENT_PINGREQ, _client_pingreq }, { CLIENT_LOGINREQ1, _client_loginreq1 }, { CLIENT_LOGINREQ2, _client_loginreq2 }, { CLIENT_LOGINREQ_W3, _client_loginreqw3 }, { CLIENT_PASSCHANGEREQ, _client_passchangereq }, { CLIENT_PASSCHANGEPROOFREQ, _client_passchangeproofreq }, { CLIENT_LOGONPROOFREQ, _client_logonproofreq }, { CLIENT_CHANGECLIENT, _client_changeclient }, { CLIENT_GETPASSWORDREQ, _client_getpasswordreq }, { CLIENT_CHANGEEMAILREQ, _client_changeemailreq }, { CLIENT_CRASHDUMP, _client_crashdump }, { -1, NULL } }; /* connection state loggedin handlers */ static const t_htable_row bnet_htable_log[] = { { CLIENT_CHANGEGAMEPORT, _client_changegameport }, { CLIENT_FRIENDSLISTREQ, _client_friendslistreq }, { CLIENT_FRIENDINFOREQ, _client_friendinforeq }, { CLIENT_ARRANGEDTEAM_FRIENDSCREEN, _client_atfriendscreen }, { CLIENT_ARRANGEDTEAM_INVITE_FRIEND, _client_atinvitefriend }, { CLIENT_ARRANGEDTEAM_ACCEPT_INVITE, _client_atacceptinvite }, { CLIENT_ARRANGEDTEAM_ACCEPT_DECLINE_INVITE, _client_atacceptdeclineinvite }, /* anongame packet (44ff) handled in handle_anongame.c */ { CLIENT_FINDANONGAME, handle_anongame_packet }, { CLIENT_FILEINFOREQ, _client_fileinforeq }, { CLIENT_MOTD_W3, _client_motdw3 }, { CLIENT_REALMLISTREQ, _client_realmlistreq }, { CLIENT_REALMLISTREQ_110, _client_realmlistreq110 }, { CLIENT_PROFILEREQ, _client_profilereq }, { CLIENT_REALMJOINREQ_109, _client_realmjoinreq109 }, { CLIENT_UNKNOWN_37, _client_charlistreq }, { CLIENT_UNKNOWN_39, _client_unknown39 }, { CLIENT_ECHOREPLY, _client_echoreply }, { CLIENT_PINGREQ, _client_pingreq }, { CLIENT_ADREQ, _client_adreq }, { CLIENT_ADACK, _client_adack }, { CLIENT_ADCLICK, _client_adclick }, { CLIENT_ADCLICK2, _client_adclick2 }, { CLIENT_READMEMORY, _client_readmemory }, { CLIENT_STATSREQ, _client_statsreq }, { CLIENT_STATSUPDATE, _client_statsupdate }, { CLIENT_PLAYERINFOREQ, _client_playerinforeq }, { CLIENT_PROGIDENT2, _client_progident2 }, { CLIENT_JOINCHANNEL, _client_joinchannel }, { CLIENT_MESSAGE, _client_message }, { CLIENT_GAMELISTREQ, _client_gamelistreq }, { CLIENT_JOIN_GAME, _client_joingame }, { CLIENT_STARTGAME1, _client_startgame1 }, { CLIENT_STARTGAME3, _client_startgame3 }, { CLIENT_STARTGAME4, _client_startgame4 }, { CLIENT_CLOSEGAME, _client_closegame }, { CLIENT_CLOSEGAME2, _client_closegame }, { CLIENT_GAME_REPORT, _client_gamereport }, { CLIENT_LEAVECHANNEL, _client_leavechannel }, { CLIENT_LADDERREQ, _client_ladderreq }, { CLIENT_LADDERSEARCHREQ, _client_laddersearchreq }, { CLIENT_MAPAUTHREQ1, _client_mapauthreq1 }, { CLIENT_MAPAUTHREQ2, _client_mapauthreq2 }, { CLIENT_CLAN_DISBANDREQ, _client_clan_disbandreq }, { CLIENT_CLANMEMBERLIST_REQ, _client_clanmemberlistreq }, { CLIENT_CLAN_MOTDCHG, _client_clan_motdchg }, { CLIENT_CLAN_MOTDREQ, _client_clan_motdreq }, { CLIENT_CLAN_CREATEREQ, _client_clan_createreq }, { CLIENT_CLAN_CREATEINVITEREQ, _client_clan_createinvitereq }, { CLIENT_CLAN_CREATEINVITEREPLY, _client_clan_createinvitereply }, { CLIENT_CLANMEMBER_RANKUPDATE_REQ, _client_clanmember_rankupdatereq }, { CLIENT_CLANMEMBER_REMOVE_REQ, _client_clanmember_removereq }, { CLIENT_CLAN_MEMBERNEWCHIEFREQ, _client_clan_membernewchiefreq }, { CLIENT_CLAN_INVITEREQ, _client_clan_invitereq }, { CLIENT_CLAN_INVITEREPLY, _client_clan_invitereply }, { CLIENT_CRASHDUMP, _client_crashdump }, { CLIENT_SETEMAILREPLY, _client_setemailreply }, { CLIENT_CLANINFOREQ, _client_claninforeq }, { CLIENT_EXTRAWORK, _client_extrawork }, { CLIENT_NULL, NULL }, { -1, NULL } }; /* main handler function */ static int handle(const t_htable_row * htable, int type, t_connection * c, t_packet const *const packet); extern int handle_bnet_packet(t_connection * c, t_packet const *const packet) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", conn_get_socket(c)); return -1; } if (packet_get_class(packet) != packet_class_bnet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", conn_get_socket(c), (int)packet_get_class(packet)); return -1; } switch (conn_get_state(c)) { case conn_state_connected: switch (handle(bnet_htable_con, packet_get_type(packet), c, packet)) { case 1: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown (unlogged in) bnet packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); break; case -1: eventlog(eventlog_level_error, __FUNCTION__, "[{}] (unlogged in) got error handling packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); break; }; break; case conn_state_loggedin: switch (handle(bnet_htable_log, packet_get_type(packet), c, packet)) { case 1: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown (logged in) bnet packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); break; case -1: eventlog(eventlog_level_error, __FUNCTION__, "[{}] (logged in) got error handling packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); break; }; break; case conn_state_untrusted: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown (untrusted) bnet packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] invalid login state {}", conn_get_socket(c), conn_get_state(c)); }; return 0; } static int handle(const t_htable_row * htable, int type, t_connection * c, t_packet const *const packet) { t_htable_row const *p; int res = 1; for (p = htable; p->type != -1; p++) if (p->type == type) { res = 0; if (p->handler != NULL) res = p->handler(c, packet); if (res != 2) break; /* return 2 means we want to continue parsing */ } return res; } /* handlers for bnet packets */ static int _client_unknown_1b(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_unknown_1b)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UNKNOWN_1B packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_unknown_1b), packet_get_size(packet)); return -1; } { unsigned int newip; unsigned short newport; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] UNKNOWN_1B unknown1=0x{:04hx}", conn_get_socket(c), bn_short_get(packet->u.client_unknown_1b.unknown1)); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] UNKNOWN_1B unknown2=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_unknown_1b.unknown2)); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] UNKNOWN_1B unknown3=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_unknown_1b.unknown3)); newip = bn_int_nget(packet->u.client_unknown_1b.ip); newport = bn_short_nget(packet->u.client_unknown_1b.port); eventlog(eventlog_level_info, __FUNCTION__, "[{}] UNKNOWN_1B set new UDP address to {}", conn_get_socket(c), addr_num_to_addr_str(newip, newport)); conn_set_game_addr(c, newip); conn_set_game_port(c, newport); } return 0; } static int _client_compinfo1(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_compinfo1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COMPINFO1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_compinfo1), packet_get_size(packet)); return -1; } { char const *host; char const *user; if (!(host = packet_get_str_const(packet, sizeof(t_client_compinfo1), MAX_WINHOST_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COMPINFO1 packet (missing or too long host)", conn_get_socket(c)); return -1; } if (!(user = packet_get_str_const(packet, sizeof(t_client_compinfo1)+std::strlen(host) + 1, MAX_WINUSER_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COMPINFO1 packet (missing or too long user)", conn_get_socket(c)); return -1; } conn_set_host(c, host); conn_set_user(c, user); } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_compreply)); packet_set_type(rpacket, SERVER_COMPREPLY); bn_int_set(&rpacket->u.server_compreply.reg_version, SERVER_COMPREPLY_REG_VERSION); bn_int_set(&rpacket->u.server_compreply.reg_auth, SERVER_COMPREPLY_REG_AUTH); bn_int_set(&rpacket->u.server_compreply.client_id, SERVER_COMPREPLY_CLIENT_ID); bn_int_set(&rpacket->u.server_compreply.client_token, SERVER_COMPREPLY_CLIENT_TOKEN); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_sessionkey1)); packet_set_type(rpacket, SERVER_SESSIONKEY1); bn_int_set(&rpacket->u.server_sessionkey1.sessionkey, conn_get_sessionkey(c)); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_compinfo2(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_compinfo2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COMPINFO2 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_compinfo2), packet_get_size(packet)); return -1; } { char const *host; char const *user; if (!(host = packet_get_str_const(packet, sizeof(t_client_compinfo2), MAX_WINHOST_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COMPINFO2 packet (missing or too long host)", conn_get_socket(c)); return -1; } if (!(user = packet_get_str_const(packet, sizeof(t_client_compinfo2)+std::strlen(host) + 1, MAX_WINUSER_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COMPINFO2 packet (missing or too long user)", conn_get_socket(c)); return -1; } conn_set_host(c, host); conn_set_user(c, user); } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_compreply)); packet_set_type(rpacket, SERVER_COMPREPLY); bn_int_set(&rpacket->u.server_compreply.reg_version, SERVER_COMPREPLY_REG_VERSION); bn_int_set(&rpacket->u.server_compreply.reg_auth, SERVER_COMPREPLY_REG_AUTH); bn_int_set(&rpacket->u.server_compreply.client_id, SERVER_COMPREPLY_CLIENT_ID); bn_int_set(&rpacket->u.server_compreply.client_token, SERVER_COMPREPLY_CLIENT_TOKEN); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_sessionkey2)); packet_set_type(rpacket, SERVER_SESSIONKEY2); bn_int_set(&rpacket->u.server_sessionkey2.sessionnum, conn_get_sessionnum(c)); bn_int_set(&rpacket->u.server_sessionkey2.sessionkey, conn_get_sessionkey(c)); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_countryinfo1(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_countryinfo1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COUNTRYINFO1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_countryinfo1), packet_get_size(packet)); return -1; } { char const *langstr; char const *countrycode; char const *country; unsigned int tzbias; if (!(langstr = packet_get_str_const(packet, sizeof(t_client_countryinfo1), MAX_LANG_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COUNTRYINFO1 packet (missing or too long langstr)", conn_get_socket(c)); return -1; } if (!(countrycode = packet_get_str_const(packet, sizeof(t_client_countryinfo1)+std::strlen(langstr) + 1, MAX_COUNTRYCODE_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COUNTRYINFO1 packet (missing or too long countrycode)", conn_get_socket(c)); return -1; } if (!(country = packet_get_str_const(packet, sizeof(t_client_countryinfo1)+std::strlen(langstr) + 1 + std::strlen(countrycode) + 1, MAX_COUNTRY_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COUNTRYINFO1 packet (missing or too long country)", conn_get_socket(c)); return -1; } if (!(packet_get_str_const(packet, sizeof(t_client_countryinfo1)+std::strlen(langstr) + 1 + std::strlen(countrycode) + 1 + std::strlen(country) + 1, MAX_COUNTRYNAME_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad COUNTRYINFO1 packet (missing or too long countryname)", conn_get_socket(c)); return -1; } tzbias = bn_int_get(packet->u.client_countryinfo1.bias); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] COUNTRYINFO1 packet from tzbias=0x{:04x} langstr={} countrycode={} country={}", conn_get_socket(c), tzbias, langstr, countrycode, country); conn_set_country(c, country); conn_set_tzbias(c, uint32_to_int(tzbias)); } return 0; } static int _client_auth_info(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_auth_info)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTH_INFO packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_auth_info), packet_get_size(packet)); return -1; } { char const *langstr; char const *countryname; unsigned int tzbias; char archtag_str[5]; char clienttag_str[5]; char gamelang_str[5]; if (!(langstr = packet_get_str_const(packet, sizeof(t_client_auth_info), MAX_LANG_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTH_INFO packet (missing or too long langstr)", conn_get_socket(c)); return -1; } if (!(countryname = packet_get_str_const(packet, sizeof(t_client_auth_info)+std::strlen(langstr) + 1, MAX_COUNTRYNAME_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTH_INFO packet (missing or too long countryname)", conn_get_socket(c)); return -1; } /* check if it's an allowed client type */ if (tag_check_in_list(bn_int_get(packet->u.client_auth_info.clienttag), prefs_get_allowed_clients())) { conn_set_state(c, conn_state_destroy); return 0; } tzbias = bn_int_get(packet->u.client_auth_info.bias); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] AUTH_INFO packet {{ protocol={:#02x}, platform={}, product={}, versionid={:#02x}, language={}, localip={:#04x}, tzbias={:04x}, locale={}, language={}, country={}.{} }}", conn_get_socket(c), bn_int_get(packet->u.client_auth_info.protocol), tag_uint_to_str(archtag_str, bn_int_get(packet->u.client_auth_info.archtag)), tag_uint_to_str(clienttag_str, bn_int_get(packet->u.client_auth_info.clienttag)), bn_int_get(packet->u.client_auth_info.versionid), tag_uint_to_str(gamelang_str, bn_int_get(packet->u.client_auth_info.gamelang)), bn_int_get(packet->u.client_auth_info.localip), tzbias, bn_int_get(packet->u.client_auth_info.lcid), bn_int_get(packet->u.client_auth_info.langid), langstr, countryname); conn_set_country(c, langstr); /* FIXME: This isn't right. We want USA not ENU (English-US) */ conn_set_tzbias(c, uint32_to_int(tzbias)); conn_set_versionid(c, bn_int_get(packet->u.client_auth_info.versionid)); conn_set_archtag(c, bn_int_get(packet->u.client_auth_info.archtag)); conn_set_clienttag(c, bn_int_get(packet->u.client_auth_info.clienttag)); conn_set_gamelang(c, bn_int_get(packet->u.client_auth_info.gamelang)); /* First, send an ECHO_REQ */ if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_echoreq)); packet_set_type(rpacket, SERVER_ECHOREQ); bn_int_set(&rpacket->u.server_echoreq.ticks, get_ticks()); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_authreq_109)); packet_set_type(rpacket, SERVER_AUTHREQ_109); // Logon type if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT)) bn_int_set(&rpacket->u.server_authreq_109.logontype, SERVER_AUTHREQ_109_LOGONTYPE_W3); else if ((conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) bn_int_set(&rpacket->u.server_authreq_109.logontype, SERVER_AUTHREQ_109_LOGONTYPE_W3XP); else bn_int_set(&rpacket->u.server_authreq_109.logontype, SERVER_AUTHREQ_109_LOGONTYPE); // Session bn_int_set(&rpacket->u.server_authreq_109.sessionkey, conn_get_sessionkey(c)); bn_int_set(&rpacket->u.server_authreq_109.sessionnum, conn_get_sessionnum(c)); // CheckRevision std::tuple checkrevision = select_checkrevision(bn_int_get(packet->u.client_auth_info.archtag), bn_int_get(packet->u.client_auth_info.clienttag), bn_int_get(packet->u.client_auth_info.versionid)); file_to_mod_time(c, std::get<0>(checkrevision).c_str(), &rpacket->u.server_authreq_109.timestamp); // Checkrevision file timestamp packet_append_string(rpacket, std::get<0>(checkrevision).c_str()); // CheckRevision filename packet_append_string(rpacket, std::get<1>(checkrevision).c_str()); // CheckRevision equation eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), std::get<0>(checkrevision), std::get<1>(checkrevision)); // WarCraft 3 Server Signature if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) { char padding[128]; std::memset(padding, 0, 128); packet_append_data(rpacket, padding, 128); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_unknown2b(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_unknown_2b)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UNKNOWN_2B packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_unknown_2b), packet_get_size(packet)); return -1; } return 0; } static int _client_progident(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_progident)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PROGIDENT packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_progident), packet_get_size(packet)); return -1; } if (tag_check_in_list(bn_int_get(packet->u.client_progident.clienttag), prefs_get_allowed_clients())) { conn_set_state(c, conn_state_destroy); return 0; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] CLIENT_PROGIDENT archtag=0x{:08x} clienttag=0x{:08x} versionid=0x{:08x} unknown1=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_progident.archtag), bn_int_get(packet->u.client_progident.clienttag), bn_int_get(packet->u.client_progident.versionid), bn_int_get(packet->u.client_progident.unknown1)); conn_set_archtag(c, bn_int_get(packet->u.client_progident.archtag)); conn_set_clienttag(c, bn_int_get(packet->u.client_progident.clienttag)); conn_set_versionid(c, bn_int_get(packet->u.client_progident.versionid)); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_authreq1)); packet_set_type(rpacket, SERVER_AUTHREQ1); // CheckRevision std::tuple checkrevision = select_checkrevision(bn_int_get(packet->u.client_progident.archtag), bn_int_get(packet->u.client_progident.clienttag), bn_int_get(packet->u.client_progident.versionid)); file_to_mod_time(c, std::get<0>(checkrevision).c_str(), &rpacket->u.server_authreq1.timestamp); // Checkrevision file timestamp packet_append_string(rpacket, std::get<0>(checkrevision).c_str()); // CheckRevision filename packet_append_string(rpacket, std::get<1>(checkrevision).c_str()); // CheckRevision equation eventlog(eventlog_level_debug, __FUNCTION__, "[{}] selected \"{}\" \"{}\"", conn_get_socket(c), std::get<0>(checkrevision), std::get<1>(checkrevision)); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_createaccountw3(t_connection * c, t_packet const *const packet) { char const *username; char const *plainpass; t_hash sc_hash; unsigned int i; const char *account_salt; const char *account_verifier; t_account *account; bool presume_plainpass = true; if (packet_get_size(packet) < sizeof(t_client_createaccount_w3)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CREATEACCOUNT_W3 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_createaccount_w3), packet_get_size(packet)); return -1; } username = packet_get_str_const(packet, sizeof(t_client_createaccount_w3), UNCHECKED_NAME_STR); if (!username) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CREATEACCOUNT_W3 (missing or too long username)", conn_get_socket(c)); return -1; } /* PELISH: We are testing if username missing and if packetsize is good so we does not need to test if salt and verifier are present */ account_salt = (const char *)packet_get_data_const(packet, offsetof(t_client_createaccount_w3, salt), 32); account_verifier = (const char *)packet_get_data_const(packet, offsetof(t_client_createaccount_w3, password_verifier), 32); for (i = 0; i<16; i++){ int value = (unsigned char)account_verifier[i]; if (value == 0) break; presume_plainpass &= (std::isprint(value)>0); } for (i = 16; (i < 32) && presume_plainpass; i++){ int value = account_verifier[i]; presume_plainpass &= (value == 0); } if (presume_plainpass) eventlog(eventlog_level_debug, __FUNCTION__, "This looks more like a plaintext password than like a verifier"); plainpass = packet_get_str_const(packet, offsetof(t_client_createaccount_w3, password_verifier), 16); if (presume_plainpass && !plainpass) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CREATEACCOUNT_W3 (missing password)", conn_get_socket(c)); return -1; } t_packet* const rpacket = packet_create(packet_class_bnet); if (!rpacket) return -1; packet_set_size(rpacket, sizeof(t_server_createaccount_w3)); packet_set_type(rpacket, SERVER_CREATEACCOUNT_W3); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] new account requested for \"{}\"", conn_get_socket(c), username); if (prefs_get_allow_new_accounts() == 0) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account not created (disabled)", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createaccount_w3.result, SERVER_CREATEACCOUNT_W3_RESULT_EXIST); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } if (account_check_name(username) < 0) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account not created (invalid symbols)", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createaccount_w3.result, SERVER_CREATEACCOUNT_W3_RESULT_INVALID); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } char lpass[20] = {}; if (plainpass) { /* convert plaintext password to lowercase for sc etc. */ std::snprintf(lpass, sizeof lpass, "%s", plainpass); strtolower(lpass); } //set password hash for sc etc. bnet_hash(&sc_hash, std::strlen(lpass), lpass); if (!(account = accountlist_create_account(username, hash_get_str(sc_hash)))) { bn_int_set(&rpacket->u.server_createaccount_w3.result, SERVER_CREATEACCOUNT_W3_RESULT_EXIST); } else { if (presume_plainpass) { BigInt salt = BigInt((unsigned char*)account_salt, 32, 4, false); BnetSRP3 srp3 = BnetSRP3(username, plainpass); srp3.setSalt(salt); BigInt verifier = srp3.getVerifier(); account_verifier = (const char*)verifier.getData(32, 4, false); } else { //NEED TO TAG ACCOUNT AS "SC PASS BROKEN" } account_set_salt(account, account_salt); account_set_verifier(account, account_verifier); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account created", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createaccount_w3.result, SERVER_CREATEACCOUNT_W3_RESULT_OK); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_createacctreq1(t_connection * c, t_packet const *const packet) { t_packet *rpacket; char const *username; t_hash newpasshash1; if (packet_get_size(packet) < sizeof(t_client_createacctreq1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CREATEACCTREQ1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_createacctreq1), packet_get_size(packet)); return -1; } if (!(username = packet_get_str_const(packet, sizeof(t_client_createacctreq1), UNCHECKED_NAME_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CREATEACCTREQ1 (missing or too long username)", conn_get_socket(c)); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] new account requested for \"{}\"", conn_get_socket(c), username); rpacket = packet_create(packet_class_bnet); if (!rpacket) return -1; packet_set_size(rpacket, sizeof(t_server_createacctreply1)); packet_set_type(rpacket, SERVER_CREATEACCTREPLY1); if (prefs_get_allow_new_accounts() == 0) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account not created (disabled)", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createacctreply1.result, SERVER_CREATEACCTREPLY1_RESULT_NO); goto out; } bnhash_to_hash(packet->u.client_createacctreq1.password_hash1, &newpasshash1); if (!accountlist_create_account(username, hash_get_str(newpasshash1))) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account not created (failed)", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createacctreply1.result, SERVER_CREATEACCTREPLY1_RESULT_NO); goto out; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account created", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createacctreply1.result, SERVER_CREATEACCTREPLY1_RESULT_OK); out: conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_createacctreq2(t_connection * c, t_packet const *const packet) { t_packet *rpacket; char const *username; t_hash newpasshash1; if (packet_get_size(packet) < sizeof(t_client_createacctreq2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_CREATEACCTREQ2 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_createacctreq2), packet_get_size(packet)); return -1; } username = packet_get_str_const(packet, sizeof(t_client_createacctreq2), UNCHECKED_NAME_STR); if (!username) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CREATEACCTREQ2 (missing or too long username)", conn_get_socket(c)); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] new account requested for \"{}\"", conn_get_socket(c), username); rpacket = packet_create(packet_class_bnet); if (!rpacket) return -1; packet_set_size(rpacket, sizeof(t_server_createacctreply2)); packet_set_type(rpacket, SERVER_CREATEACCTREPLY2); if (prefs_get_allow_new_accounts() == 0) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account not created (disabled)", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createacctreply2.result, SERVER_CREATEACCTREPLY2_RESULT_EXIST); goto out; } if (account_check_name(username) < 0) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account not created (invalid symbols)", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createaccount_w3.result, SERVER_CREATEACCTREPLY2_RESULT_INVALID); goto out; } bnhash_to_hash(packet->u.client_createacctreq2.password_hash1, &newpasshash1); if (!accountlist_create_account(username, hash_get_str(newpasshash1))) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account not created (failed)", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createacctreply2.result, SERVER_CREATEACCTREPLY2_RESULT_EXIST); /* FIXME: return reason for failure */ goto out; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account created", conn_get_socket(c)); bn_int_set(&rpacket->u.server_createacctreply2.result, SERVER_CREATEACCTREPLY2_RESULT_OK); out: conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_changepassreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_changepassreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CHANGEPASSREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_changepassreq), packet_get_size(packet)); return -1; } { char const *username; t_account *account; if (!(username = packet_get_str_const(packet, sizeof(t_client_changepassreq), UNCHECKED_NAME_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CHANGEPASSREQ (missing or too long username)", conn_get_socket(c)); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change requested for \"{}\"", conn_get_socket(c), username); if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_changepassack)); packet_set_type(rpacket, SERVER_CHANGEPASSACK); /* fail if logged in or no account */ if (connlist_find_connection_by_accountname(username) || !(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change for \"{}\" refused (no such account)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_changepassack.message, SERVER_CHANGEPASSACK_MESSAGE_FAIL); } else if (account_get_auth_changepass(account) == 0) { /* default to true */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change for \"{}\" refused (no change access)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_changepassack.message, SERVER_CHANGEPASSACK_MESSAGE_FAIL); } else if (conn_get_sessionkey(c) != bn_int_get(packet->u.client_changepassreq.sessionkey)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] password change for \"{}\" refused (expected session key 0x{:08x}, got 0x{:08x})", conn_get_socket(c), username, conn_get_sessionkey(c), bn_int_get(packet->u.client_changepassreq.sessionkey)); bn_int_set(&rpacket->u.server_changepassack.message, SERVER_CHANGEPASSACK_MESSAGE_FAIL); } else { struct { bn_int ticks; bn_int sessionkey; bn_int passhash1[5]; } temp; char const *oldstrhash1; t_hash oldpasshash1; t_hash oldpasshash2; t_hash trypasshash2; t_hash newpasshash1; if ((oldstrhash1 = account_get_pass(account))) { bn_int_set(&temp.ticks, bn_int_get(packet->u.client_changepassreq.ticks)); bn_int_set(&temp.sessionkey, bn_int_get(packet->u.client_changepassreq.sessionkey)); if (hash_set_str(&oldpasshash1, oldstrhash1) < 0) { bnhash_to_hash(packet->u.client_changepassreq.newpassword_hash1, &newpasshash1); account_set_pass(account, hash_get_str(newpasshash1)); eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change for \"{}\" successful (bad previous password)", conn_get_socket(c), account_get_name(account)); bn_int_set(&rpacket->u.server_changepassack.message, SERVER_CHANGEPASSACK_MESSAGE_SUCCESS); } else { hash_to_bnhash((t_hash const *)&oldpasshash1, temp.passhash1); /* avoid warning */ bnet_hash(&oldpasshash2, sizeof(temp), &temp); /* do the double hash */ bnhash_to_hash(packet->u.client_changepassreq.oldpassword_hash2, &trypasshash2); if (hash_eq(trypasshash2, oldpasshash2) == 1) { bnhash_to_hash(packet->u.client_changepassreq.newpassword_hash1, &newpasshash1); account_set_pass(account, hash_get_str(newpasshash1)); eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change for \"{}\" successful (previous password)", conn_get_socket(c), account_get_name(account)); bn_int_set(&rpacket->u.server_changepassack.message, SERVER_CHANGEPASSACK_MESSAGE_SUCCESS); } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change for \"{}\" refused (wrong password)", conn_get_socket(c), account_get_name(account)); conn_increment_passfail_count(c); bn_int_set(&rpacket->u.server_changepassack.message, SERVER_CHANGEPASSACK_MESSAGE_FAIL); } } } else { bnhash_to_hash(packet->u.client_changepassreq.newpassword_hash1, &newpasshash1); account_set_pass(account, hash_get_str(newpasshash1)); eventlog(eventlog_level_info, __FUNCTION__, "[{}] password change for \"{}\" successful (no previous password)", conn_get_socket(c), account_get_name(account)); bn_int_set(&rpacket->u.server_changepassack.message, SERVER_CHANGEPASSACK_MESSAGE_SUCCESS); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_echoreply(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_echoreply)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ECHOREPLY packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_echoreply), packet_get_size(packet)); return -1; } { unsigned int now; unsigned int then; now = get_ticks(); then = bn_int_get(packet->u.client_echoreply.ticks); if (!now || !then || now < then) eventlog(eventlog_level_warn, __FUNCTION__, "[{}] bad timing in echo reply: now={} then={}", conn_get_socket(c), now, then); else conn_set_latency(c, now - then); } return 0; } static int _client_authreq1(t_connection * c, t_packet const *const packet) { eventlog(eventlog_level_trace, __FUNCTION__, "[{}] received AUTHREQ1(0x07) packet", conn_get_socket(c)); if (packet_get_size(packet) < sizeof(t_client_authreq1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_authreq1), packet_get_size(packet)); return -1; } auto send_failed_packet = [](t_connection *c) { t_packet *rpacket = packet_create(packet_class_bnet); if (rpacket) { packet_set_size(rpacket, sizeof(t_server_authreply1)); packet_set_type(rpacket, SERVER_AUTHREPLY1); conn_set_state(c, conn_state_untrusted); bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_BADVERSION); packet_append_string(rpacket, ""); packet_append_string(rpacket, ""); // undocumented extra null terminator conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } }; // The following if statements are sanity checks // The client should have already sent this information in a previous packet and is resending it again in this packet if (bn_int_get(packet->u.client_authreq1.archtag) != conn_get_archtag(c)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 (mismatch architecture)", conn_get_socket(c)); send_failed_packet(c); return -1; } if (bn_int_get(packet->u.client_authreq1.clienttag) != conn_get_clienttag(c)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 (mismatch client tag)", conn_get_socket(c)); send_failed_packet(c); return -1; } if (bn_int_get(packet->u.client_authreq1.versionid) != conn_get_versionid(c)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 (mismatch version ID)", conn_get_socket(c)); send_failed_packet(c); return -1; } const char *exeinfo = packet_get_str_const(packet, sizeof(t_client_authreq1), MAX_EXEINFO_STR); if (exeinfo) { conn_set_clientexe(c, exeinfo); } else { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ1 (missing or too long exeinfo)", conn_get_socket(c)); send_failed_packet(c); return 0; } std::string version_string = vernum_to_verstr(bn_int_get(packet->u.client_authreq1.gameversion)); conn_set_clientver(c, version_string.c_str()); eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_AUTHREQ1 archtag=0x{:08x} clienttag=0x{:08x} verstr={} exeinfo=\"{}\" versionid=0x{:08x} gameversion=0x{:08x} checksum=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_authreq1.archtag), bn_int_get(packet->u.client_authreq1.clienttag), version_string, exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c)); conn_set_versionid(c, bn_int_get(packet->u.client_authreq1.versionid)); conn_set_checksum(c, bn_int_get(packet->u.client_authreq1.checksum)); conn_set_gameversion(c, bn_int_get(packet->u.client_authreq1.gameversion)); const VersionCheck* vc = select_versioncheck(conn_get_archtag(c), conn_get_clienttag(c), conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c)); conn_set_versioncheck(c, vc); if (vc) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] client matches versiontag \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag()); } else { if (prefs_get_allow_unknown_version()) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] skipping versioncheck because allow_unknown_version is true", conn_get_socket(c)); } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed versioncheck", conn_get_socket(c)); send_failed_packet(c); return 0; } } t_packet *rpacket = packet_create(packet_class_bnet); if (rpacket) { packet_set_size(rpacket, sizeof(t_server_authreply1)); packet_set_type(rpacket, SERVER_AUTHREPLY1); char *mpqfilename = nullptr; if (vc) { mpqfilename = autoupdate_check(conn_get_archtag(c), conn_get_clienttag(c), conn_get_gamelang(c), vc->get_version_tag().c_str(), nullptr); } // Only handle updates when there is an update file available. if (mpqfilename) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] an upgrade for version {} is available \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag(), mpqfilename); bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_UPDATE); packet_append_string(rpacket, mpqfilename); xfree(static_cast(mpqfilename)); } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] no upgrade is available", conn_get_socket(c)); } bn_int_set(&rpacket->u.server_authreply1.message, SERVER_AUTHREPLY1_MESSAGE_OK); packet_append_string(rpacket, ""); packet_append_string(rpacket, ""); // FIXME: what's the second string for? conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_authreq109(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_authreq_109)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_authreq_109), packet_get_size(packet)); return 0; } auto send_failed_packet = [](t_connection *c) { t_packet *rpacket = packet_create(packet_class_bnet); if (rpacket) { packet_set_size(rpacket, sizeof(t_server_authreply_109)); packet_set_type(rpacket, SERVER_AUTHREPLY_109); conn_set_state(c, conn_state_untrusted); bn_int_set(&rpacket->u.server_authreply_109.message, SERVER_AUTHREPLY_109_MESSAGE_BADVERSION); packet_append_string(rpacket, ""); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } }; std::uint32_t count = bn_int_get(packet->u.client_authreq_109.cdkey_number); std::size_t position = sizeof(t_client_authreq_109) + (count * sizeof(t_cdkey_info)); const char *const exeinfo = packet_get_str_const(packet, position, MAX_EXEINFO_STR); if (exeinfo) { conn_set_clientexe(c, exeinfo); } else { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long exeinfo)", conn_get_socket(c)); send_failed_packet(c); return 0; } position += std::strlen(exeinfo) + 1; const char *const owner = packet_get_str_const(packet, position, MAX_OWNER_STR); if (owner) { conn_set_owner(c, owner); } else { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad AUTHREQ_109 (missing or too long owner)", conn_get_socket(c)); conn_set_owner(c, ""); } conn_set_checksum(c, bn_int_get(packet->u.client_authreq_109.checksum)); conn_set_gameversion(c, bn_int_get(packet->u.client_authreq_109.gameversion)); std::string version = vernum_to_verstr(bn_int_get(packet->u.client_authreq_109.gameversion)); conn_set_clientver(c, version.c_str()); eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_AUTHREQ_109 ticks=0x{:08x}, verstr={} exeinfo=\"{}\" versionid=0x{:08x} gameversion=0x{:08x} checksum=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_authreq_109.ticks), version, exeinfo, conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c)); t_packet *rpacket; if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_authreply_109)); packet_set_type(rpacket, SERVER_AUTHREPLY_109); const VersionCheck* vc = select_versioncheck(conn_get_archtag(c), conn_get_clienttag(c), conn_get_versionid(c), conn_get_gameversion(c), conn_get_checksum(c)); conn_set_versioncheck(c, vc); if (vc) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] client matches versiontag \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag()); } else { if (prefs_get_allow_unknown_version()) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] skipping versioncheck because allow_unknown_version is true", conn_get_socket(c)); } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] client failed versioncheck", conn_get_socket(c)); packet_del_ref(rpacket); send_failed_packet(c); return 0; } } char *mpqfilename = nullptr; if (vc) { mpqfilename = autoupdate_check(conn_get_archtag(c), conn_get_clienttag(c), conn_get_gamelang(c), conn_get_versioncheck(c)->get_version_tag().c_str(), NULL); } // Only handle updates when there is an update file available. if (mpqfilename) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] an upgrade for {} is available \"{}\"", conn_get_socket(c), conn_get_versioncheck(c)->get_version_tag(), mpqfilename); bn_int_set(&rpacket->u.server_authreply_109.message, SERVER_AUTHREPLY_109_MESSAGE_UPDATE); packet_append_string(rpacket, mpqfilename); xfree(static_cast(mpqfilename)); } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] no upgrade is available", conn_get_socket(c)); } bn_int_set(&rpacket->u.server_authreply_109.message, SERVER_AUTHREPLY_109_MESSAGE_OK); packet_append_string(rpacket, ""); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_regsnoopreply(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_regsnoopreply)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad REGSNOOPREPLY packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_regsnoopreply), packet_get_size(packet)); return -1; } return 0; } static int _client_iconreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_iconreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ICONREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_iconreq), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_iconreply)); packet_set_type(rpacket, SERVER_ICONREPLY); file_to_mod_time(c, prefs_get_iconfile(), &rpacket->u.server_iconreply.timestamp); /* battle.net sends different file on iconreq for WAR3 and W3XP [Omega] */ if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT)) packet_append_string(rpacket, prefs_get_war3_iconfile()); /* battle.net still sends "icons.bni" to sc/bw clients * clients request icons_STAR.bni seperatly */ /* else if (std::strcmp(conn_get_clienttag(c),CLIENTTAG_STARCRAFT)==0) packet_append_string(rpacket,prefs_get_star_iconfile()); else if (std::strcmp(conn_get_clienttag(c),CLIENTTAG_BROODWARS)==0) packet_append_string(rpacket,prefs_get_star_iconfile()); */ else packet_append_string(rpacket, prefs_get_iconfile()); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_cdkey(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_cdkey)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CDKEY packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_cdkey), packet_get_size(packet)); return -1; } { char const *cdkey; char const *owner; if (!(cdkey = packet_get_str_const(packet, sizeof(t_client_cdkey), MAX_CDKEY_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CDKEY packet (missing or too long cdkey)", conn_get_socket(c)); return -1; } if (!(owner = packet_get_str_const(packet, sizeof(t_client_cdkey)+std::strlen(cdkey) + 1, MAX_OWNER_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CDKEY packet (missing or too long owner)", conn_get_socket(c)); return -1; } conn_set_cdkey(c, cdkey); conn_set_owner(c, owner); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_cdkeyreply)); packet_set_type(rpacket, SERVER_CDKEYREPLY); bn_int_set(&rpacket->u.server_cdkeyreply.message, SERVER_CDKEYREPLY_MESSAGE_OK); packet_append_string(rpacket, owner); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } #if 0 /* Blizzard used this to track down pirates, should only be accepted by old clients */ if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_regsnoopreq)); packet_set_type(rpacket, SERVER_REGSNOOPREQ); bn_int_set(&rpacket->u.server_regsnoopreq.unknown1, SERVER_REGSNOOPREQ_UNKNOWN1); /* sequence num */ bn_int_set(&rpacket->u.server_regsnoopreq.hkey, SERVER_REGSNOOPREQ_HKEY_CURRENT_USER); packet_append_string(rpacket, SERVER_REGSNOOPREQ_REGKEY); packet_append_string(rpacket, SERVER_REGSNOOPREQ_REGVALNAME); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } #endif return 0; } static int _client_cdkey2(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_cdkey2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CDKEY2 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_cdkey2), packet_get_size(packet)); return -1; } { char const *owner; if (!(owner = packet_get_str_const(packet, sizeof(t_client_cdkey2), MAX_OWNER_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CDKEY2 packet (missing or too long owner)", conn_get_socket(c)); return -1; } conn_set_owner(c, owner); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_cdkeyreply2)); packet_set_type(rpacket, SERVER_CDKEYREPLY2); bn_int_set(&rpacket->u.server_cdkeyreply2.message, SERVER_CDKEYREPLY2_MESSAGE_OK); packet_append_string(rpacket, owner); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_cdkey3(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_cdkey3)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CDKEY3 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_cdkey2), packet_get_size(packet)); return -1; } { char const *owner; if (!(owner = packet_get_str_const(packet, sizeof(t_client_cdkey3), MAX_OWNER_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CDKEY3 packet (missing or too long owner)", conn_get_socket(c)); return -1; } conn_set_owner(c, owner); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_cdkeyreply3)); packet_set_type(rpacket, SERVER_CDKEYREPLY3); bn_int_set(&rpacket->u.server_cdkeyreply3.message, SERVER_CDKEYREPLY3_MESSAGE_OK); packet_append_string(rpacket, ""); /* FIXME: owner, message, ??? */ conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_udpok(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_udpok)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UDPOK packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_udpok), packet_get_size(packet)); return -1; } /* we could check the contents but there really isn't any point */ conn_set_udpok(c); return 0; } static int _client_fileinforeq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_fileinforeq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad FILEINFOREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_fileinforeq), packet_get_size(packet)); return -1; } { char const *filename; if (!(filename = packet_get_str_const(packet, sizeof(t_client_fileinforeq), MAX_FILENAME_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad FILEINFOREQ packet (missing or too long tosfile)", conn_get_socket(c)); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] file requested: \"{}\" - type = 0x{:02x}", conn_get_socket(c), filename, bn_int_get(packet->u.client_fileinforeq.type)); /* TODO: if type is TOSFILE make bnetd to send default tosfile if selected is not found */ if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_fileinforeply)); packet_set_type(rpacket, SERVER_FILEINFOREPLY); bn_int_set(&rpacket->u.server_fileinforeply.type, bn_int_get(packet->u.client_fileinforeq.type)); bn_int_set(&rpacket->u.server_fileinforeply.unknown2, bn_int_get(packet->u.client_fileinforeq.unknown2)); /* Note from Sherpya: * timestamp -> 0x852b7d00 - 0x01c0e863 b.net send this (bn_int), * I suppose is not a long * if bnserver-D2DV is bad diablo 2 crashes * timestamp doesn't work correctly and starcraft * needs name in client locale or displays hostname */ file_to_mod_time(c, filename, &rpacket->u.server_fileinforeply.timestamp); packet_append_string(rpacket, filename); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static const char *_attribute_req(t_account * reqacc, t_account * myacc, const char *key) { const char *result = ""; const char *tval; if (!reqacc) goto out; if (reqacc != myacc && !strncasecmp(key, "BNET", 4)) goto out; tval = account_get_strattr(reqacc, key); if (tval) result = tval; out: return result; } static int _client_statsreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; char const *name; char const *key; unsigned int name_count; unsigned int key_count; unsigned int i, j; unsigned int name_off; unsigned int keys_off; unsigned int key_off; t_account *reqacc, *myacc; if (packet_get_size(packet) < sizeof(t_client_statsreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STATSREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_statsreq), packet_get_size(packet)); return -1; } name_count = bn_int_get(packet->u.client_statsreq.name_count); key_count = bn_int_get(packet->u.client_statsreq.key_count); for (i = 0, name_off = sizeof(t_client_statsreq); i < name_count && (name = packet_get_str_const(packet, name_off, UNCHECKED_NAME_STR)); i++, name_off += std::strlen(name) + 1); if (i < name_count) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STATSREQ packet (only {} names of {})", conn_get_socket(c), i, name_count); return -1; } keys_off = name_off; if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_statsreply)); packet_set_type(rpacket, SERVER_STATSREPLY); bn_int_set(&rpacket->u.server_statsreply.name_count, name_count); bn_int_set(&rpacket->u.server_statsreply.key_count, key_count); bn_int_set(&rpacket->u.server_statsreply.requestid, bn_int_get(packet->u.client_statsreq.requestid)); myacc = conn_get_account(c); for (i = 0, name_off = sizeof(t_client_statsreq); i < name_count && (name = packet_get_str_const(packet, name_off, UNCHECKED_NAME_STR)); i++, name_off += std::strlen(name) + 1) { reqacc = accountlist_find_account(name); if (!reqacc) reqacc = myacc; for (j = 0, key_off = keys_off; j < key_count && (key = packet_get_str_const(packet, key_off, MAX_ATTRKEY_STR)); j++, key_off += std::strlen(key) + 1) { if (*key == '\0') continue; packet_append_string(rpacket, _attribute_req(reqacc, myacc, key)); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_loginreq1(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_loginreq1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LOGINREQ1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_loginreq1), packet_get_size(packet)); return -1; } { char const *username; t_account *account; if (!(username = packet_get_str_const(packet, sizeof(t_client_loginreq1), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LOGINREQ1 (missing or too long username)", conn_get_socket(c)); return -1; } t_packet* const rpacket = packet_create(packet_class_bnet); if (!rpacket) { return -1; } packet_set_size(rpacket, sizeof(t_server_loginreply1)); packet_set_type(rpacket, SERVER_LOGINREPLY1); // too many logins? [added by NonReal] if (prefs_get_max_concurrent_logins() > 0) { if (prefs_get_max_concurrent_logins() <= connlist_login_get_length()) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] login denied, too many concurrent logins. max: {}. current: {}.", conn_get_socket(c), prefs_get_max_concurrent_logins(), connlist_login_get_length()); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return -1; } } /* fail if no account */ if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (no such account)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); } else /* already logged in */ if (connlist_find_connection_by_account(account) && prefs_get_kick_old_login() == 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (already logged in)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); } else if (account_get_auth_bnetlogin(account) == 0) { /* default to true */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (no bnet access)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); } else if (account_get_auth_lock(account) == 1) { /* default to false */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (this account is locked)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); } else if (conn_get_sessionkey(c) != bn_int_get(packet->u.client_loginreq1.sessionkey)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] login for \"{}\" refused (expected session key 0x{:08x}, got 0x{:08x})", conn_get_socket(c), username, conn_get_sessionkey(c), bn_int_get(packet->u.client_loginreq1.sessionkey)); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); } else { struct { bn_int ticks; bn_int sessionkey; bn_int passhash1[5]; } temp; char const *oldstrhash1; t_hash oldpasshash1; t_hash oldpasshash2; t_hash trypasshash2; if ((oldstrhash1 = account_get_pass(account))) { bn_int_set(&temp.ticks, bn_int_get(packet->u.client_loginreq1.ticks)); bn_int_set(&temp.sessionkey, bn_int_get(packet->u.client_loginreq1.sessionkey)); if (hash_set_str(&oldpasshash1, oldstrhash1) < 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (corrupted passhash1?)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); } else { hash_to_bnhash((t_hash const *)&oldpasshash1, temp.passhash1); /* avoid warning */ bnet_hash(&oldpasshash2, sizeof(temp), &temp); /* do the double hash */ bnhash_to_hash(packet->u.client_loginreq1.password_hash2, &trypasshash2); if (hash_eq(trypasshash2, oldpasshash2) == 1) { conn_login(c, account, username); eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" logged in (correct password)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_SUCCESS); #ifdef WITH_LUA if (lua_handle_user(c, NULL, NULL, luaevent_user_login) == 1) { // feature to break login from Lua conn_set_state(c, conn_state_destroy); return -1; } #endif #ifdef WIN32_GUI guiOnUpdateUserList(); #endif } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (wrong password)", conn_get_socket(c), username); conn_increment_passfail_count(c); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_FAIL); } } } else { conn_login(c, account, username); eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" logged in (no password)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY1_MESSAGE_SUCCESS); #ifdef WIN32_GUI guiOnUpdateUserList(); #endif } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } void client_init_email(t_connection * c, t_account * account) { t_packet *packet; char const *email; if (!c || !account) return; if (!(email = account_get_email(account))) { if ((packet = packet_create(packet_class_bnet))) { packet_set_size(packet, sizeof(t_server_setemailreq)); packet_set_type(packet, SERVER_SETEMAILREQ); conn_push_outqueue(c, packet); packet_del_ref(packet); } } return; } static int _client_loginreq2(t_connection * c, t_packet const *const packet) { t_packet *rpacket; int success = 0; if (packet_get_size(packet) < sizeof(t_client_loginreq2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LOGINREQ2 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_loginreq2), packet_get_size(packet)); return -1; } { char const *username; t_account *account; char supports_locked_reply = 0; t_clienttag clienttag = conn_get_clienttag(c); if (clienttag == CLIENTTAG_STARCRAFT_UINT || clienttag == CLIENTTAG_BROODWARS_UINT || clienttag == CLIENTTAG_SHAREWARE_UINT || clienttag == CLIENTTAG_DIABLORTL_UINT || clienttag == CLIENTTAG_DIABLOSHR_UINT || clienttag == CLIENTTAG_WARCIIBNE_UINT || clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_STARJAPAN_UINT || clienttag == CLIENTTAG_DIABLO2ST_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT || clienttag == CLIENTTAG_WARCRAFT3_UINT || clienttag == CLIENTTAG_WAR3XP_UINT ) { if (conn_get_versionid(c) >= 0x0000000b) supports_locked_reply = 1; } if (!(username = packet_get_str_const(packet, sizeof(t_client_loginreq2), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LOGINREQ2 (missing or too long username)", conn_get_socket(c)); return -1; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_loginreply2)); packet_set_type(rpacket, SERVER_LOGINREPLY2); // too many logins? [added by NonReal] if (prefs_get_max_concurrent_logins() > 0) { if (prefs_get_max_concurrent_logins() <= connlist_login_get_length()) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] login denied, too many concurrent logins. max: {}. current: {}.", conn_get_socket(c), prefs_get_max_concurrent_logins(), connlist_login_get_length()); if (supports_locked_reply) { bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_LOCKED); packet_append_string(rpacket, "Too many concurrent logins. Try again later."); } else { bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_BADPASS); } packet_del_ref(rpacket); return -1; } } /* fail if no account */ if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (no such account)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_NONEXIST); } /* already logged in */ else if (connlist_find_connection_by_account(account) && prefs_get_kick_old_login() == 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (already logged in)", conn_get_socket(c), username); if (supports_locked_reply) { bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY2_MESSAGE_LOCKED); packet_append_string(rpacket, "This account is already logged in."); } else { bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY2_MESSAGE_BADPASS); } } else if (account_get_auth_bnetlogin(account) == 0) { /* default to true */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (no bnet access)", conn_get_socket(c), username); if (supports_locked_reply) { bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY2_MESSAGE_LOCKED); packet_append_string(rpacket, "This account is barred from bnet access."); } else { bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY2_MESSAGE_BADPASS); } } else if (account_get_auth_lock(account) == 1) { /* default to false */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (this account is locked)", conn_get_socket(c), username); if (supports_locked_reply) { bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY2_MESSAGE_LOCKED); std::string msgtemp = localize(c, "This account has been locked"); msgtemp += account_get_locktext(c, account, true); packet_append_string(rpacket, msgtemp.c_str()); } else { bn_int_set(&rpacket->u.server_loginreply1.message, SERVER_LOGINREPLY2_MESSAGE_BADPASS); } } else if (conn_get_sessionkey(c) != bn_int_get(packet->u.client_loginreq2.sessionkey)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] login for \"{}\" refused (expected session key 0x{:08x}, got 0x{:08x})", conn_get_socket(c), username, conn_get_sessionkey(c), bn_int_get(packet->u.client_loginreq2.sessionkey)); bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_BADPASS); } else { struct { bn_int ticks; bn_int sessionkey; bn_int passhash1[5]; } temp; char const *oldstrhash1; t_hash oldpasshash1; t_hash oldpasshash2; t_hash trypasshash2; if ((oldstrhash1 = account_get_pass(account))) { bn_int_set(&temp.ticks, bn_int_get(packet->u.client_loginreq2.ticks)); bn_int_set(&temp.sessionkey, bn_int_get(packet->u.client_loginreq2.sessionkey)); if (hash_set_str(&oldpasshash1, oldstrhash1) < 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (corrupted passhash1?)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_BADPASS); } else { hash_to_bnhash((t_hash const *)&oldpasshash1, temp.passhash1); /* avoid warning */ bnet_hash(&oldpasshash2, sizeof(temp), &temp); /* do the double hash */ bnhash_to_hash(packet->u.client_loginreq2.password_hash2, &trypasshash2); if (hash_eq(trypasshash2, oldpasshash2) == 1) { conn_login(c, account, username); eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" logged in (correct password)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_SUCCESS); success = 1; } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (wrong password)", conn_get_socket(c), username); conn_increment_passfail_count(c); bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_BADPASS); } } } else { conn_login(c, account, username); eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" logged in (no password)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply2.message, SERVER_LOGINREPLY2_MESSAGE_SUCCESS); success = 1; } } if (success && account) { #ifdef WITH_LUA if (lua_handle_user(c, NULL, NULL, luaevent_user_login) == 1) { // feature to break login from Lua conn_set_state(c, conn_state_destroy); packet_del_ref(rpacket); return -1; } #endif #ifdef WIN32_GUI guiOnUpdateUserList(); #endif client_init_email(c, account); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_loginreqw3(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_loginreq_w3)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_LOGINREQ_W3 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_loginreq_w3), packet_get_size(packet)); return -1; } { char const *username; t_account *account; char const *account_salt; char const *account_verifier; const char *conn_client_public_key; int i; /* PELISH: Does not need to check conn_client_public_key != NULL because we testing packet size */ conn_client_public_key = (char *)packet_get_data_const(packet, offsetof(t_client_loginreq_w3, client_public_key), 32); if (!(username = packet_get_str_const(packet, sizeof(t_client_loginreq_w3), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_LOGINREQ_W3 (missing or too long username)", conn_get_socket(c)); return -1; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_loginreply_w3)); packet_set_type(rpacket, SERVER_LOGINREPLY_W3); for (i = 0; i < 32; i++) bn_byte_set(&rpacket->u.server_loginreply_w3.salt[i], 0); for (i = 0; i < 32; i++) bn_byte_set(&rpacket->u.server_loginreply_w3.server_public_key[i], 0); { /* too many logins? */ if (prefs_get_max_concurrent_logins() > 0 && prefs_get_max_concurrent_logins() <= connlist_login_get_length()) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] login denied, too many concurrent logins. max: {}. current: {}.", conn_get_socket(c), prefs_get_max_concurrent_logins(), connlist_login_get_length()); bn_int_set(&rpacket->u.server_loginreply_w3.message, SERVER_LOGINREPLY_W3_MESSAGE_BADACCT); } else /* fail if no account */ if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) login for \"{}\" refused (no such account)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply_w3.message, SERVER_LOGINREPLY_W3_MESSAGE_BADACCT); } else /* already logged in */ if (connlist_find_connection_by_account(account) && prefs_get_kick_old_login() == 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) login for \"{}\" refused (already logged in)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply_w3.message, SERVER_LOGINREPLY_W3_MESSAGE_ALREADY); } else if (account_get_auth_bnetlogin(account) == 0) { /* default to true */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) login for \"{}\" refused (no bnet access)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_loginreply_w3.message, SERVER_LOGINREPLY_W3_MESSAGE_BADACCT); } else if (!(account_salt = account_get_salt(account))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) \"{}\" passed account check (even though account has no salt)", conn_get_socket(c), username); conn_set_loggeduser(c, username); bn_int_set(&rpacket->u.server_loginreply_w3.message, SERVER_LOGINREPLY_W3_MESSAGE_SUCCESS); } else if (!(account_verifier = account_get_verifier(account))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) \"{}\" passed account check (even though account has no verifier)", conn_get_socket(c), username); conn_set_loggeduser(c, username); bn_int_set(&rpacket->u.server_loginreply_w3.message, SERVER_LOGINREPLY_W3_MESSAGE_SUCCESS); xfree((void*)account_salt); } else { for (i = 0; i < 32; i++){ bn_byte_set(&rpacket->u.server_loginreply_w3.salt[i], account_salt[i]); } BigInt salt = BigInt((unsigned char*)account_salt, 32, 4, false); BigInt verifier = BigInt((unsigned char*)account_verifier, 32, 1, false); BnetSRP3 srp3 = BnetSRP3(username, salt); BigInt client_public_key = BigInt((unsigned char*)conn_client_public_key, 32, 1, false); BigInt server_public_key = srp3.getServerSessionPublicKey(verifier); server_public_key.getData((unsigned char*)&rpacket->u.server_loginreply_w3.server_public_key, 32, 4, false); BigInt hashed_server_secret_ = srp3.getHashedServerSecret(client_public_key, verifier); BigInt client_proof = srp3.getClientPasswordProof(client_public_key, server_public_key, hashed_server_secret_); BigInt server_proof = srp3.getServerPasswordProof(client_public_key, client_proof, hashed_server_secret_); char * conn_client_proof = (char*)client_proof.getData(20, 4, false); char * conn_server_proof = (char*)server_proof.getData(20, 4, false); conn_set_client_proof(c, conn_client_proof); conn_set_server_proof(c, conn_server_proof); xfree((void*)account_verifier); xfree((void*)account_salt); xfree(conn_client_proof); xfree(conn_server_proof); eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) \"{}\" passed account check", conn_get_socket(c), username); conn_set_loggeduser(c, username); bn_int_set(&rpacket->u.server_loginreply_w3.message, SERVER_LOGINREPLY_W3_MESSAGE_SUCCESS); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_passchangereq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_passchangereq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_PASSCHANGEREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_passchangereq), packet_get_size(packet)); return -1; } { char const *username; t_account *account; char const *account_salt; char const *account_verifier; const char *conn_client_public_key; int i; /* PELISH: Does not need to check conn_client_public_key != NULL because we testing packet size */ conn_client_public_key = (char *)packet_get_data_const(packet, offsetof(t_client_loginreq_w3, client_public_key), 32); if (!(username = packet_get_str_const(packet, sizeof(t_client_passchangereq), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_PASSCHANGEREQ (missing or too long username)", conn_get_socket(c)); return -1; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_passchangereply)); packet_set_type(rpacket, SERVER_PASSCHANGEREPLY); for (i = 0; i < 32; i++) bn_byte_set(&rpacket->u.server_loginreply_w3.salt[i], 0); for (i = 0; i < 32; i++) bn_byte_set(&rpacket->u.server_loginreply_w3.server_public_key[i], 0); { bn_int_set(&rpacket->u.server_passchangereply.message, SERVER_PASSCHANGEREPLY_MESSAGE_REJECT); /* fail if no account */ if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) passchange for \"{}\" refused (no such account)", conn_get_socket(c), username); } else /* fail if bnetlogin is disallowed */ if (account_get_auth_bnetlogin(account) == 0) { /* default to true */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) passchange for \"{}\" refused (no bnet access)", conn_get_socket(c), username); } else /* fail if no salt */ if ((account_salt = account_get_salt(account)) == NULL) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) passchange for \"{}\" refused (no SALT)", conn_get_socket(c), username); } else /* fail if no verifier */ if ((account_verifier = account_get_verifier(account)) == NULL) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) passchange for \"{}\" refused (no VERIFIER)", conn_get_socket(c), username); xfree((void*)account_salt); } else { for (i = 0; i < 32; i++){ bn_byte_set(&rpacket->u.server_passchangereply.salt[i], account_salt[i]); } BigInt salt = BigInt((unsigned char*)account_salt, 32, 4, false); BigInt verifier = BigInt((unsigned char*)account_verifier, 32, 1, false); BnetSRP3 srp3 = BnetSRP3(username, salt); BigInt client_public_key = BigInt((unsigned char*)conn_client_public_key, 32, 1, false); BigInt server_public_key = srp3.getServerSessionPublicKey(verifier); server_public_key.getData((unsigned char*)&rpacket->u.server_loginreply_w3.server_public_key, 32, 4, false); BigInt hashed_server_secret_ = srp3.getHashedServerSecret(client_public_key, verifier); BigInt client_proof = srp3.getClientPasswordProof(client_public_key, server_public_key, hashed_server_secret_); BigInt server_proof = srp3.getServerPasswordProof(client_public_key, client_proof, hashed_server_secret_); char * conn_client_proof = (char*)client_proof.getData(20, 4, false); char * conn_server_proof = (char*)server_proof.getData(20, 4, false); conn_set_client_proof(c, conn_client_proof); conn_set_server_proof(c, conn_server_proof); xfree((void*)account_verifier); xfree((void*)account_salt); xfree(conn_client_proof); xfree(conn_server_proof); eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) \"{}\" passed account passchange check", conn_get_socket(c), username); conn_set_loggeduser(c, username); bn_int_set(&rpacket->u.server_passchangereply.message, SERVER_PASSCHANGEREPLY_MESSAGE_ACCEPT); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_passchangeproofreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_passchangeproofreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PASSCHANGEPROOFREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_passchangeproofreq), packet_get_size(packet)); return -1; } { char const *username; t_account *account; const char *salt; const char *verifier; eventlog(eventlog_level_info, __FUNCTION__, "[{}] passchange proof requested", conn_get_socket(c)); if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_passchangeproofreply)); packet_set_type(rpacket, SERVER_PASSCHANGEPROOFREPLY); std::memset(&rpacket->u.server_passchangeproofreply.server_password_proof, 0, sizeof(rpacket->u.server_passchangeproofreply.server_password_proof)); bn_int_set(&rpacket->u.server_logonproofreply.response, SERVER_PASSCHANGEPROOFREPLY_RESPONSE_BADPASS); if (!(username = conn_get_loggeduser(c))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) got NULL username, 0x56ff before 0x55ff?", conn_get_socket(c)); } else if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) passchange in 0x56ff for \"{}\" refused (no such account)", conn_get_socket(c), username); } else if (account_get_auth_lock(account) == 1) { /* default to false */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] passchange for \"{}\" refused (this account is locked)", conn_get_socket(c), username); } else { int i; const char * client_password_proof; if (!(client_password_proof = (const char*)packet_get_data_const(packet, offsetof(t_client_passchangeproofreq, client_password_proof), 20))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] (W3) got bad PASSCHANGEPROOFREQ packet (missing hash)", conn_get_socket(c)); packet_del_ref(rpacket); return -1; } if (std::memcmp(client_password_proof, conn_get_client_proof(c), 20) == 0) { const char * server_proof = conn_get_server_proof(c); for (i = 0; i < 20; i++){ bn_byte_set(&rpacket->u.server_passchangeproofreply.server_password_proof[i], server_proof[i]); } salt = (const char *)packet_get_data_const(packet, offsetof(t_client_passchangeproofreq, salt), 32); verifier = (const char *)packet_get_data_const(packet, offsetof(t_client_passchangeproofreq, password_verifier), 32); account_set_salt(account, salt); account_set_verifier(account, verifier); eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) \"{}\" successful passchange (right client password proof)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_passchangeproofreply.response, SERVER_PASSCHANGEPROOFREPLY_RESPONSE_OK); } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) got wrong client password proof for \"{}\"", conn_get_socket(c), username); conn_increment_passfail_count(c); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); conn_set_client_proof(c, NULL); conn_set_server_proof(c, NULL); } return 0; } static int _client_pingreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_pingreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PINGREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_pingreq), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_pingreply)); packet_set_type(rpacket, SERVER_PINGREPLY); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_logonproofreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_logonproofreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LOGONPROOFREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_logonproofreq), packet_get_size(packet)); return -1; } { char const *username; t_account *account; eventlog(eventlog_level_info, __FUNCTION__, "[{}] logon proof requested", conn_get_socket(c)); if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_logonproofreply)); packet_set_type(rpacket, SERVER_LOGONPROOFREPLY); std::memset(&rpacket->u.server_logonproofreply.server_password_proof, 0, sizeof(rpacket->u.server_logonproofreply.server_password_proof)); bn_int_set(&rpacket->u.server_logonproofreply.response, SERVER_LOGONPROOFREPLY_RESPONSE_BADPASS); if (!(username = conn_get_loggeduser(c))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) got NULL username, 0x54ff before 0x53ff?", conn_get_socket(c)); } else if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) login in 0x54ff for \"{}\" refused (no such account)", conn_get_socket(c), username); } else if (account_get_auth_lock(account) == 1) { /* default to false */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] login for \"{}\" refused (this account is locked)", conn_get_socket(c), username); bn_int_set(&rpacket->u.server_logonproofreply.response, SERVER_LOGONPROOFREPLY_RESPONSE_CUSTOM); std::string msgtemp = localize(c, "This account has been locked"); msgtemp += account_get_locktext(c, account, true); packet_append_string(rpacket, msgtemp.c_str()); } else { t_hash serverhash; t_hash clienthash; int i; const char * client_password_proof; /* PELISH: This can not occur - We already tested packet size which must be wrong firstly. Also pvpgn will crash when will dereferencing NULL pointer (so we cant got this errorlog message) I vote for deleting this "if" */ if (!(client_password_proof = (const char*)packet_get_data_const(packet, offsetof(t_client_logonproofreq, client_password_proof), 20))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] (W3) got bad LOGONPROOFREQ packet (missing hash)", conn_get_socket(c)); packet_del_ref(rpacket); return -1; } clienthash[0] = bn_int_get(packet->u.client_logonproofreq.client_password_proof[0]); clienthash[1] = bn_int_get(packet->u.client_logonproofreq.client_password_proof[4]); clienthash[2] = bn_int_get(packet->u.client_logonproofreq.client_password_proof[8]); clienthash[3] = bn_int_get(packet->u.client_logonproofreq.client_password_proof[12]); clienthash[4] = bn_int_get(packet->u.client_logonproofreq.client_password_proof[16]); hash_set_str(&serverhash, account_get_pass(account)); if (conn_get_client_proof(c) && std::memcmp(client_password_proof, conn_get_client_proof(c), 20) == 0) { const char * server_proof = conn_get_server_proof(c); for (i = 0; i < 20; i++){ bn_byte_set(&rpacket->u.server_logonproofreply.server_password_proof[i], server_proof[i]); } conn_login(c, account, username); eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) \"{}\" logged in (right client password proof)", conn_get_socket(c), username); if ((conn_get_versionid(c) >= 0x0000000D) && (account_get_email(account) == NULL)) bn_int_set(&rpacket->u.server_logonproofreply.response, SERVER_LOGONPROOFREPLY_RESPONSE_EMAIL); else bn_int_set(&rpacket->u.server_logonproofreply.response, SERVER_LOGONPROOFREPLY_RESPONSE_OK); // by amadeo updates the userlist #ifdef WIN32_GUI guiOnUpdateUserList(); #endif } else if (hash_eq(clienthash, serverhash)) { conn_login(c, account, username); eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) \"{}\" logged in (right password)", conn_get_socket(c), username); if ((conn_get_versionid(c) >= 0x0000000D) && (account_get_email(account) == NULL)) bn_int_set(&rpacket->u.server_logonproofreply.response, SERVER_LOGONPROOFREPLY_RESPONSE_EMAIL); else bn_int_set(&rpacket->u.server_logonproofreply.response, SERVER_LOGONPROOFREPLY_RESPONSE_OK); #ifdef WITH_LUA if (lua_handle_user(c, NULL, NULL, luaevent_user_login) == 1) { // feature to break login from Lua conn_set_state(c, conn_state_destroy); packet_del_ref(rpacket); return -1; } #endif // by amadeo updates the userlist #ifdef WIN32_GUI guiOnUpdateUserList(); #endif } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] (W3) got wrong client password proof for \"{}\"", conn_get_socket(c), username); conn_increment_passfail_count(c); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); conn_set_client_proof(c, NULL); conn_set_server_proof(c, NULL); } clan_send_status_window(c); return 0; } static int _client_changegameport(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_changegameport)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad changegameport packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_changegameport), packet_get_size(packet)); return -1; } { unsigned short port = bn_short_get(packet->u.client_changegameport.port); if (port < 1024) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] invalid port in changegameport packet: {}", conn_get_socket(c), (int)port); return -1; } conn_set_game_port(c, port); } return 0; } static int _client_friendslistreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_friendslistreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad FRIENDSLISTREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_friendslistreq), packet_get_size(packet)); return -1; } { int frienduid; t_list *flist; t_friend *fr; t_account *account = conn_get_account(c); int i; int n = account_get_friendcount(account); int friendcount = 0; t_server_friendslistreply_status status; t_connection *dest_c; t_game *game; t_channel *channel; char stat; if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_friendslistreply)); packet_set_type(rpacket, SERVER_FRIENDSLISTREPLY); if ((flist = account_get_friends(account)) == NULL) { packet_del_ref(rpacket); return -1; } for (i = 0; i < n; i++) { frienduid = account_get_friend(account, i); if ((fr = friendlist_find_uid(flist, frienduid)) == NULL) continue; packet_append_string(rpacket, account_get_name(friend_get_account(fr))); game = NULL; channel = NULL; if (!(dest_c = connlist_find_connection_by_uid(frienduid))) { bn_byte_set(&status.location, FRIENDSTATUS_OFFLINE); bn_byte_set(&status.status, 0); bn_int_set(&status.clienttag, 0); } else { bn_int_set(&status.clienttag, conn_get_clienttag(dest_c)); stat = 0; if ((friend_get_mutual(fr))) stat |= FRIEND_TYPE_MUTUAL; if ((conn_get_dndstr(dest_c))) stat |= FRIEND_TYPE_DND; if ((conn_get_awaystr(dest_c))) stat |= FRIEND_TYPE_AWAY; bn_byte_set(&status.status, stat); if ((game = conn_get_game(dest_c))) { if (game_get_flag(game) != game_flag_private) bn_byte_set(&status.location, FRIENDSTATUS_PUBLIC_GAME); else bn_byte_set(&status.location, FRIENDSTATUS_PRIVATE_GAME); } else if ((channel = conn_get_channel(dest_c))) { bn_byte_set(&status.location, FRIENDSTATUS_CHAT); } else { bn_byte_set(&status.location, FRIENDSTATUS_ONLINE); } } packet_append_data(rpacket, &status, sizeof(status)); if (game) packet_append_string(rpacket, game_get_name(game)); else if (channel) packet_append_string(rpacket, channel_get_name(channel)); else packet_append_string(rpacket, ""); friendcount++; } bn_byte_set(&rpacket->u.server_friendslistreply.friendcount, friendcount); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_friendinforeq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_friendinforeq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad FRIENDINFOREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_friendinforeq), packet_get_size(packet)); return -1; } { t_connection const *dest_c; t_game const *game; t_channel const *channel; t_account *account = conn_get_account(c); int frienduid; t_friend *fr; t_list *flist; int n = account_get_friendcount(account); char type; if (n == 0) return 0; if (bn_byte_get(packet->u.client_friendinforeq.friendnum) > n) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] bad friend number in FRIENDINFOREQ packet", conn_get_socket(c)); return -1; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_friendinforeply)); packet_set_type(rpacket, SERVER_FRIENDINFOREPLY); frienduid = account_get_friend(account, bn_byte_get(packet->u.client_friendinforeq.friendnum)); if (frienduid < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] friend number {} not found", conn_get_socket(c), (int)bn_byte_get(packet->u.client_friendinforeq.friendnum)); return -1; } bn_byte_set(&rpacket->u.server_friendinforeply.friendnum, bn_byte_get(packet->u.client_friendinforeq.friendnum)); flist = account_get_friends(account); fr = friendlist_find_uid(flist, frienduid); if (fr == NULL || (dest_c = connlist_find_connection_by_account(friend_get_account(fr))) == NULL) { bn_byte_set(&rpacket->u.server_friendinforeply.type, FRIEND_TYPE_NON_MUTUAL); bn_byte_set(&rpacket->u.server_friendinforeply.status, FRIENDSTATUS_OFFLINE); bn_int_set(&rpacket->u.server_friendinforeply.clienttag, 0); packet_append_string(rpacket, ""); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } type = FRIEND_TYPE_NON_MUTUAL; if (friend_get_mutual(fr)) type |= FRIEND_TYPE_MUTUAL; if ((conn_get_dndstr(dest_c))) type |= FRIEND_TYPE_DND; if ((conn_get_awaystr(dest_c))) type |= FRIEND_TYPE_AWAY; bn_byte_set(&rpacket->u.server_friendinforeply.type, type); if ((game = conn_get_game(dest_c))) { if (game_get_flag(game) != game_flag_private) bn_byte_set(&rpacket->u.server_friendinforeply.status, FRIENDSTATUS_PUBLIC_GAME); else bn_byte_set(&rpacket->u.server_friendinforeply.status, FRIENDSTATUS_PRIVATE_GAME); packet_append_string(rpacket, game_get_name(game)); } else if ((channel = conn_get_channel(dest_c))) { bn_byte_set(&rpacket->u.server_friendinforeply.status, FRIENDSTATUS_CHAT); packet_append_string(rpacket, channel_get_name(channel)); } else { bn_byte_set(&rpacket->u.server_friendinforeply.status, FRIENDSTATUS_ONLINE); packet_append_string(rpacket, ""); } bn_int_set(&rpacket->u.server_friendinforeply.clienttag, conn_get_clienttag(dest_c)); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_atfriendscreen(t_connection * c, t_packet const *const packet) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got CLIENT_ARRANGEDTEAM_FRIENDSCREEN packet", conn_get_socket(c)); const char *my_username = conn_get_username(c); if (!my_username) { return -1; } t_channel *my_channel = conn_get_channel(c); int my_channel_is_public = 1; if (my_channel) { my_channel_is_public = channel_get_flags(my_channel) & channel_flags_public; } t_packet *rpacket = packet_create(packet_class_bnet); if (!rpacket) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create friendscreen server packet", conn_get_socket(c)); return -1; } packet_set_size(rpacket, sizeof(t_server_arrangedteam_friendscreen)); packet_set_type(rpacket, SERVER_ARRANGEDTEAM_FRIENDSCREEN); std::uint8_t available_players = 0; // begin search for mutual and available friends t_list *my_friend_list = account_get_friends(conn_get_account(c)); t_elem *entry; LIST_TRAVERSE(my_friend_list, entry) { t_friend *_friend = static_cast(elem_get_data(entry)); if (!_friend) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if (available_players == std::numeric_limits::max()) { eventlog(eventlog_level_info, __FUNCTION__, "Reached maximum amount of available players to send in packet"); break; } // skip non-mutual friends if (friend_get_mutual(_friend) == FRIEND_NOTMUTUAL) { continue; } t_account *friend_account = friend_get_account(_friend); t_connection *friend_connection = connlist_find_connection_by_account(friend_account); // if user is offline, then continue to next friend if (!friend_connection) { continue; } const std::string my_version_tag = conn_get_versioncheck(c) ? conn_get_versioncheck(c)->get_version_tag() : ""; const std::string friend_version_tag = conn_get_versioncheck(friend_connection) ? conn_get_versioncheck(friend_connection)->get_version_tag() : ""; // friend is using another game or is on a different version if (my_version_tag != friend_version_tag) { continue; } // friend has Do Not Disturb mode enabled if (conn_get_dndstr(friend_connection)) { continue; } // friend is away if (conn_get_awaystr(friend_connection)) { continue; } // friend is not in a channel t_channel *friend_channel = conn_get_channel(friend_connection); if (!friend_channel) { continue; } // // don't list YET if in same private channel if (!my_channel_is_public && (friend_channel == my_channel)) { continue; } const char *friend_name = account_get_name(friend_account); eventlog(eventlog_level_trace, __FUNCTION__, "Friend {} is available for an AT Game.", friend_name); available_players += 1; packet_append_string(rpacket, friend_name); } // now list matching users in same private chan if (!my_channel_is_public) { for (t_connection *user_connection = channel_get_first(my_channel); user_connection; user_connection = channel_get_next()) { if (available_players == std::numeric_limits::max()) { eventlog(eventlog_level_info, __FUNCTION__, "Reached maximum amount of available players to send in packet"); break; } // skip self if (user_connection == c) { continue; } const std::string my_version_tag = conn_get_versioncheck(c) ? conn_get_versioncheck(c)->get_version_tag() : ""; const std::string user_version_tag = conn_get_versioncheck(user_connection) ? conn_get_versioncheck(user_connection)->get_version_tag() : ""; // user is using another game or is on a different version if (my_version_tag != user_version_tag) { continue; } // user is dnd if (conn_get_dndstr(user_connection)) { continue; } // user is away if (conn_get_awaystr(user_connection)) { continue; } const char *username = account_get_name(conn_get_account(user_connection)); if (!username) { continue; } eventlog(eventlog_level_trace, __FUNCTION__, "user {} in private channel {} is available for an AT Game.", username, channel_get_name(my_channel)); available_players += 1; packet_append_string(rpacket, username); } } if (available_players == 0) { eventlog(eventlog_level_info, __FUNCTION__, "No available players"); } bn_byte_set(&rpacket->u.server_arrangedteam_friendscreen.f_count, available_players); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_atinvitefriend(t_connection * c, t_packet const *const packet) { t_packet *rpacket; t_clienttag ctag; if (packet_get_size(packet) < sizeof(t_client_arrangedteam_invite_friend)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ARRANGEDTEAM_INVITE_FRIEND packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_arrangedteam_invite_friend), packet_get_size(packet)); return -1; } ctag = conn_get_clienttag(c); { int count_to_invite, count, id; char const *invited_usernames[8]; t_account *members[MAX_TEAMSIZE]; int i, n, offset, teammemcount; t_connection *dest_c; t_team *team; unsigned int teamid; count_to_invite = bn_byte_get(packet->u.client_arrangedteam_invite_friend.numfriends); count = bn_int_get(packet->u.client_arrangedteam_invite_friend.count); id = bn_int_get(packet->u.client_arrangedteam_invite_friend.id); teammemcount = count_to_invite + 1; if ((count_to_invite < 1) || (count_to_invite > 3)) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid number of users to invite to game"); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] got ARRANGEDTEAM INVITE packet for {} invitees", conn_get_socket(c), count_to_invite); offset = sizeof(t_client_arrangedteam_invite_friend); for (i = 0; i < count_to_invite; i++) { if (!(invited_usernames[i] = packet_get_str_const(packet, offset, MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, "handle_bnet", "Could not get username from invite packet"); return -1; } else { offset += std::strlen(invited_usernames[i]) + 1; eventlog(eventlog_level_debug, "handle_bnet", "Added user {} to invite array.", invited_usernames[i]); } } members[0] = conn_get_account(c); for (i = 1; i < MAX_TEAMSIZE; i++) { if ((i < teammemcount)) { if (!(members[i] = accountlist_find_account(invited_usernames[i - 1]))) { eventlog(eventlog_level_error, __FUNCTION__, "got invitation for non-existant user \"{}\"", invited_usernames[i - 1]); return -1; } } else members[i] = NULL; } if (!(team = account_find_team_by_accounts(members[0], members, ctag))) { team = create_team(members, ctag); //no need to free on return -1 because it's already in teamlist eventlog(eventlog_level_trace, __FUNCTION__, "this team has never played before, creating new team"); } else { eventlog(eventlog_level_trace, __FUNCTION__, "this team has already played before"); } teamid = team_get_teamid(team); account_set_currentatteam(conn_get_account(c), team_get_teamid(team)); //Create the packet to send to each of the users you wanted to invite conn_part_channel(c); for (i = 0; i < teammemcount; i++) { if (!(dest_c = account_get_conn(team_get_member(team, i)))) continue; if ((dest_c == c)) continue; if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_arrangedteam_send_invite)); packet_set_type(rpacket, SERVER_ARRANGEDTEAM_SEND_INVITE); bn_int_set(&rpacket->u.server_arrangedteam_send_invite.count, count); bn_int_set(&rpacket->u.server_arrangedteam_send_invite.id, id); { /* trans support */ unsigned short port = conn_get_game_port(c); unsigned int addr = conn_get_addr(c); trans_net(conn_get_addr(dest_c), &addr, &port); bn_int_nset(&rpacket->u.server_arrangedteam_send_invite.inviterip, addr); bn_short_set(&rpacket->u.server_arrangedteam_send_invite.port, port); } bn_byte_set(&rpacket->u.server_arrangedteam_send_invite.numfriends, count_to_invite); for (n = 0; n < teammemcount; n++) { if (n != i) packet_append_string(rpacket, account_get_name(team_get_member(team, n))); } //now send packet conn_push_outqueue(dest_c, rpacket); packet_del_ref(rpacket); account_set_currentatteam(conn_get_account(dest_c), teamid); } //now send a ACK to the inviter if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_arrangedteam_invite_friend_ack)); packet_set_type(rpacket, SERVER_ARRANGEDTEAM_INVITE_FRIEND_ACK); bn_int_set(&rpacket->u.server_arrangedteam_invite_friend_ack.count, count); bn_int_set(&rpacket->u.server_arrangedteam_invite_friend_ack.id, id); bn_int_set(&rpacket->u.server_arrangedteam_invite_friend_ack.timestamp, now); bn_byte_set(&rpacket->u.server_arrangedteam_invite_friend_ack.teamsize, count_to_invite + 1); /* * five int's to fill * fill with uid's of all teammembers, including the inviter * and the rest with FFFFFFFF * to be used when sever recieves anongame search * [Omega] */ for (i = 0; i < 5; i++) { if (i < teammemcount) { bn_int_set(&rpacket->u.server_arrangedteam_invite_friend_ack.info[i], team_get_memberuid(team, i)); } else { /* fill rest with FFFFFFFF */ bn_int_set(&rpacket->u.server_arrangedteam_invite_friend_ack.info[i], 0xFFFFFFFF); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_atacceptdeclineinvite(t_connection * c, t_packet const *const packet) { t_packet *rpacket; t_clienttag ctag; if (packet_get_size(packet) < sizeof(t_client_arrangedteam_accept_decline_invite)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ARRANGEDTEAM_ACCEPT_DECLINE_INVITE packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_arrangedteam_accept_decline_invite), packet_get_size(packet)); return -1; } ctag = conn_get_clienttag(c); { char const *inviter; t_connection *dest_c; //if user declined the invitation then if (bn_int_get(packet->u.client_arrangedteam_accept_decline_invite.option) == CLIENT_ARRANGEDTEAM_DECLINE) { inviter = packet_get_str_const(packet, sizeof(t_client_arrangedteam_accept_decline_invite), MAX_USERNAME_LEN); dest_c = connlist_find_connection_by_accountname(inviter); eventlog(eventlog_level_info, "handle_bnet", "{} declined a arranged team game with {}", conn_get_username(c), inviter); if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_arrangedteam_member_decline)); packet_set_type(rpacket, SERVER_ARRANGEDTEAM_MEMBER_DECLINE); bn_int_set(&rpacket->u.server_arrangedteam_member_decline.count, bn_int_get(packet->u.client_arrangedteam_accept_decline_invite.count)); bn_int_set(&rpacket->u.server_arrangedteam_member_decline.action, SERVER_ARRANGEDTEAM_DECLINE); packet_append_string(rpacket, conn_get_username(c)); conn_push_outqueue(dest_c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_atacceptinvite(t_connection * c, t_packet const *const packet) { // t_packet * rpacket; if (packet_get_size(packet) < sizeof(t_client_arrangedteam_accept_invite)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ARRANGEDTEAM_ACCEPT_INVITE packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_arrangedteam_accept_invite), packet_get_size(packet)); return -1; } /* conn_set_channel(c, "Arranged Teams"); */ return 0; } typedef struct { t_connection *c; unsigned lnews; unsigned fnews; } t_motd_data; static int _news_cb(std::time_t date, t_lstr * lstr, void *data) { t_packet *rpacket; t_motd_data *motdd = (t_motd_data *)data; if (date < motdd->lnews) return -1; /* exit traversing */ rpacket = packet_create(packet_class_bnet); if (!rpacket) return -1; packet_set_size(rpacket, sizeof(t_server_motd_w3)); packet_set_type(rpacket, SERVER_MOTD_W3); bn_byte_set(&rpacket->u.server_motd_w3.msgtype, SERVER_MOTD_W3_MSGTYPE); bn_int_set(&rpacket->u.server_motd_w3.curr_time, now); bn_int_set(&rpacket->u.server_motd_w3.first_news_time, motdd->fnews); bn_int_set(&rpacket->u.server_motd_w3.timestamp, date); bn_int_set(&rpacket->u.server_motd_w3.timestamp2, date); /* Append news to packet, we used the already cached len in the lstr */ packet_append_lstr(rpacket, lstr); /* Send news packet */ conn_push_outqueue(motdd->c, rpacket); packet_del_ref(rpacket); return 0; } // motd for warcraft 3 (http://img21.imageshack.us/img21/1808/j2py.png) static int _client_motdw3(t_connection * c, t_packet const *const packet) { t_packet *rpacket; t_clienttag ctag; t_motd_data motdd; if (packet_get_size(packet) < sizeof(t_client_motd_w3)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_MOTD_W3 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_motd_w3), packet_get_size(packet)); return -1; } ctag = conn_get_clienttag(c); /* if in a game, remove user from his game */ if (conn_get_game(c) != NULL) conn_set_game(c, NULL, NULL, NULL, game_type_none, 0); /* News */ motdd.lnews = bn_int_get(packet->u.client_motd_w3.last_news_time); motdd.fnews = news_get_firstnews(); motdd.c = c; eventlog(eventlog_level_trace, __FUNCTION__, "lastnews() {} news_time {}", news_get_lastnews(), motdd.lnews); news_traverse(_news_cb, &motdd); /* Welcome Message */ rpacket = packet_create(packet_class_bnet); if (!rpacket) return -1; packet_set_size(rpacket, sizeof(t_server_motd_w3)); packet_set_type(rpacket, SERVER_MOTD_W3); //bn_int_set(&rpacket->u.server_motd_w3.ticks,get_ticks()); bn_byte_set(&rpacket->u.server_motd_w3.msgtype, SERVER_MOTD_W3_MSGTYPE); bn_int_set(&rpacket->u.server_motd_w3.curr_time, now); bn_int_set(&rpacket->u.server_motd_w3.first_news_time, motdd.fnews); bn_int_set(&rpacket->u.server_motd_w3.timestamp, motdd.fnews + 1); bn_int_set(&rpacket->u.server_motd_w3.timestamp2, SERVER_MOTD_W3_WELCOME); // read text from bnmotd_w3.txt { fmt::MemoryWriter serverinfo; std::string filename = i18n_filename(prefs_get_motdw3file(), conn_get_gamelang_localized(c)); std::FILE* fp = std::fopen(filename.c_str(), "r"); if (fp) { while (char* buff = file_get_line(fp)) { char* line = message_format_line(c, buff); serverinfo << (line + 1) << '\n'; xfree((void*)line); } if (std::fclose(fp) == EOF) { eventlog(eventlog_level_error, __FUNCTION__, "could not close motdw3 file \"{}\" after reading (std::fopen: {})", filename, std::strerror(errno)); } } packet_append_string(rpacket, serverinfo.c_str()); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_realmlistreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_realmlistreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad REALMLISTREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_realmlistreq), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { t_elem const *curr; t_realm const *realm; t_server_realmlistreply_data realmdata; unsigned int count; packet_set_size(rpacket, sizeof(t_server_realmlistreply)); packet_set_type(rpacket, SERVER_REALMLISTREPLY); bn_int_set(&rpacket->u.server_realmlistreply.unknown1, SERVER_REALMLISTREPLY_UNKNOWN1); count = 0; LIST_TRAVERSE_CONST(realmlist(), curr) { realm = (t_realm*)elem_get_data(curr); if (!realm_get_active(realm)) continue; bn_int_set(&realmdata.unknown3, SERVER_REALMLISTREPLY_DATA_UNKNOWN3); bn_int_set(&realmdata.unknown4, SERVER_REALMLISTREPLY_DATA_UNKNOWN4); bn_int_set(&realmdata.unknown5, SERVER_REALMLISTREPLY_DATA_UNKNOWN5); bn_int_set(&realmdata.unknown6, SERVER_REALMLISTREPLY_DATA_UNKNOWN6); bn_int_set(&realmdata.unknown7, SERVER_REALMLISTREPLY_DATA_UNKNOWN7); bn_int_set(&realmdata.unknown8, SERVER_REALMLISTREPLY_DATA_UNKNOWN8); bn_int_set(&realmdata.unknown9, SERVER_REALMLISTREPLY_DATA_UNKNOWN9); packet_append_data(rpacket, &realmdata, sizeof(realmdata)); packet_append_string(rpacket, realm_get_name(realm)); packet_append_string(rpacket, realm_get_description(realm)); count++; } bn_int_set(&rpacket->u.server_realmlistreply.count, count); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_realmlistreq110(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_realmlistreq_110)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad REALMLISTREQ_110 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_realmlistreq), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { t_elem const *curr; t_realm const *realm; t_server_realmlistreply_110_data realmdata; unsigned int count; packet_set_size(rpacket, sizeof(t_server_realmlistreply_110)); packet_set_type(rpacket, SERVER_REALMLISTREPLY_110); bn_int_set(&rpacket->u.server_realmlistreply_110.unknown1, SERVER_REALMLISTREPLY_110_UNKNOWN1); count = 0; LIST_TRAVERSE_CONST(realmlist(), curr) { realm = (t_realm*)elem_get_data(curr); if (!realm_get_active(realm)) continue; bn_int_set(&realmdata.unknown1, SERVER_REALMLISTREPLY_110_DATA_UNKNOWN1); packet_append_data(rpacket, &realmdata, sizeof(realmdata)); packet_append_string(rpacket, realm_get_name(realm)); packet_append_string(rpacket, realm_get_description(realm)); count++; } bn_int_set(&rpacket->u.server_realmlistreply_110.count, count); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_claninforeq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; int count; char const *username; t_account *account; t_clanmember *clanmember; t_clan *clan; t_clantag clantag1, clantag2; if (packet_get_size(packet) < sizeof(t_client_claninforeq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLANINFOREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_claninforeq), packet_get_size(packet)); return -1; } count = bn_int_get(packet->u.client_claninforeq.count); clantag1 = bn_int_get(packet->u.client_claninforeq.clantag); clan = NULL; if (!(username = packet_get_str_const(packet, sizeof(t_client_claninforeq), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLANINFOREQ (missing or too long username)", conn_get_socket(c)); return -1; } if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_error, __FUNCTION__, "requested claninfo for non-existant account"); return -1; } if ((clanmember = account_get_clanmember(account)) && (clan = clanmember_get_clan(clanmember))) clantag2 = clan_get_clantag(clan); else clantag2 = 0; if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_claninforeply)); packet_set_type(rpacket, SERVER_CLANINFOREPLY); bn_int_set(&rpacket->u.server_profilereply.count, count); if (clantag1 == clantag2) { t_bnettime bn_time; bn_long ltime; bn_byte_set(&rpacket->u.server_claninforeply.fail, 0); packet_append_string(rpacket, clan_get_name(clan)); char status = clanmember_get_status(clanmember); packet_append_data(rpacket, &status, 1); std::time_t temp = clanmember_get_join_time(clanmember); bn_time = time_to_bnettime(temp, 0); bn_time = bnettime_add_tzbias(bn_time, -conn_get_tzbias(c)); bnettime_to_bn_long(bn_time, <ime); packet_append_data(rpacket, <ime, 8); } else bn_byte_set(&rpacket->u.server_claninforeply.fail, 1); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_profilereq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; int count; char const *username; t_account *account; t_clanmember *clanmember; t_clan *clan; bn_int clanTAG; if (packet_get_size(packet) < sizeof(t_client_profilereq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PROFILEREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_profilereq), packet_get_size(packet)); return -1; } count = bn_int_get(packet->u.client_profilereq.count); if (!(username = packet_get_str_const(packet, sizeof(t_client_profilereq), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PROFILEREQ (missing or too long username)", conn_get_socket(c)); return -1; } if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_error, __FUNCTION__, "requested profile for non-existant account"); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_profilereply)); packet_set_type(rpacket, SERVER_PROFILEREPLY); bn_int_set(&rpacket->u.server_profilereply.count, count); bn_byte_set(&rpacket->u.server_profilereply.fail, 0); packet_append_string(rpacket, account_get_desc(account).c_str()); packet_append_string(rpacket, account_get_loc(account).c_str()); if ((clanmember = account_get_clanmember(account)) && (clan = clanmember_get_clan(clanmember))) bn_int_set(&clanTAG, clan_get_clantag(clan)); else bn_int_set(&clanTAG, 0); packet_append_data(rpacket, clanTAG, 4); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_realmjoinreq109(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_realmjoinreq_109)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad REALMJOINREQ_109 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_realmjoinreq_109), packet_get_size(packet)); return -1; } { char const *realmname; t_realm *realm; if (!(realmname = packet_get_str_const(packet, sizeof(t_client_realmjoinreq_109), MAX_REALMNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad REALMJOINREQ_109 (missing or too long realmname)", conn_get_socket(c)); return -1; } if ((realm = realmlist_find_realm(realmname))) { unsigned int salt; struct { bn_int salt; bn_int sessionkey; bn_int sessionnum; bn_int secret; bn_int passhash[5]; } temp; char const *pass_str; t_hash secret_hash; t_hash passhash; t_realm *prev_realm; /* FIXME: should we only set this after they log in to the realm server? */ prev_realm = conn_get_realm(c); if (prev_realm) { if (prev_realm != realm) { realm_add_player_number(realm, 1); realm_add_player_number(prev_realm, -1); conn_set_realm(c, realm); } } else { realm_add_player_number(realm, 1); conn_set_realm(c, realm); } if ((pass_str = account_get_pass(conn_get_account(c)))) { if (hash_set_str(&passhash, pass_str) == 0) { hash_to_bnhash((t_hash const *)&passhash, temp.passhash); salt = bn_int_get(packet->u.client_realmjoinreq_109.seqno); bn_int_set(&temp.salt, salt); bn_int_set(&temp.sessionkey, conn_get_sessionkey(c)); bn_int_set(&temp.sessionnum, conn_get_sessionnum(c)); bn_int_set(&temp.secret, conn_get_secret(c)); bnet_hash(&secret_hash, sizeof(temp), &temp); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_realmjoinreply_109)); packet_set_type(rpacket, SERVER_REALMJOINREPLY_109); bn_int_set(&rpacket->u.server_realmjoinreply_109.seqno, salt); bn_int_set(&rpacket->u.server_realmjoinreply_109.u1, 0x0); bn_short_set(&rpacket->u.server_realmjoinreply_109.u3, 0x0); /* reg auth */ bn_int_set(&rpacket->u.server_realmjoinreply_109.bncs_addr1, 0x0); bn_int_set(&rpacket->u.server_realmjoinreply_109.sessionnum, conn_get_sessionnum(c)); { /* trans support */ unsigned int addr = realm_get_ip(realm); unsigned short port = realm_get_port(realm); trans_net(conn_get_addr(c), &addr, &port); bn_int_nset(&rpacket->u.server_realmjoinreply_109.addr, addr); bn_short_nset(&rpacket->u.server_realmjoinreply_109.port, port); } bn_int_set(&rpacket->u.server_realmjoinreply_109.sessionkey, conn_get_sessionkey(c)); bn_int_set(&rpacket->u.server_realmjoinreply_109.u5, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.u6, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.bncs_addr2, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.u7, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.versionid, conn_get_versionid(c)); bn_int_set(&rpacket->u.server_realmjoinreply_109.clienttag, conn_get_clienttag(c)); hash_to_bnhash((t_hash const *)&secret_hash, rpacket->u.server_realmjoinreply_109.secret_hash); /* avoid warning */ packet_append_string(rpacket, conn_get_username(c)); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] realm join for \"{}\" failed (unable to hash password)", conn_get_socket(c), conn_get_loggeduser(c)); } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] realm join for \"{}\" failed (no password)", conn_get_socket(c), conn_get_loggeduser(c)); } } else eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not find active realm \"{}\"", conn_get_socket(c), realmname); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_realmjoinreply_109)); packet_set_type(rpacket, SERVER_REALMJOINREPLY_109); bn_int_set(&rpacket->u.server_realmjoinreply_109.seqno, bn_int_get(packet->u.client_realmjoinreq_109.seqno)); bn_int_set(&rpacket->u.server_realmjoinreply_109.u1, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.sessionnum, 0); bn_short_set(&rpacket->u.server_realmjoinreply_109.u3, 0); bn_int_nset(&rpacket->u.server_realmjoinreply_109.addr, 0); bn_short_nset(&rpacket->u.server_realmjoinreply_109.port, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.sessionkey, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.u5, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.u6, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.u7, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.bncs_addr1, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.bncs_addr2, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.versionid, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.clienttag, 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.secret_hash[0], 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.secret_hash[1], 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.secret_hash[2], 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.secret_hash[3], 0); bn_int_set(&rpacket->u.server_realmjoinreply_109.secret_hash[4], 0); packet_append_string(rpacket, ""); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_charlistreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_unknown_37)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UNKNOWN_37 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_unknown_37), packet_get_size(packet)); return -1; } /* 0x0070: 83 80 ff ff ff ff ff 2f t,taran,......./ 0x0080: ff ff ff ff ff ff ff ff ff ff 03 ff ff ff ff ff ................ 0x0090: ff ff ff ff ff ff ff ff ff ff ff 07 80 80 80 80 ................ 0x00a0: ff ff ff 00 */ if ((rpacket = packet_create(packet_class_bnet))) { char const *charlist; char *temp; packet_set_size(rpacket, sizeof(t_server_unknown_37)); packet_set_type(rpacket, SERVER_UNKNOWN_37); bn_int_set(&rpacket->u.server_unknown_37.unknown1, SERVER_UNKNOWN_37_UNKNOWN1); bn_int_set(&rpacket->u.server_unknown_37.unknown2, SERVER_UNKNOWN_37_UNKNOWN2); if (!(charlist = account_get_closed_characterlist(conn_get_account(c), conn_get_clienttag(c), realm_get_name(conn_get_realm(c))))) { bn_int_set(&rpacket->u.server_unknown_37.count, 0); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } temp = xstrdup(charlist); { char const *tok1; char const *tok2; t_character const *ch; unsigned int count; count = 0; tok1 = (char const *)std::strtok(temp, ","); /* std::strtok modifies the string it is passed */ tok2 = std::strtok(NULL, ","); while (tok1) { if (!tok2) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] account \"{}\" has bad character list \"{}\"", conn_get_socket(c), conn_get_username(c), temp); break; } if ((ch = characterlist_find_character(tok1, tok2))) { packet_append_ntstring(rpacket, character_get_realmname(ch)); packet_append_ntstring(rpacket, ","); packet_append_string(rpacket, character_get_name(ch)); packet_append_string(rpacket, character_get_playerinfo(ch)); packet_append_string(rpacket, character_get_guildname(ch)); count++; } else eventlog(eventlog_level_error, __FUNCTION__, "[{}] character \"{}\" is missing", conn_get_socket(c), tok2); tok1 = std::strtok(NULL, ","); tok2 = std::strtok(NULL, ","); } xfree(temp); bn_int_set(&rpacket->u.server_unknown_37.count, count); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_unknown39(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_unknown_39)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UNKNOWN_39 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_unknown_39), packet_get_size(packet)); return -1; } return 0; } static int _client_adreq(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_adreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ADREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_adreq), packet_get_size(packet)); return -1; } /* eventlog(eventlog_level_debug, __FUNCTION__, "[%d] SID_CHECKAD { %d %d %d %d }", conn_get_socket(c), bn_int_get(packet->u.client_adreq.archtag), bn_int_get(packet->u.client_adreq.clienttag), bn_int_get(packet->u.client_adreq.prev_adid), bn_int_get(packet->u.client_adreq.ticks)); */ const AdBanner* ad = AdBannerList.pick(conn_get_clienttag(c), conn_get_gamelang(c), bn_int_get(packet->u.client_adreq.prev_adid)); if (!ad) { return 0; } t_packet* const rpacket = packet_create(packet_class_bnet); if (!rpacket) { eventlog(eventlog_level_error, __FUNCTION__, "Could not create a packet"); return -1; } packet_set_size(rpacket, sizeof(t_server_adreply)); packet_set_type(rpacket, SERVER_ADREPLY); bn_int_set(&rpacket->u.server_adreply.adid, ad->get_id()); bn_int_set(&rpacket->u.server_adreply.extensiontag, ad->get_extension_tag()); file_to_mod_time(c, ad->get_filename().c_str(), &rpacket->u.server_adreply.timestamp); packet_append_string(rpacket, ad->get_filename().c_str()); packet_append_string(rpacket, ad->get_url().c_str()); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_adack(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_adack)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ADACK packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_adack), packet_get_size(packet)); return -1; } /* { char const * tname; eventlog(eventlog_level_info,__FUNCTION__,"[%d] ad acknowledgement for adid 0x%04x from \"%s\"",conn_get_socket(c),bn_int_get(packet->u.client_adack.adid),(tname = conn_get_chatname(c))); conn_unget_chatname(c,tname); } */ return 0; } static int _client_adclick(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_adclick)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ADCLICK packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_adclick), packet_get_size(packet)); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] ad click for adid 0x{:04x} from \"{}\"", conn_get_socket(c), bn_int_get(packet->u.client_adclick.adid), conn_get_username(c)); return 0; } static int _client_adclick2(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_adclick2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad ADCLICK2 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_adclick2), packet_get_size(packet)); return -1; } eventlog(eventlog_level_trace, __FUNCTION__, "[{}] ad click2 for adid 0x{:04x} from \"{}\"", conn_get_socket(c), bn_int_get(packet->u.client_adclick2.adid), conn_get_username(c)); const AdBanner* const ad = AdBannerList.find(conn_get_clienttag(c), conn_get_gamelang(c), bn_int_get(packet->u.client_adclick2.adid)); if (!ad) { return 0; } t_packet* const rpacket = packet_create(packet_class_bnet); if (!rpacket) { eventlog(eventlog_level_error, __FUNCTION__, "Could not create a packet"); return -1; } packet_set_size(rpacket, sizeof(t_server_adclickreply2)); packet_set_type(rpacket, SERVER_ADCLICKREPLY2); bn_int_set(&rpacket->u.server_adclickreply2.adid, ad->get_id()); packet_append_string(rpacket, ad->get_url().c_str()); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_readmemory(t_connection * c, t_packet const *const packet) { unsigned int size, offset, request_id; if (packet_get_size(packet) < sizeof(t_client_readmemory)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad READMEMORY packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_readmemory), packet_get_size(packet)); return -1; } request_id = bn_int_get(packet->u.client_readmemory.request_id); size = (unsigned int)packet_get_size(packet); offset = sizeof(t_client_readmemory); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] Received READMEMORY packet with Request ID: {} and Memory size: {}", conn_get_socket(c), request_id, size - offset); #ifdef WITH_LUA std::vector _data; for (int i = offset; i < size; i++) { _data.push_back(packet->u.data[i]); } lua_handle_client_readmemory(c, request_id, _data); #endif return 0; } static int _client_statsupdate(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_statsupdate)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STATSUPDATE packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_statsupdate), packet_get_size(packet)); return -1; } { char const *name; char const *key; char const *val; unsigned int name_count; unsigned int key_count; unsigned int i, j; unsigned int name_off; unsigned int keys_off; unsigned int key_off; unsigned int vals_off; unsigned int val_off; t_account *account; name_count = bn_int_get(packet->u.client_statsupdate.name_count); key_count = bn_int_get(packet->u.client_statsupdate.key_count); if (name_count != 1) eventlog(eventlog_level_warn, __FUNCTION__, "[{}] got suspicious STATSUPDATE packet (name_count={})", conn_get_socket(c), name_count); for (i = 0, name_off = sizeof(t_client_statsupdate); i < name_count && (name = packet_get_str_const(packet, name_off, UNCHECKED_NAME_STR)); i++, name_off += std::strlen(name) + 1); if (i < name_count) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STATSUPDATE packet (only {} names of {})", conn_get_socket(c), i, name_count); return -1; } keys_off = name_off; for (i = 0, key_off = keys_off; i < key_count && (key = packet_get_str_const(packet, key_off, MAX_ATTRKEY_STR)); i++, key_off += std::strlen(key) + 1); if (i < key_count) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STATSUPDATE packet (only {} keys of {})", conn_get_socket(c), i, key_count); return -1; } vals_off = key_off; if ((account = conn_get_account(c))) { if (account_get_auth_changeprofile(account) == 0) { /* default to true */ eventlog(eventlog_level_error, __FUNCTION__, "[{}] stats update for \"{}\" refused (no change profile access)", conn_get_socket(c), conn_get_username(c)); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] updating player profile for \"{}\"", conn_get_socket(c), conn_get_username(c)); for (i = 0, name_off = sizeof(t_client_statsupdate); i < name_count && (name = packet_get_str_const(packet, name_off, UNCHECKED_NAME_STR)); i++, name_off += std::strlen(name) + 1) for (j = 0, key_off = keys_off, val_off = vals_off; j < key_count && (key = packet_get_str_const(packet, key_off, MAX_ATTRKEY_STR)) && (val = packet_get_str_const(packet, val_off, MAX_ATTRVAL_STR)); j++, key_off += std::strlen(key) + 1, val_off += std::strlen(val) + 1) if (std::strlen(key) < 9 || strncasecmp(key, "profile\\", 8) != 0) eventlog(eventlog_level_error, __FUNCTION__, "[{}] got STATSUPDATE with suspicious key \"{}\" value \"{}\"", conn_get_socket(c), key, val); else account_set_strattr(account, key, val); } } return 0; } static int _client_playerinforeq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_playerinforeq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PLAYERINFOREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_playerinforeq), packet_get_size(packet)); return -1; } { char const *username; char const *info; t_account *account; if (!(username = packet_get_str_const(packet, sizeof(t_client_playerinforeq), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PLAYERINFOREQ (missing or too long username)", conn_get_socket(c)); return -1; } if (!(info = packet_get_str_const(packet, sizeof(t_client_playerinforeq)+std::strlen(username) + 1, MAX_PLAYERINFO_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PLAYERINFOREQ (missing or too long info)", conn_get_socket(c)); return -1; } if (info[0] != '\0') conn_set_playerinfo(c, info); if (!username[0]) username = conn_get_loggeduser(c); account = conn_get_account(c); if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_playerinforeply)); packet_set_type(rpacket, SERVER_PLAYERINFOREPLY); if (account) { packet_append_string(rpacket, username); packet_append_string(rpacket, conn_get_playerinfo(c)); packet_append_string(rpacket, username); } else { packet_append_string(rpacket, ""); packet_append_string(rpacket, ""); packet_append_string(rpacket, ""); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_progident2(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_progident2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad PROGIDENT2 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_progident2), packet_get_size(packet)); return -1; } /* d2 uses this packet with clienttag = 0 to request the channel list */ if (bn_int_get(packet->u.client_progident2.clienttag)) { if (tag_check_in_list(bn_int_get(packet->u.client_progident2.clienttag), prefs_get_allowed_clients())) { conn_set_state(c, conn_state_destroy); return 0; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] CLIENT_PROGIDENT2 clienttag=0x{:08x}", conn_get_socket(c), bn_int_get(packet->u.client_progident2.clienttag)); /* Hmm... no archtag. Hope we get it in CLIENT_AUTHREQ1 (but we won't if we use the shortcut) */ conn_set_clienttag(c, bn_int_get(packet->u.client_progident2.clienttag)); } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_channellist)); packet_set_type(rpacket, SERVER_CHANNELLIST); { t_channel *ch; t_elem const *curr; LIST_TRAVERSE_CONST(channellist(), curr) { ch = (t_channel*)elem_get_data(curr); if ((!(channel_get_flags(ch) & channel_flags_clan)) && (!prefs_get_hide_temp_channels() || channel_get_permanent(ch)) && (!channel_get_clienttag(ch) || channel_get_clienttag(ch) == conn_get_clienttag(c)) && (!(channel_get_flags(ch) & channel_flags_thevoid)) && // don't display theVoid in channel list ((channel_get_max(ch) != 0) || ((channel_get_max(ch) == 0) && (account_is_operator_or_admin(conn_get_account(c), channel_get_name(ch)) == 1)))) // don't display restricted channel for no admins/ops packet_append_string(rpacket, channel_get_name(ch)); } } packet_append_string(rpacket, ""); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_joinchannel(t_connection * c, t_packet const *const packet) { t_account *account; char const *cname; int found = 1; t_clan *user_clan; t_clantag clantag; std::uint32_t clienttag; t_channel *channel; if (packet_get_size(packet) < sizeof(t_client_joinchannel)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad JOINCHANNEL packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_joinchannel), packet_get_size(packet)); return -1; } account = conn_get_account(c); if (!(cname = packet_get_str_const(packet, sizeof(t_client_joinchannel), MAX_CHANNELNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad JOINCHANNEL (missing or too long cname)", conn_get_socket(c)); return -1; } if ((channel = conn_get_channel(c)) && (strcasecmp(channel_get_name(channel), cname) == 0)) return 0; //we are already in this channel std::string tmpstr; clienttag = conn_get_clienttag(c); if ((clienttag == CLIENTTAG_WARCRAFT3_UINT) || (clienttag == CLIENTTAG_WAR3XP_UINT)) { conn_update_w3_playerinfo(c); switch (bn_int_get(packet->u.client_joinchannel.channelflag)) { case CLIENT_JOINCHANNEL_NORMAL: eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_JOINCHANNEL_NORMAL channel \"{}\"", conn_get_socket(c), cname); if (prefs_get_ask_new_channel() && (!(channellist_find_channel_by_name(cname, conn_get_country(c), realm_get_name(conn_get_realm(c)))))) { found = 0; eventlog(eventlog_level_info, __FUNCTION__, "[{}] didn't find channel \"{}\" to join", conn_get_socket(c), cname); message_send_text(c, message_type_channeldoesnotexist, c, cname); } break; case CLIENT_JOINCHANNEL_GENERIC: if ((user_clan = account_get_clan(account)) && (clantag = clan_get_clantag(user_clan))) { std::ostringstream ostr; ostr << "Clan " << clantag_to_str(clantag); tmpstr = ostr.str(); cname = tmpstr.c_str(); } eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_JOINCHANNEL_GENERIC channel \"{}\"", conn_get_socket(c), cname); /* don't have to do anything here */ break; case CLIENT_JOINCHANNEL_CREATE: eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_JOINCHANNEL_CREATE channel \"{}\"", conn_get_socket(c), cname); eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_JOINCHANNEL_CREATE channel \"{}\"", conn_get_socket(c), cname); /* don't have to do anything here */ break; } if (found && conn_set_channel(c, cname) < 0) conn_set_channel(c, CHANNEL_NAME_BANNED); /* should not fail */ } else { // not W3 if (conn_set_channel(c, cname) < 0) conn_set_channel(c, CHANNEL_NAME_BANNED); /* should not fail */ } // here we set channel flags on user channel_set_userflags(c); return 0; } static int _client_message(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_message)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad MESSAGE packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_message), packet_get_size(packet)); return -1; } { char const *text; t_channel const *channel; if (!(text = packet_get_str_const(packet, sizeof(t_client_message), MAX_MESSAGE_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad MESSAGE (missing or too long text)", conn_get_socket(c)); return -1; } conn_set_idletime(c); if ((channel = conn_get_channel(c))) channel_message_log(channel, c, 1, text); /* we don't log game commands currently */ if (text[0] == '/') handle_command(c, text); else if (channel && !conn_quota_exceeded(c, text)) channel_message_send(channel, message_type_talk, c, text); /* else discard */ } return 0; } static int _glist_cb(t_game * game, void *data) { struct glist_cbdata *cbdata = (struct glist_cbdata*)data; char clienttag_str[5]; t_server_gamelistreply_game glgame; unsigned int addr; unsigned short port; bn_int game_spacer = { 1, 0, 0, 0 }; cbdata->tcount++; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] considering listing game=\"{}\", pass=\"{}\" clienttag=\"{}\" gtype={}", conn_get_socket(cbdata->c), game_get_name(game), game_get_pass(game), tag_uint_to_str(clienttag_str, game_get_clienttag(game)), (int)game_get_type(game)); if (prefs_get_hide_pass_games() && game_get_flag(game) == game_flag_private) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is passworded or has private flag", conn_get_socket(cbdata->c)); return 0; } if (prefs_get_hide_started_games() && game_get_status(game) != game_status_open) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is not open", conn_get_socket(cbdata->c)); return 0; } if (game_get_clienttag(game) != conn_get_clienttag(cbdata->c)) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is for a different client", conn_get_socket(cbdata->c)); return 0; } if (cbdata->gtype != game_type_all && game_get_type(game) != cbdata->gtype) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is wrong type", conn_get_socket(cbdata->c)); return 0; } if (conn_get_versioncheck(cbdata->c) && conn_get_versioncheck(game_get_owner(game)) && conn_get_versioncheck(cbdata->c)->get_version_tag() != conn_get_versioncheck(game_get_owner(game))->get_version_tag()) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is wrong versiontag", conn_get_socket(cbdata->c)); return 0; } bn_short_set(&glgame.gametype, gtype_to_bngtype(game_get_type(game))); bn_short_set(&glgame.unknown1, SERVER_GAMELISTREPLY_GAME_UNKNOWN1); bn_short_set(&glgame.unknown3, SERVER_GAMELISTREPLY_GAME_UNKNOWN3); addr = game_get_addr(game); port = game_get_port(game); trans_net(conn_get_addr(cbdata->c), &addr, &port); bn_short_nset(&glgame.port, port); bn_int_nset(&glgame.game_ip, addr); bn_int_set(&glgame.unknown4, SERVER_GAMELISTREPLY_GAME_UNKNOWN4); bn_int_set(&glgame.unknown5, SERVER_GAMELISTREPLY_GAME_UNKNOWN5); switch (game_get_status(game)) { case game_status_started: bn_int_set(&glgame.status, SERVER_GAMELISTREPLY_GAME_STATUS_STARTED); break; case game_status_full: bn_int_set(&glgame.status, SERVER_GAMELISTREPLY_GAME_STATUS_FULL); break; case game_status_open: bn_int_set(&glgame.status, SERVER_GAMELISTREPLY_GAME_STATUS_OPEN); break; case game_status_done: bn_int_set(&glgame.status, SERVER_GAMELISTREPLY_GAME_STATUS_DONE); break; default: eventlog(eventlog_level_warn, __FUNCTION__, "[{}] game \"{}\" has bad status={}", conn_get_socket(cbdata->c), game_get_name(game), (int)game_get_status(game)); bn_int_set(&glgame.status, 0); } bn_int_set(&glgame.unknown6, SERVER_GAMELISTREPLY_GAME_UNKNOWN6); if (packet_get_size(cbdata->rpacket) + sizeof(glgame)+std::strlen(game_get_name(game)) + 1 + std::strlen(game_get_pass(game)) + 1 + std::strlen(game_get_info(game)) + 1 > MAX_PACKET_SIZE) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] out of room for games", conn_get_socket(cbdata->c)); return -1; /* no more room */ } if (cbdata->counter) { packet_append_data(cbdata->rpacket, &game_spacer, sizeof(game_spacer)); } packet_append_data(cbdata->rpacket, &glgame, sizeof(glgame)); packet_append_string(cbdata->rpacket, game_get_name(game)); packet_append_string(cbdata->rpacket, game_get_pass(game)); packet_append_string(cbdata->rpacket, game_get_info(game)); cbdata->counter++; return 0; } static int _client_gamelistreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; char const *gamename; char const *gamepass; unsigned short bngtype; t_game_type gtype; t_clienttag clienttag; t_game *game; t_server_gamelistreply_game glgame; unsigned int addr; unsigned short port; char clienttag_str[5]; if (packet_get_size(packet) < sizeof(t_client_gamelistreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GAMELISTREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_gamelistreq), packet_get_size(packet)); return -1; } if (!(gamename = packet_get_str_const(packet, sizeof(t_client_gamelistreq), MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GAMELISTREQ (missing or too long gamename)", conn_get_socket(c)); return -1; } if (!(gamepass = packet_get_str_const(packet, sizeof(t_client_gamelistreq)+std::strlen(gamename) + 1, MAX_GAMEPASS_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GAMELISTREQ (missing or too long password)", conn_get_socket(c)); return -1; } bngtype = bn_short_get(packet->u.client_gamelistreq.gametype); clienttag = conn_get_clienttag(c); gtype = bngreqtype_to_gtype(clienttag, bngtype); if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_gamelistreply)); packet_set_type(rpacket, SERVER_GAMELISTREPLY); bn_int_set(&rpacket->u.server_gamelistreply.sstatus, 0); /* specific game requested? */ if (gamename[0] != '\0') { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY looking for specific game tag=\"{}\" bngtype=0x{:08x} gtype={} name=\"{}\" pass=\"{}\"", conn_get_socket(c), tag_uint_to_str(clienttag_str, clienttag), bngtype, (int)gtype, gamename, gamepass); if ((game = gamelist_find_game(gamename, clienttag, gtype))) { /* game found but first we need to make sure everything is OK */ bn_int_set(&rpacket->u.server_gamelistreply.gamecount, 0); switch (game_get_status(game)) { case game_status_started: bn_int_set(&rpacket->u.server_gamelistreply.sstatus, SERVER_GAMELISTREPLY_GAME_SSTATUS_STARTED); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY found but started", conn_get_socket(c)); break; case game_status_full: bn_int_set(&rpacket->u.server_gamelistreply.sstatus, SERVER_GAMELISTREPLY_GAME_SSTATUS_FULL); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY found but full", conn_get_socket(c)); break; case game_status_done: bn_int_set(&rpacket->u.server_gamelistreply.sstatus, SERVER_GAMELISTREPLY_GAME_SSTATUS_NOTFOUND); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY found but done", conn_get_socket(c)); break; case game_status_open: case game_status_loaded: if (std::strcmp(gamepass, game_get_pass(game))) { /* passworded game must match password in request */ bn_int_set(&rpacket->u.server_gamelistreply.sstatus, SERVER_GAMELISTREPLY_GAME_SSTATUS_PASS); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY found but is password protected and wrong password given", conn_get_socket(c)); break; } if (game_get_status(game) == game_status_loaded) { bn_int_set(&rpacket->u.server_gamelistreply.sstatus, SERVER_GAMELISTREPLY_GAME_SSTATUS_LOADED); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY found loaded game", conn_get_socket(c)); } /* everything seems fine, lets reply with the found game */ bn_int_set(&glgame.status, SERVER_GAMELISTREPLY_GAME_STATUS_OPEN); bn_short_set(&glgame.gametype, gtype_to_bngtype(game_get_type(game))); bn_short_set(&glgame.unknown1, SERVER_GAMELISTREPLY_GAME_UNKNOWN1); bn_short_set(&glgame.unknown3, SERVER_GAMELISTREPLY_GAME_UNKNOWN3); addr = game_get_addr(game); port = game_get_port(game); trans_net(conn_get_addr(c), &addr, &port); bn_short_nset(&glgame.port, port); bn_int_nset(&glgame.game_ip, addr); bn_int_set(&glgame.unknown4, SERVER_GAMELISTREPLY_GAME_UNKNOWN4); bn_int_set(&glgame.unknown5, SERVER_GAMELISTREPLY_GAME_UNKNOWN5); bn_int_set(&glgame.unknown6, SERVER_GAMELISTREPLY_GAME_UNKNOWN6); packet_append_data(rpacket, &glgame, sizeof(glgame)); packet_append_string(rpacket, game_get_name(game)); packet_append_string(rpacket, game_get_pass(game)); packet_append_string(rpacket, game_get_info(game)); bn_int_set(&rpacket->u.server_gamelistreply.gamecount, 1); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY specific game found", conn_get_socket(c)); break; default: eventlog(eventlog_level_warn, __FUNCTION__, "[{}] game \"{}\" has bad status {}", conn_get_socket(c), game_get_name(game), game_get_status(game)); } } else { bn_int_set(&rpacket->u.server_gamelistreply.gamecount, 0); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY specific game doesn't seem to exist", conn_get_socket(c)); } } else { /* list all public games of this type */ struct glist_cbdata cbdata; if (gtype == game_type_all) eventlog(eventlog_level_debug, __FUNCTION__, "GAMELISTREPLY looking for public games tag=\"{}\" bngtype=0x{:08x} gtype=all", tag_uint_to_str(clienttag_str, clienttag), bngtype); else eventlog(eventlog_level_debug, __FUNCTION__, "GAMELISTREPLY looking for public games tag=\"{}\" bngtype=0x{:08x} gtype={}", tag_uint_to_str(clienttag_str, clienttag), bngtype, (int)gtype); cbdata.counter = 0; cbdata.tcount = 0; cbdata.c = c; cbdata.gtype = gtype; cbdata.rpacket = rpacket; gamelist_traverse(_glist_cb, &cbdata, gamelist_source_joinbutton); bn_int_set(&rpacket->u.server_gamelistreply.gamecount, cbdata.counter); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] GAMELISTREPLY sent {} of {} games", conn_get_socket(c), cbdata.counter, cbdata.tcount); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); return 0; } static int _client_joingame(t_connection * c, t_packet const *const packet) { char const *gamename; char const *gamepass; t_game *game; t_game_type gtype; if (packet_get_size(packet) < sizeof(t_client_join_game)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad JOIN_GAME packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_join_game), packet_get_size(packet)); return -1; } if (!(gamename = packet_get_str_const(packet, sizeof(t_client_join_game), MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_JOIN_GAME (missing or too long gamename)", conn_get_socket(c)); return -1; } if (!(gamepass = packet_get_str_const(packet, sizeof(t_client_join_game)+std::strlen(gamename) + 1, MAX_GAMEPASS_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_JOIN_GAME packet (missing or too long gamepass)", conn_get_socket(c)); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] trying to join game \"{}\" pass=\"{}\"", conn_get_socket(c), gamename, gamepass); if (conn_get_joingamewhisper_ack(c) == 0) { watchlist->dispatch(conn_get_account(c), gamename, conn_get_clienttag(c), Watch::ET_joingame); conn_set_joingamewhisper_ack(c, 1); /* 1 = already whispered. We reset this each time user joins a channel */ clanmember_on_change_status_by_connection(c); } if (conn_get_channel(c)) conn_part_channel(c); if (!std::strcmp(gamename, "BNet") && !handle_anongame_join(c)) { gtype = game_type_anongame; gamename = NULL; return 0; /* tmp: do not record any anongames as yet */ } else { if (!(game = gamelist_find_game_available(gamename, conn_get_clienttag(c), game_type_all))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] unable to find game \"{}\" for user to join", conn_get_socket(c), gamename); return 0; } gtype = game_get_type(game); gamename = game_get_name(game); if ((gtype == game_type_ladder && account_get_auth_joinladdergame(conn_get_account(c)) == 0) || /* default to true */ (gtype != game_type_ladder && account_get_auth_joinnormalgame(conn_get_account(c)) == 0)) { /* default to true */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] game join for \"{}\" to \"{}\" refused (no authority)", conn_get_socket(c), conn_get_username(c), gamename); /* If the user is not in a game, then map authorization will fail and keep them from playing. */ return 0; } } if (conn_set_game(c, gamename, gamepass, "", gtype, STARTVER_UNKNOWN) < 0) eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" joined game \"{}\", but could not be recorded on server", conn_get_socket(c), conn_get_username(c), gamename); else eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" joined game \"{}\"", conn_get_socket(c), conn_get_username(c), gamename); #ifdef WITH_LUA lua_handle_game(game, c, luaevent_game_userjoin); #endif return 0; } static int _client_startgame1(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_startgame1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_startgame1), packet_get_size(packet)); return -1; } { char const *gamename; char const *gamepass; char const *gameinfo; unsigned short bngtype; unsigned int status; t_game *currgame; if (!(gamename = packet_get_str_const(packet, sizeof(t_client_startgame1), MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME1 packet (missing or too long gamename)", conn_get_socket(c)); return -1; } if (!(gamepass = packet_get_str_const(packet, sizeof(t_client_startgame1)+std::strlen(gamename) + 1, MAX_GAMEPASS_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME1 packet (missing or too long gamepass)", conn_get_socket(c)); return -1; } if (!(gameinfo = packet_get_str_const(packet, sizeof(t_client_startgame1)+std::strlen(gamename) + 1 + std::strlen(gamepass) + 1, MAX_GAMEINFO_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME1 packet (missing or too long gameinfo)", conn_get_socket(c)); return -1; } if (conn_get_joingamewhisper_ack(c) == 0) { if (watchlist->dispatch(conn_get_account(c), gamename, conn_get_clienttag(c), Watch::ET_joingame) == 0) eventlog(eventlog_level_info, "handle_bnet", "Told Mutual Friends your in game {}", gamename); conn_set_joingamewhisper_ack(c, 1); //1 = already whispered. We reset this each time user joins a channel } bngtype = bn_short_get(packet->u.client_startgame1.gametype); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got startgame1 status for game \"{}\" is 0x{:08x} (gametype = 0x{:04x})", conn_get_socket(c), gamename, bn_int_get(packet->u.client_startgame1.status), bngtype); status = bn_int_get(packet->u.client_startgame1.status) & CLIENT_STARTGAME1_STATUSMASK; if ((currgame = conn_get_game(c))) { switch (status) { case CLIENT_STARTGAME1_STATUS_STARTED: game_set_status(currgame, game_status_started); break; case CLIENT_STARTGAME1_STATUS_FULL: game_set_status(currgame, game_status_full); break; case CLIENT_STARTGAME1_STATUS_OPEN: game_set_status(currgame, game_status_open); break; case CLIENT_STARTGAME1_STATUS_DONE: game_set_status(currgame, game_status_done); eventlog(eventlog_level_info, __FUNCTION__, "[{}] game \"{}\" is finished", conn_get_socket(c), gamename); break; } } else if (status != CLIENT_STARTGAME1_STATUS_DONE) { t_game_type gtype; gtype = bngtype_to_gtype(conn_get_clienttag(c), bngtype); if ((gtype == game_type_ladder && account_get_auth_createladdergame(conn_get_account(c)) == 0) || /* default to true */ (gtype != game_type_ladder && account_get_auth_createnormalgame(conn_get_account(c)) == 0)) /* default to true */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] game start for \"{}\" refused (no authority)", conn_get_socket(c), conn_get_username(c)); else conn_set_game(c, gamename, gamepass, gameinfo, gtype, STARTVER_GW1); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_startgame1_ack)); packet_set_type(rpacket, SERVER_STARTGAME1_ACK); if (conn_get_game(c)) bn_int_set(&rpacket->u.server_startgame1_ack.reply, SERVER_STARTGAME1_ACK_OK); else bn_int_set(&rpacket->u.server_startgame1_ack.reply, SERVER_STARTGAME1_ACK_NO); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] client tried to set game status DONE to destroyed game", conn_get_socket(c)); } return 0; } static int _client_startgame3(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_startgame3)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME3 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_startgame3), packet_get_size(packet)); return -1; } { char const *gamename; char const *gamepass; char const *gameinfo; unsigned short bngtype; unsigned int status; t_game *currgame; if (!(gamename = packet_get_str_const(packet, sizeof(t_client_startgame3), MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME3 packet (missing or too long gamename)", conn_get_socket(c)); return -1; } if (!(gamepass = packet_get_str_const(packet, sizeof(t_client_startgame3)+std::strlen(gamename) + 1, MAX_GAMEPASS_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME3 packet (missing or too long gamepass)", conn_get_socket(c)); return -1; } if (!(gameinfo = packet_get_str_const(packet, sizeof(t_client_startgame3)+std::strlen(gamename) + 1 + std::strlen(gamepass) + 1, MAX_GAMEINFO_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME3 packet (missing or too long gameinfo)", conn_get_socket(c)); return -1; } if (conn_get_joingamewhisper_ack(c) == 0) { if (watchlist->dispatch(conn_get_account(c), gamename, conn_get_clienttag(c), Watch::ET_joingame) == 0) eventlog(eventlog_level_info, "handle_bnet", "Told Mutual Friends your in game {}", gamename); conn_set_joingamewhisper_ack(c, 1); //1 = already whispered. We reset this each time user joins a channel } bngtype = bn_short_get(packet->u.client_startgame3.gametype); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got startgame3 status for game \"{}\" is 0x{:08x} (gametype = 0x{:04x})", conn_get_socket(c), gamename, bn_int_get(packet->u.client_startgame3.status), bngtype); status = bn_int_get(packet->u.client_startgame3.status) & CLIENT_STARTGAME3_STATUSMASK; if ((currgame = conn_get_game(c))) { switch (status) { case CLIENT_STARTGAME3_STATUS_STARTED: game_set_status(currgame, game_status_started); break; case CLIENT_STARTGAME3_STATUS_FULL: game_set_status(currgame, game_status_full); break; case CLIENT_STARTGAME3_STATUS_OPEN1: case CLIENT_STARTGAME3_STATUS_OPEN: game_set_status(currgame, game_status_open); break; case CLIENT_STARTGAME3_STATUS_DONE: game_set_status(currgame, game_status_done); eventlog(eventlog_level_info, __FUNCTION__, "[{}] game \"{}\" is finished", conn_get_socket(c), gamename); break; } } else if (status != CLIENT_STARTGAME3_STATUS_DONE) { t_game_type gtype; gtype = bngtype_to_gtype(conn_get_clienttag(c), bngtype); if ((gtype == game_type_ladder && account_get_auth_createladdergame(conn_get_account(c)) == 0) || (gtype != game_type_ladder && account_get_auth_createnormalgame(conn_get_account(c)) == 0)) eventlog(eventlog_level_info, __FUNCTION__, "[{}] game start for \"{}\" refused (no authority)", conn_get_socket(c), conn_get_username(c)); else conn_set_game(c, gamename, gamepass, gameinfo, gtype, STARTVER_GW3); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_startgame3_ack)); packet_set_type(rpacket, SERVER_STARTGAME3_ACK); if (conn_get_game(c)) bn_int_set(&rpacket->u.server_startgame3_ack.reply, SERVER_STARTGAME3_ACK_OK); else bn_int_set(&rpacket->u.server_startgame3_ack.reply, SERVER_STARTGAME3_ACK_NO); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] client tried to set game status DONE to destroyed game", conn_get_socket(c)); } return 0; } static int _client_startgame4(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_startgame4)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME4 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_startgame4), packet_get_size(packet)); return -1; } if (conn_get_clienttag(c) == CLIENTTAG_STARCRAFT_UINT || conn_get_clienttag(c) == CLIENTTAG_BROODWARS_UINT) { // FIXME: (HarpyWar) Protection from hack attempt // Large map name size will cause crash Starcraft client for user who select an item in game list ("Join" area) // It occurs when the packet size of packet 0x0c in length interval 161-164 // https://github.com/pvpgn/pvpgn-server/issues/159 if (packet_get_size(packet) > 160) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got abnormal STARTGAME4 packet length (got {} bytes, hack attempt?)", conn_get_socket(c), packet_get_size(packet)); return -1; } } // Quick hack to make W3 part channels when creating a game if (conn_get_channel(c)) conn_part_channel(c); { char const *gamename; char const *gamepass; char const *gameinfo; unsigned short bngtype; unsigned int status; unsigned int flag; unsigned short option; t_game *currgame; if (!(gamename = packet_get_str_const(packet, sizeof(t_client_startgame4), MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME4 packet (missing or too long gamename)", conn_get_socket(c)); return -1; } if (!(gamepass = packet_get_str_const(packet, sizeof(t_client_startgame4)+std::strlen(gamename) + 1, MAX_GAMEPASS_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME4 packet (missing or too long gamepass)", conn_get_socket(c)); return -1; } if (!(gameinfo = packet_get_str_const(packet, sizeof(t_client_startgame4)+std::strlen(gamename) + 1 + std::strlen(gamepass) + 1, MAX_GAMEINFO_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad STARTGAME4 packet (missing or too long gameinfo)", conn_get_socket(c)); return -1; } if (conn_get_joingamewhisper_ack(c) == 0) { if (watchlist->dispatch(conn_get_account(c), gamename, conn_get_clienttag(c), Watch::ET_joingame) == 0) eventlog(eventlog_level_info, "handle_bnet", "Told Mutual Friends your in game {}", gamename); conn_set_joingamewhisper_ack(c, 1); //1 = already whispered. We reset this each time user joins a channel } bngtype = bn_short_get(packet->u.client_startgame4.gametype); option = bn_short_get(packet->u.client_startgame4.option); status = bn_int_get(packet->u.client_startgame4.status); flag = bn_short_get(packet->u.client_startgame4.flag); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got startgame4 status for game \"{}\" is 0x{:08x} (gametype=0x{:04x} option=0x{:04x}, flag=0x{:04x})", conn_get_socket(c), gamename, status, bngtype, option, flag); if ((currgame = conn_get_game(c))) { if ((status & CLIENT_STARTGAME4_STATUSMASK_OPEN_VALID) == status) { if (status & CLIENT_STARTGAME4_STATUS_START) game_set_status(currgame, game_status_started); else if (status & CLIENT_STARTGAME4_STATUS_FULL) game_set_status(currgame, game_status_full); else game_set_status(currgame, game_status_open); } else { eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown startgame4 status {} (clienttag: {})", conn_get_socket(c), status, clienttag_uint_to_str(conn_get_clienttag(c))); } } else if ((status & CLIENT_STARTGAME4_STATUSMASK_INIT_VALID) == status) { /*valid creation status would be: 0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, 0x80, 0x81, 0x82, 0x83 */ t_game_type gtype; bool allow_create_custom = false; t_game *game; gtype = bngtype_to_gtype(conn_get_clienttag(c), bngtype); if ((gtype == game_type_ladder && account_get_auth_createladdergame(conn_get_account(c)) == 0) || (gtype != game_type_ladder && account_get_auth_createnormalgame(conn_get_account(c)) == 0)) eventlog(eventlog_level_info, __FUNCTION__, "[{}] game start for \"{}\" refused (no authority)", conn_get_socket(c), conn_get_username(c)); else { //find is there any existing game with same name and allow the host to create game // with same name only when another game is already started or already done if ((!(game = gamelist_find_game_available(gamename, conn_get_clienttag(c), game_type_all))) && (conn_set_game(c, gamename, gamepass, gameinfo, gtype, STARTVER_GW4) == 0)) { game_set_option(conn_get_game(c), bngoption_to_goption(conn_get_clienttag(c), gtype, option)); if (status & CLIENT_STARTGAME4_STATUS_PRIVATE) game_set_flag(conn_get_game(c), game_flag_private); if (status & CLIENT_STARTGAME4_STATUS_FULL) game_set_status(conn_get_game(c), game_status_full); if (bngtype == CLIENT_GAMELISTREQ_LOADED) /* PELISH: seems strange but it is really needed for loaded games */ game_set_status(conn_get_game(c), game_status_loaded); //FIXME: still need special handling for status disc-is-loss and replay } } } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] client tried to set game status 0x{:x} to unexistent game (clienttag: {})", conn_get_socket(c), status, clienttag_uint_to_str(conn_get_clienttag(c))); } if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_startgame4_ack)); packet_set_type(rpacket, SERVER_STARTGAME4_ACK); if (conn_get_game(c)) bn_int_set(&rpacket->u.server_startgame4_ack.reply, SERVER_STARTGAME4_ACK_OK); else bn_int_set(&rpacket->u.server_startgame4_ack.reply, SERVER_STARTGAME4_ACK_NO); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } /* First, send an ECHO_REQ */ if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_echoreq)); packet_set_type(rpacket, SERVER_ECHOREQ); bn_int_set(&rpacket->u.server_echoreq.ticks, get_ticks()); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_closegame(t_connection * c, t_packet const *const packet) { t_game *game; eventlog(eventlog_level_info, __FUNCTION__, "[{}] client closing game", conn_get_socket(c)); if (packet_get_type(packet) == CLIENT_CLOSEGAME2 || ((conn_get_clienttag(c) != CLIENTTAG_WARCRAFT3_UINT) && (conn_get_clienttag(c) != CLIENTTAG_WAR3XP_UINT))) conn_set_game(c, NULL, NULL, NULL, game_type_none, 0); else if ((game = conn_get_game(c))) game_set_status(game, game_status_started); return 0; } static int _client_gamereport(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_game_report)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GAME_REPORT packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_game_report), packet_get_size(packet)); return -1; } { t_account *my_account; t_account *other_account; t_game *game; unsigned int player_count; unsigned int i, s; t_client_game_report_result const *result_data; unsigned int result_off; t_game_result result; char const *player; unsigned int player_off; t_game_result *results; player_count = bn_int_get(packet->u.client_gamerep.count); if (!(game = conn_get_game(c))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got GAME_REPORT when not in a game for user \"{}\"", conn_get_socket(c), conn_get_username(c)); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] CLIENT_GAME_REPORT: {} ({} players)", conn_get_socket(c), conn_get_username(c), player_count); my_account = conn_get_account(c); results = (t_game_result*)xmalloc(sizeof(t_game_result)* game_get_count(game)); for (i = 0; i < game_get_count(game); i++) results[i] = game_result_none; for (i = 0, result_off = sizeof(t_client_game_report), player_off = sizeof(t_client_game_report)+player_count * sizeof(t_client_game_report_result); i < player_count; i++, result_off += sizeof(t_client_game_report_result), player_off += std::strlen(player) + 1) { /* PELISH: Fixme - Can this crash server (NULL pointer dereferencing)?? */ if (!(result_data = (const t_client_game_report_result*)packet_get_data_const(packet, result_off, sizeof(t_client_game_report_result)))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got corrupt GAME_REPORT packet (missing results {}-{})", conn_get_socket(c), i + 1, player_count); break; } if (!(player = packet_get_str_const(packet, player_off, MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got corrupt GAME_REPORT packet (missing players {}-{})", conn_get_socket(c), i + 1, player_count); break; } if (player[0] == '\0') /* empty slots have empty player name */ continue; if (i >= game_get_count(game)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got more results than the game had players - ignoring extra results", conn_get_socket(c)); break; } if (!(other_account = accountlist_find_account(player))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got GAME_REPORT with unknown player \"{}\"", conn_get_socket(c), player); break; } // as player position in game structure and in game report might differ, // search for right position for (s = 0; s < game_get_count(game); s++) { if (game_get_player(game, s) == other_account) break; } if (s < game_get_count(game)) { result = bngresult_to_gresult(bn_int_get(result_data->result)); results[s] = result; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got player {} (\"{}\") result {}", conn_get_socket(c), i, player, game_result_get_str(result)); } else { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got GAME_REPORT for non-participating player \"{}\"", conn_get_socket(c), player); } } if (i == player_count) { /* if everything checked out... */ char const *head; char const *body; if (!(head = packet_get_str_const(packet, player_off, MAX_GAMEREP_HEAD_STR))) eventlog(eventlog_level_error, __FUNCTION__, "[{}] got GAME_REPORT with missing or too long report head", conn_get_socket(c)); else { player_off += std::strlen(head) + 1; if (!(body = packet_get_str_const(packet, player_off, MAX_GAMEREP_BODY_STR))) eventlog(eventlog_level_error, __FUNCTION__, "[{}] got GAME_REPORT with missing or too ling report body", conn_get_socket(c)); else game_set_report(game, my_account, head, body); } } if (game_set_reported_results(game, my_account, results) < 0) xfree((void *)results); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] finished parsing result... now leaving game", conn_get_socket(c)); conn_set_game(c, NULL, NULL, NULL, game_type_none, 0); } return 0; } static int _client_leavechannel(t_connection * c, t_packet const *const packet) { /* If this user in a channel, notify everyone that the user has left */ if (conn_get_channel(c)) conn_part_channel(c); return 0; } static int _client_ladderreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_ladderreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LADDERREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_ladderreq), packet_get_size(packet)); return -1; } { t_ladder_entry entry; unsigned int i; unsigned int type; unsigned int start; unsigned int count; unsigned int idnum; t_account *account; t_clienttag clienttag; char const *timestr; t_bnettime bt; t_ladder_id id; t_ladder_sort sort; bool error = false; clienttag = conn_get_clienttag(c); type = bn_int_get(packet->u.client_ladderreq.type); start = bn_int_get(packet->u.client_ladderreq.startplace); count = bn_int_get(packet->u.client_ladderreq.count); idnum = bn_int_get(packet->u.client_ladderreq.id); /* eventlog(eventlog_level_debug,__FUNCTION__,"got LADDERREQ type=%u start=%u count=%u id=%u",type,start,count,id); */ switch (idnum) { case CLIENT_LADDERREQ_ID_STANDARD: id = ladder_id_normal; break; case CLIENT_LADDERREQ_ID_IRONMAN: id = ladder_id_ironman; break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] got unknown ladder ladderreq.id=0x{:08x}", conn_get_socket(c), idnum); id = ladder_id_normal; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_ladderreply)); packet_set_type(rpacket, SERVER_LADDERREPLY); bn_int_set(&rpacket->u.server_ladderreply.clienttag, clienttag); bn_int_set(&rpacket->u.server_ladderreply.id, idnum); bn_int_set(&rpacket->u.server_ladderreply.type, type); bn_int_set(&rpacket->u.server_ladderreply.startplace, start); bn_int_set(&rpacket->u.server_ladderreply.count, count); switch (type){ case CLIENT_LADDERREQ_TYPE_HIGHESTRATED: sort = ladder_sort_highestrated; break; case CLIENT_LADDERREQ_TYPE_MOSTWINS: sort = ladder_sort_mostwins; break; case CLIENT_LADDERREQ_TYPE_MOSTGAMES: sort = ladder_sort_mostgames; break; default: sort = ladder_sort_default; eventlog(eventlog_level_error, __FUNCTION__, "[{}] got unknown value for ladderreq.type={}", conn_get_socket(c), type); error = true; } LadderList* ladderList_active = NULL; LadderList* ladderList_current = NULL; if (!error) { ladderList_active = ladders.getLadderList(LadderKey(id, clienttag, sort, ladder_time_active)); ladderList_current = ladders.getLadderList(LadderKey(id, clienttag, sort, ladder_time_current)); } if (!ladderList_active || ladderList_current) error = true; for (i = start; i < start + count; i++) { const LadderReferencedObject* referencedObject = NULL; account = NULL; if (!error){ if (!(referencedObject = ladderList_active->getReferencedObject(i + 1))) referencedObject = ladderList_current->getReferencedObject(i + 1); } if ((referencedObject) && (account = referencedObject->getAccount())) { bn_int_set(&entry.active.wins, account_get_ladder_active_wins(account, clienttag, id)); bn_int_set(&entry.active.loss, account_get_ladder_active_losses(account, clienttag, id)); bn_int_set(&entry.active.disconnect, account_get_ladder_active_disconnects(account, clienttag, id)); bn_int_set(&entry.active.rating, account_get_ladder_active_rating(account, clienttag, id)); bn_int_set(&entry.active.rank, account_get_ladder_active_rank(account, clienttag, id) - 1); if (!(timestr = account_get_ladder_active_last_time(account, clienttag, id))) timestr = BNETD_LADDER_DEFAULT_TIME; bnettime_set_str(&bt, timestr); bnettime_to_bn_long(bt, &entry.lastgame_active); bn_int_set(&entry.current.wins, account_get_ladder_wins(account, clienttag, id)); bn_int_set(&entry.current.loss, account_get_ladder_losses(account, clienttag, id)); bn_int_set(&entry.current.disconnect, account_get_ladder_disconnects(account, clienttag, id)); bn_int_set(&entry.current.rating, account_get_ladder_rating(account, clienttag, id)); bn_int_set(&entry.current.rank, account_get_ladder_rank(account, clienttag, id) - 1); if (!(timestr = account_get_ladder_last_time(account, clienttag, id))) timestr = BNETD_LADDER_DEFAULT_TIME; bnettime_set_str(&bt, timestr); bnettime_to_bn_long(bt, &entry.lastgame_current); } else { bn_int_set(&entry.active.wins, 0); bn_int_set(&entry.active.loss, 0); bn_int_set(&entry.active.disconnect, 0); bn_int_set(&entry.active.rating, 0); bn_int_set(&entry.active.rank, 0); bn_long_set_a_b(&entry.lastgame_active, 0, 0); bn_int_set(&entry.current.wins, 0); bn_int_set(&entry.current.loss, 0); bn_int_set(&entry.current.disconnect, 0); bn_int_set(&entry.current.rating, 0); bn_int_set(&entry.current.rank, 0); bn_long_set_a_b(&entry.lastgame_current, 0, 0); } bn_int_set(&entry.ttest[0], i); // rank bn_int_set(&entry.ttest[1], 0); // bn_int_set(&entry.ttest[2], 0); // if (account) bn_int_set(&entry.ttest[3], account_get_ladder_high_rating(account, clienttag, id)); else bn_int_set(&entry.ttest[3], 0); bn_int_set(&entry.ttest[4], 0); // bn_int_set(&entry.ttest[5], 0); // packet_append_data(rpacket, &entry, sizeof(entry)); if (account) packet_append_string(rpacket, account_get_name(account)); else packet_append_string(rpacket, " "); /* use a space so the client won't show the user's own account when double-clicked on */ } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_laddersearchreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_laddersearchreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LADDERSEARCHREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_laddersearchreq), packet_get_size(packet)); return -1; } { char const *playername; t_account *account; unsigned int idnum; unsigned int type; unsigned int rank; /* starts at zero */ t_ladder_id id; t_ladder_sort sort; t_clienttag ctag = conn_get_clienttag(c); idnum = bn_int_get(packet->u.client_laddersearchreq.id); switch (idnum) { case CLIENT_LADDERREQ_ID_STANDARD: id = ladder_id_normal; break; case CLIENT_LADDERREQ_ID_IRONMAN: id = ladder_id_ironman; break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] got unknown ladder laddersearchreq.id=0x{:08x}", conn_get_socket(c), idnum); id = ladder_id_normal; } type = bn_int_get(packet->u.client_laddersearchreq.type); switch (type) { case CLIENT_LADDERSEARCHREQ_TYPE_HIGHESTRATED: sort = ladder_sort_highestrated; break; case CLIENT_LADDERSEARCHREQ_TYPE_MOSTWINS: sort = ladder_sort_mostwins; break; case CLIENT_LADDERSEARCHREQ_TYPE_MOSTGAMES: sort = ladder_sort_mostgames; break; default: sort = ladder_sort_default; eventlog(eventlog_level_error, __FUNCTION__, "[{}] got unknown ladder search type {}", conn_get_socket(c), type); } if (!(playername = packet_get_str_const(packet, sizeof(t_client_laddersearchreq), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad LADDERSEARCHREQ packet (missing or too long playername)", conn_get_socket(c)); return -1; } if (!(account = accountlist_find_account(playername))) rank = SERVER_LADDERSEARCHREPLY_RANK_NONE; else { LadderList * ladderList_active = ladders.getLadderList(LadderKey(id, ctag, sort, ladder_time_active)); LadderList * ladderList_current = ladders.getLadderList(LadderKey(id, ctag, sort, ladder_time_current)); unsigned int uid = account_get_uid(account); switch (type) { case CLIENT_LADDERSEARCHREQ_TYPE_HIGHESTRATED: case CLIENT_LADDERSEARCHREQ_TYPE_MOSTWINS: case CLIENT_LADDERSEARCHREQ_TYPE_MOSTGAMES: if (!(rank = ladderList_active->getRank(uid))) { if (!(rank = ladderList_current->getRank(uid)) || ladderList_active->getReferencedObject(rank)) rank = 0; } break; default: rank = 0; eventlog(eventlog_level_error, __FUNCTION__, "[{}] got unknown ladder search type {}", conn_get_socket(c), bn_int_get(packet->u.client_laddersearchreq.type)); } if (rank == 0) rank = SERVER_LADDERSEARCHREPLY_RANK_NONE; else rank--; } if (!(rpacket = packet_create(packet_class_bnet))) return -1; packet_set_size(rpacket, sizeof(t_server_laddersearchreply)); packet_set_type(rpacket, SERVER_LADDERSEARCHREPLY); bn_int_set(&rpacket->u.server_laddersearchreply.rank, rank); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_mapauthreq1(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_mapauthreq1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad MAPAUTHREQ1 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_mapauthreq1), packet_get_size(packet)); return -1; } { char const *mapname; t_game *game; if (!(mapname = packet_get_str_const(packet, sizeof(t_client_mapauthreq1), MAP_NAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad MAPAUTHREQ1 packet (missing or too long mapname)", conn_get_socket(c)); return -1; } game = conn_get_game(c); if (game) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] map auth requested for map \"{}\" gametype \"{}\"", conn_get_socket(c), mapname, game_type_get_str(game_get_type(game))); game_set_mapname(game, mapname); } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] map auth requested when not in a game", conn_get_socket(c)); if ((rpacket = packet_create(packet_class_bnet))) { unsigned int val; if (!game) { val = SERVER_MAPAUTHREPLY1_NO; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] map authorization denied (not in a game)", conn_get_socket(c)); } else if (strcasecmp(game_get_mapname(game), mapname) != 0) { val = SERVER_MAPAUTHREPLY1_NO; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] map authorization denied (map name \"{}\" does not match game map name \"{}\")", conn_get_socket(c), mapname, game_get_mapname(game)); } else { game_set_status(game, game_status_started); if (game_get_type(game) == game_type_ladder) { val = SERVER_MAPAUTHREPLY1_LADDER_OK; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] giving map ladder authorization (in a ladder game)", conn_get_socket(c)); } else if (ladder_check_map(game_get_mapname(game), game_get_maptype(game), conn_get_clienttag(c))) { val = SERVER_MAPAUTHREPLY1_LADDER_OK; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] giving map ladder authorization (is a ladder map)", conn_get_socket(c)); } else { val = SERVER_MAPAUTHREPLY1_OK; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] giving map normal authorization", conn_get_socket(c)); } } packet_set_size(rpacket, sizeof(t_server_mapauthreply1)); packet_set_type(rpacket, SERVER_MAPAUTHREPLY1); bn_int_set(&rpacket->u.server_mapauthreply1.response, val); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_mapauthreq2(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_mapauthreq2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad MAPAUTHREQ2 packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_mapauthreq2), packet_get_size(packet)); return -1; } { char const *mapname; t_game *game; if (!(mapname = packet_get_str_const(packet, sizeof(t_client_mapauthreq2), MAP_NAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad MAPAUTHREQ2 packet (missing or too long mapname)", conn_get_socket(c)); return -1; } game = conn_get_game(c); if (game) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] map auth requested for map \"{}\" gametype \"{}\"", conn_get_socket(c), mapname, game_type_get_str(game_get_type(game))); game_set_mapname(game, mapname); } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] map auth requested when not in a game", conn_get_socket(c)); if ((rpacket = packet_create(packet_class_bnet))) { unsigned int val; if (!game) { val = SERVER_MAPAUTHREPLY2_NO; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] map authorization denied (not in a game)", conn_get_socket(c)); } else if (strcasecmp(game_get_mapname(game), mapname) != 0) { val = SERVER_MAPAUTHREPLY2_NO; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] map authorization denied (map name \"{}\" does not match game map name \"{}\")", conn_get_socket(c), mapname, game_get_mapname(game)); } else { game_set_status(game, game_status_started); if (game_get_type(game) == game_type_ladder) { val = SERVER_MAPAUTHREPLY2_LADDER_OK; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] giving map ladder authorization (in a ladder game)", conn_get_socket(c)); } else if (ladder_check_map(game_get_mapname(game), game_get_maptype(game), conn_get_clienttag(c))) { val = SERVER_MAPAUTHREPLY2_LADDER_OK; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] giving map ladder authorization (is a ladder map)", conn_get_socket(c)); } else { val = SERVER_MAPAUTHREPLY2_OK; eventlog(eventlog_level_debug, __FUNCTION__, "[{}] giving map normal authorization", conn_get_socket(c)); } } packet_set_size(rpacket, sizeof(t_server_mapauthreply2)); packet_set_type(rpacket, SERVER_MAPAUTHREPLY2); bn_int_set(&rpacket->u.server_mapauthreply2.response, val); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_changeclient(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_changeclient)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_CHANGECLIENT packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_changeclient), packet_get_size(packet)); return -1; } if (conn_get_clienttag(c) != CLIENTTAG_WAR3XP_UINT || bn_int_get(packet->u.client_changeclient.clienttag) != CLIENTTAG_WARCRAFT3_UINT) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] invalid attempt to change client from {X} to {X}", conn_get_socket(c), conn_get_clienttag(c), bn_int_get(packet->u.client_changeclient.clienttag)); conn_set_state(c, conn_state_destroy); return -1; } conn_set_clienttag(c, bn_int_get(packet->u.client_changeclient.clienttag)); eventlog(eventlog_level_info, __FUNCTION__, "[{}] changed client to {X}", conn_get_socket(c), bn_int_get(packet->u.client_changeclient.clienttag)); return 0; } static int _client_clanmemberlistreq(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_clanmemberlist_req)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLANMEMBERLIST_REQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clanmemberlist_req), packet_get_size(packet)); return -1; } clan_send_memberlist(c, packet); return 0; } static int _client_clan_motdreq(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_clan_motdreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_MOTDREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_motdreq), packet_get_size(packet)); return -1; } clan_send_motd_reply(c, packet); return 0; } static int _client_clan_motdchg(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_clan_motdreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_MOTDCHGREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_motdreq), packet_get_size(packet)); return -1; } clan_save_motd_chg(c, packet); return 0; } static int _client_clan_disbandreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_clan_disbandreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_DISBANDREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_disbandreq), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet))) { t_clan *clan; t_clanmember *member; t_account *account; packet_set_size(rpacket, sizeof(t_server_clan_disbandreply)); packet_set_type(rpacket, SERVER_CLAN_DISBANDREPLY); bn_int_set(&rpacket->u.server_clan_disbandreply.count, bn_int_get(packet->u.client_clan_disbandreq.count)); if (!((account = conn_get_account(c)) && (clan = account_get_clan(account)) && (member = account_get_clanmember(account)) && (clanmember_get_status(member) >= CLAN_CHIEFTAIN))) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] got suspicious CLAN_DISBANDREQ packet (request without required privileges)", conn_get_socket(c)); bn_byte_set(&rpacket->u.server_clan_disbandreply.result, CLAN_RESPONSE_NOT_AUTHORIZED); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } else if ((clanlist_remove_clan(clan) == 0) && (clan_remove(clan_get_clantag(clan)) == 0)) { bn_byte_set(&rpacket->u.server_clan_disbandreply.result, CLAN_RESPONSE_SUCCESS); clan_close_status_window_on_disband(clan); clan_send_packet_to_online_members(clan, rpacket); packet_del_ref(rpacket); clan_destroy(clan); } else { bn_byte_set(&rpacket->u.server_clan_disbandreply.result, CLAN_RESPONSE_FAIL); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_clan_createreq(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_clan_createreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_INFOREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_createreq), packet_get_size(packet)); return -1; } clan_get_possible_member(c, packet); return 0; } static int _client_clan_createinvitereq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; unsigned size; const char *clanname; const char *username; t_clantag clantag; unsigned offset; t_clan *clan; if ((size = packet_get_size(packet)) < sizeof(t_client_clan_createinvitereq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_CREATEINVITEREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_createinvitereq), packet_get_size(packet)); return -1; } offset = sizeof(t_client_clan_createinvitereq); if (!(clanname = packet_get_str_const(packet, offset, CLAN_NAME_MAX))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_CREATEINVITEREQ packet (missing clanname)", conn_get_socket(c)); return -1; } offset += (std::strlen(clanname) + 1); if (packet_get_size(packet) < offset + 4) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_CREATEINVITEREQ packet (missing clantag)", conn_get_socket(c)); return -1; } clantag = *((int *)packet_get_data_const(packet, offset, 4)); offset += 4; if ((rpacket = packet_create(packet_class_bnet))) { if ((clan = clan_create(conn_get_account(c), clantag, clanname, NULL)) && clanlist_add_clan(clan)) { char membercount; if (packet_get_size(packet) < offset + 1) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_CREATEINVITEREQ packet (missing membercount)", conn_get_socket(c)); packet_del_ref(rpacket); return -1; } membercount = *((char *)packet_get_data_const(packet, offset, 1)); clan_set_created(clan, -membercount); /* FIXME: We should also check if membercount == count of names on end of this packet */ packet_set_size(rpacket, sizeof(t_server_clan_createinvitereq)); packet_set_type(rpacket, SERVER_CLAN_CREATEINVITEREQ); bn_int_set(&rpacket->u.server_clan_createinvitereq.count, bn_int_get(packet->u.client_clan_createinvitereq.count)); bn_int_set(&rpacket->u.server_clan_createinvitereq.clantag, clantag); packet_append_string(rpacket, clanname); packet_append_string(rpacket, conn_get_username(c)); packet_append_data(rpacket, packet_get_data_const(packet, offset, size - offset), size - offset); /* Pelish: we will send bad packet if we got bad packet... */ offset++; do { username = packet_get_str_const(packet, offset, MAX_USERNAME_LEN); if (username) { t_connection *conn; offset += (std::strlen(username) + 1); if ((conn = connlist_find_connection_by_accountname(username)) != NULL) { t_clanmember *clanmember; if (prefs_get_clan_newer_time() > 0) { clanmember = clan_add_member(clan, conn_get_account(conn), CLAN_NEW); clanmember_set_fullmember(clanmember, 1); /* FIXME: do only this here and no clan_add_member() */ } else { clanmember = clan_add_member(clan, conn_get_account(conn), CLAN_PEON); clanmember_set_fullmember(clanmember, 1); /* FIXME: do only this here and no clan_add_member() */ } conn_push_outqueue(conn, rpacket); } } } while (username && (offset < size)); } else { packet_set_size(rpacket, sizeof(t_server_clan_createinvitereply)); packet_set_type(rpacket, SERVER_CLAN_CREATEINVITEREPLY); bn_int_set(&rpacket->u.server_clan_createinvitereply.count, bn_int_get(packet->u.client_clan_createinvitereply.count)); bn_byte_set(&rpacket->u.server_clan_createinvitereply.status, 0); } packet_del_ref(rpacket); } return 0; } static int _client_clan_createinvitereply(t_connection * c, t_packet const *const packet) { t_packet *rpacket; t_connection *conn; t_clan *clan; const char *username; char status; if (packet_get_size(packet) < sizeof(t_client_clan_createinvitereply)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_CREATEINVITEREPLY packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_createinvitereq), packet_get_size(packet)); return -1; } std::size_t offset = sizeof(t_client_clan_createinvitereply); username = packet_get_str_const(packet, offset, MAX_USERNAME_LEN); if (!username) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_CREATEINVITEREPLY packet (bad username)", conn_get_socket(c)); return -1; } offset += (std::strlen(username) + 1); if (packet_get_size(packet) < offset + 1) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_CREATEINVITEREPLY packet (mising status)", conn_get_socket(c)); return -1; } status = *((char *)packet_get_data_const(packet, offset, 1)); if ((conn = connlist_find_connection_by_accountname(username)) == NULL) return -1; if ((clan = account_get_creating_clan(conn_get_account(conn))) == NULL) return -1; if ((status != CLAN_RESPONSE_ACCEPT) && (rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_clan_createinvitereply)); packet_set_type(rpacket, SERVER_CLAN_CREATEINVITEREPLY); bn_int_set(&rpacket->u.server_clan_createinvitereply.count, bn_int_get(packet->u.client_clan_createinvitereply.count)); bn_byte_set(&rpacket->u.server_clan_createinvitereply.status, status); packet_append_string(rpacket, conn_get_username(c)); conn_push_outqueue(conn, rpacket); packet_del_ref(rpacket); if (clan) { clanlist_remove_clan(clan); clan_destroy(clan); } } else { int created = clan_get_created(clan); if (created > 0) { eventlog(eventlog_level_error, __FUNCTION__, "clan {} has already been created", clan_get_name(clan)); return 0; } created++; if ((created >= 0) && (rpacket = packet_create(packet_class_bnet))) { clan_set_created(clan, 1); clan_set_creation_time(clan, std::time(NULL)); packet_set_size(rpacket, sizeof(t_server_clan_createinvitereply)); packet_set_type(rpacket, SERVER_CLAN_CREATEINVITEREPLY); bn_int_set(&rpacket->u.server_clan_createinvitereply.count, bn_int_get(packet->u.client_clan_createinvitereply.count)); bn_byte_set(&rpacket->u.server_clan_createinvitereply.status, CLAN_RESPONSE_SUCCESS); packet_append_string(rpacket, ""); conn_push_outqueue(conn, rpacket); packet_del_ref(rpacket); clan_send_status_window_on_create(clan); clan_save(clan); } else clan_set_created(clan, created); } return 0; } static int _client_clanmember_rankupdatereq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_clanmember_rankupdate_req)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLANMEMBER_RANKUPDATE_REQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clanmember_rankupdate_req), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet)) != NULL) { std::size_t offset = sizeof(t_client_clanmember_rankupdate_req); char status; t_clan *clan; t_clanmember *dest_member; t_clanmember *member; t_account *account; packet_set_size(rpacket, sizeof(t_server_clanmember_rankupdate_reply)); packet_set_type(rpacket, SERVER_CLANMEMBER_RANKUPDATE_REPLY); bn_int_set(&rpacket->u.server_clanmember_rankupdate_reply.count, bn_int_get(packet->u.client_clanmember_rankupdate_req.count)); const char* const username = packet_get_str_const(packet, offset, MAX_USERNAME_LEN); if (!username) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] Could not retrieve username from CLANMEMBER_RANKUPDATE_REQ packet", conn_get_socket(c)); packet_del_ref(rpacket); return -1; } offset += (std::strlen(username) + 1); if (packet_get_size(packet) < offset + 1) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLANMEMBER_RANKUPDATE_REQ packet (mising status)", conn_get_socket(c)); packet_del_ref(rpacket); return -1; } status = *((char *)packet_get_data_const(packet, offset, 1)); account = (conn_get_account(c)); if ((clan = account_get_clan(account)) && (member = clan_find_member(clan, account)) && (dest_member = clan_find_member_by_name(clan, username)) && (member != dest_member)) { if ((status < CLAN_PEON) || (status > CLAN_SHAMAN)) { /* PELISH: CLAN_NEW can not be promoted to anything * and also noone can be promoted to CLAN_CHIEFTAIN */ DEBUG1("trying to change to bad status {}", status); bn_byte_set(&rpacket->u.server_clanmember_rankupdate_reply.result, SERVER_CLANMEMBER_RANKUPDATE_FAILED); } else if ((((clanmember_get_status(member) == CLAN_SHAMAN) && (status < CLAN_SHAMAN) && (clanmember_get_status(dest_member) < CLAN_SHAMAN)) || (clanmember_get_status(member) == CLAN_CHIEFTAIN)) && (clanmember_set_status(dest_member, status) == 0)) { bn_byte_set(&rpacket->u.server_clanmember_rankupdate_reply.result, SERVER_CLANMEMBER_RANKUPDATE_SUCCESS); clanmember_on_change_status(dest_member); } else { bn_byte_set(&rpacket->u.server_clanmember_rankupdate_reply.result, SERVER_CLANMEMBER_RANKUPDATE_FAILED); } } else { bn_byte_set(&rpacket->u.server_clanmember_rankupdate_reply.result, SERVER_CLANMEMBER_RANKUPDATE_FAILED); } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_clanmember_removereq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_clanmember_remove_req)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLANMEMBER_REMOVE_REQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clanmember_remove_req), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet)) != NULL) { t_account *acc; t_clan *clan; const char *username; t_clanmember *member; t_connection *dest_conn; packet_set_size(rpacket, sizeof(t_server_clanmember_remove_reply)); packet_set_type(rpacket, SERVER_CLANMEMBER_REMOVE_REPLY); bn_int_set(&rpacket->u.server_clanmember_remove_reply.count, bn_int_get(packet->u.client_clanmember_remove_req.count)); username = packet_get_str_const(packet, sizeof(t_client_clanmember_remove_req), MAX_USERNAME_LEN); bn_byte_set(&rpacket->u.server_clanmember_remove_reply.result, SERVER_CLANMEMBER_REMOVE_FAILED); // initially presume it failed if ((acc = conn_get_account(c)) && (clan = account_get_clan(acc)) && (member = clan_find_member_by_name(clan, username))) { dest_conn = clanmember_get_conn(member); if (clan_remove_member(clan, member) == 0) { t_packet *rpacket2; if (dest_conn) { clan_close_status_window(dest_conn); conn_update_w3_playerinfo(dest_conn); channel_rejoin(dest_conn); } if ((rpacket2 = packet_create(packet_class_bnet)) != NULL) { packet_set_size(rpacket2, sizeof(t_server_clanmember_removed_notify)); packet_set_type(rpacket2, SERVER_CLANMEMBER_REMOVED_NOTIFY); packet_append_string(rpacket2, username); clan_send_packet_to_online_members(clan, rpacket2); packet_del_ref(rpacket2); } bn_byte_set(&rpacket->u.server_clanmember_remove_reply.result, SERVER_CLANMEMBER_REMOVE_SUCCESS); } } conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_clan_membernewchiefreq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; if (packet_get_size(packet) < sizeof(t_client_clan_membernewchiefreq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLIENT_CLAN_MEMBERNEWCHIEFREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_createreq), packet_get_size(packet)); return -1; } if ((rpacket = packet_create(packet_class_bnet)) != NULL) { t_account *acc; t_clan *clan; t_clanmember *oldmember; t_clanmember *newmember; const char *username; packet_set_size(rpacket, sizeof(t_server_clan_membernewchiefreply)); packet_set_type(rpacket, SERVER_CLAN_MEMBERNEWCHIEFREPLY); bn_int_set(&rpacket->u.server_clan_membernewchiefreply.count, bn_int_get(packet->u.client_clan_membernewchiefreq.count)); username = packet_get_str_const(packet, sizeof(t_client_clan_membernewchiefreq), MAX_USERNAME_LEN); if ((acc = conn_get_account(c)) && (oldmember = account_get_clanmember(acc)) && (clanmember_get_status(oldmember) == CLAN_CHIEFTAIN) && (clan = clanmember_get_clan(oldmember)) && (newmember = clan_find_member_by_name(clan, username)) && (clanmember_set_status(oldmember, CLAN_GRUNT) == 0) && (clanmember_set_status(newmember, CLAN_CHIEFTAIN) == 0)) { clanmember_on_change_status(oldmember); clanmember_on_change_status(newmember); bn_byte_set(&rpacket->u.server_clan_membernewchiefreply.result, SERVER_CLAN_MEMBERNEWCHIEFREPLY_SUCCESS); clan_send_packet_to_online_members(clan, rpacket); packet_del_ref(rpacket); } else { bn_byte_set(&rpacket->u.server_clan_membernewchiefreply.result, SERVER_CLAN_MEMBERNEWCHIEFREPLY_FAILED); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } return 0; } static int _client_clan_invitereq(t_connection * c, t_packet const *const packet) { t_packet *rpacket; t_account *account; t_clan *clan; t_clanmember *member; t_clantag clantag; const char *username; t_connection *conn; t_account *conn_account; char response_code; if (packet_get_size(packet) < sizeof(t_client_clan_invitereq)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_INVITEREQ packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_invitereq), packet_get_size(packet)); return -1; } if (!(username = packet_get_str_const(packet, sizeof(t_client_clan_invitereq), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_INVITEREQ packet (missing or too long username)", conn_get_socket(c)); return -1; } if (rpacket = packet_create(packet_class_bnet)) { // user not authorized if (!((account = conn_get_account(c)) && (clan = account_get_clan(account)) && (member = account_get_clanmember(account)) && (clanmember_get_status(member) >= CLAN_SHAMAN) && (clantag = clan_get_clantag(clan)))) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] got suspicious CLAN_INVITEREQ packet (request without required privileges)", conn_get_socket(c)); response_code = CLAN_RESPONSE_NOT_AUTHORIZED; } else { // target user not online if (!((conn = connlist_find_connection_by_accountname(username)) && (conn_account = conn_get_account(conn)))) { response_code = CLAN_RESPONSE_NOT_FOUND; // target user already in a clan or creating a clan } else if (account_get_clanmember_forced(conn_get_account(conn))) { response_code = CLAN_RESPONSE_NOT_FOUND; // clan allready ful } else if (clan_get_member_count(clan) >= prefs_get_clan_max_members()) { response_code = CLAN_RESPONSE_CLAN_FULL; // valid invitereq } else { if (prefs_get_clan_newer_time() > 0) { clan_add_member(clan, conn_account, CLAN_NEW); } else { clan_add_member(clan, conn_account, CLAN_PEON); } packet_set_size(rpacket, sizeof(t_server_clan_invitereq)); packet_set_type(rpacket, SERVER_CLAN_INVITEREQ); bn_int_set(&rpacket->u.server_clan_invitereq.count, bn_int_get(packet->u.client_clan_invitereq.count)); bn_int_set(&rpacket->u.server_clan_invitereq.clantag, clantag); packet_append_string(rpacket, clan_get_name(clan)); packet_append_string(rpacket, conn_get_username(c)); conn_push_outqueue(conn, rpacket); packet_del_ref(rpacket); return 0; } } packet_set_size(rpacket, sizeof(t_server_clan_invitereply)); packet_set_type(rpacket, SERVER_CLAN_INVITEREPLY); bn_byte_set(&rpacket->u.server_clan_invitereply.result, response_code); bn_int_set(&rpacket->u.server_clan_invitereply.count, bn_int_get(packet->u.client_clan_invitereq.count)); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int _client_clan_invitereply(t_connection * c, t_packet const *const packet) { t_packet *rpacket; t_account *acc; t_clan *clan; t_clanmember *member; const char *username; t_connection *conn; t_account *conn_account; t_clan *conn_clan; t_clanmember *conn_member; char status; if (packet_get_size(packet) < sizeof(t_client_clan_invitereply)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_INVITEREPLY packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_clan_createreq), packet_get_size(packet)); return -1; } std::size_t offset = sizeof(t_client_clan_invitereply); if (!(username = packet_get_str_const(packet, offset, MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_INVITEREPLY packet (missing username)", conn_get_socket(c)); return -1; } offset += (std::strlen(username) + 1); if (packet_get_size(packet) < offset + 1) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CLAN_INVITEREPLY packet (mising status)", conn_get_socket(c)); return -1; } status = *((char *)packet_get_data_const(packet, offset, 1)); // reply without prior request if (!((acc = conn_get_account(c)) && (member = account_get_clanmember_forced(acc)) && (clan = clanmember_get_clan(member)) && (clanmember_get_fullmember(member) == 0))) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] got suspicious CLAN_INVITEREPLY packet (reply without prior request)", conn_get_socket(c)); return -1; // invalid inviter } else if (!((conn = connlist_find_connection_by_accountname(username)) && (conn_account = conn_get_account(conn)) && (conn_clan = account_get_clan(conn_account)) && (conn_member = account_get_clanmember(conn_account)) && (clanmember_get_status(conn_member) >= CLAN_SHAMAN) && (clan_get_clantag(clan) == clan_get_clantag(conn_clan)))) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] got suspicious CLAN_INVITEREPLY packet (invalid inviter)", conn_get_socket(c)); return -1; } if (rpacket = packet_create(packet_class_bnet)) { packet_set_size(rpacket, sizeof(t_server_clan_invitereply)); packet_set_type(rpacket, SERVER_CLAN_INVITEREPLY); bn_int_set(&rpacket->u.server_clan_invitereply.count, bn_int_get(packet->u.client_clan_invitereply.count)); if (status != CLAN_RESPONSE_ACCEPT) { clan_remove_member(clan, member); bn_byte_set(&rpacket->u.server_clan_invitereply.result, status); } else { if (clan_get_member_count(clan) >= prefs_get_clan_max_members()) { clan_remove_member(clan, member); bn_byte_set(&rpacket->u.server_clan_invitereply.result, CLAN_RESPONSE_CLAN_FULL); } else { clanmember_set_fullmember(member, 1); if (conn_get_channel(c)) { conn_update_w3_playerinfo(c); channel_set_userflags(c); std::string channelname("Clan " + std::string(clantag_to_str(clan_get_clantag(clan)))); if (conn_set_channel(c, channelname.c_str()) < 0) { conn_set_channel(c, CHANNEL_NAME_BANNED); /* should not fail */ } clanmember_set_online(c); } clan_send_status_window(c); bn_byte_set(&rpacket->u.server_clan_invitereply.result, CLAN_RESPONSE_SUCCESS); } } } conn_push_outqueue(conn, rpacket); packet_del_ref(rpacket); return 0; } static int _client_crashdump(t_connection * c, t_packet const *const packet) { return 0; } static int _client_setemailreply(t_connection * c, t_packet const *const packet) { char const *email; t_account *account; if (!(email = packet_get_str_const(packet, sizeof(t_client_setemailreply), MAX_EMAIL_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad SETEMAILREPLY packet", conn_get_socket(c)); return -1; } if (!(account = conn_get_account(c))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account for connection in setemail request"); return -1; } if (account_get_email(account)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] account \"{}\" already have email set, ignore set email", conn_get_socket(c), account_get_name(account)); return 0; } if (account_set_email(account, email) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] failed to init account \"{}\" email to \"{}\"", conn_get_socket(c), account_get_name(account), email); return 0; } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] init account \"{}\" email to \"{}\"", conn_get_socket(c), account_get_name(account), email); return 0; } static int _client_changeemailreq(t_connection * c, t_packet const *const packet) { char const *oldaddr; char const *newaddr; char const *username; char const *email; t_account *account; int pos; pos = sizeof(t_client_changeemailreq); if (!(username = packet_get_str_const(packet, pos, MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad username in CHANGEEMAILREQ packet", conn_get_socket(c)); return -1; } pos += (std::strlen(username) + 1); if (!(oldaddr = packet_get_str_const(packet, pos, MAX_EMAIL_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad old email in CHANGEEMAILREQ packet", conn_get_socket(c)); return -1; } pos += (std::strlen(oldaddr) + 1); if (!(newaddr = packet_get_str_const(packet, pos, MAX_EMAIL_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad new email in CHANGEEMAILREQ packet", conn_get_socket(c)); return -1; } if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] change email for \"{}\" refused (no such account)", conn_get_socket(c), username); return 0; } if (!(email = account_get_email(account)) || !email[0]) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] account \"{}\" do not have email set, ignore changing", conn_get_socket(c), account_get_name(account)); return 0; } if (strcasecmp(email, oldaddr)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] account \"{}\" email mismatch, ignore changing", conn_get_socket(c), account_get_name(account)); return 0; } if (account_set_email(account, newaddr) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] failed to change account \"{}\" email to \"{}\"", conn_get_socket(c), account_get_name(account), newaddr); return 0; } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] change account \"{}\" email to \"{}\"", conn_get_socket(c), account_get_name(account), newaddr); return 0; } static int _client_getpasswordreq(t_connection * c, t_packet const *const packet) { char const *username; char const *try_email; char const *email; t_account *account; int pos; pos = sizeof(t_client_getpasswordreq); if (!(username = packet_get_str_const(packet, pos, MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad username in GETPASSWORDREQ packet", conn_get_socket(c)); return -1; } pos += (std::strlen(username) + 1); if (!(try_email = packet_get_str_const(packet, pos, MAX_EMAIL_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad email in GETPASSWORDREQ packet", conn_get_socket(c)); return -1; } if (!(account = accountlist_find_account(username))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] get password for \"{}\" refused (no such account)", conn_get_socket(c), username); return 0; } if (!(email = account_get_email(account)) || !email[0]) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] account \"{}\" do not have email set, ignore get password", conn_get_socket(c), account_get_name(account)); return 0; } if (strcasecmp(email, try_email)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] account \"{}\" email mismatch, ignore get password", conn_get_socket(c), account_get_name(account)); return 0; } /* TODO: send mail to user with the real password or changed password!? * (as we cannot get the real password back, we should only change the password) --Soar */ eventlog(eventlog_level_info, __FUNCTION__, "[{}] get password for account \"{}\" to email \"{}\"", conn_get_socket(c), account_get_name(account), email); return 0; } static int _client_extrawork(t_connection * c, t_packet const *const packet) { if (packet_get_size(packet) < sizeof(t_client_extrawork)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad EXTRAWORK packet (expected {} bytes, got {})", conn_get_socket(c), sizeof(t_client_extrawork), packet_get_size(packet)); return -1; } { short gametype; short length; const char * data; std::string data_s; gametype = bn_short_get(packet->u.client_extrawork.gametype); length = bn_short_get(packet->u.client_extrawork.length); if (!(data = (const char *)packet_get_raw_data_const(packet, sizeof(t_client_extrawork)))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad EXTRAWORK packet (missing or too long data)", conn_get_socket(c)); return -1; } // extract substring with given length data_s = std::string(data).substr(0, length); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] Received EXTRAWORK packet with GameType: {} and Length: {} ({})", conn_get_socket(c), gametype, length, data_s.c_str()); #ifdef WITH_LUA lua_handle_client_extrawork(c, gametype, length, data_s.c_str()); #endif } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_bnet.h000066400000000000000000000021531462640461400205000ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_BNET_PROTOS #define INCLUDED_HANDLE_BNET_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_bnet_packet(t_connection * c, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_bot.cpp000066400000000000000000000301401462640461400206640ustar00rootroot00000000000000/* * Copyright (C) 1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_bot.h" #include #include #include "common/eventlog.h" #include "common/packet.h" #include "common/bnethash.h" #include "common/tag.h" #include "common/xstring.h" #include "connection.h" #include "account.h" #include "account_wrap.h" #include "message.h" #include "channel.h" #include "command.h" #ifdef WITH_LUA #include "luainterface.h" #endif #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern int handle_bot_packet(t_connection * c, t_packet const * const packet) { t_packet * rpacket; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", conn_get_socket(c)); return -1; } if (packet_get_class(packet) != packet_class_raw) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", conn_get_socket(c), (int)packet_get_class(packet)); return -1; } { char const * const linestr = packet_get_str_const(packet, 0, MAX_MESSAGE_LEN); if (packet_get_size(packet) < 2) /* empty line */ return 0; if (!linestr) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] line too long", conn_get_socket(c)); return 0; } switch (conn_get_state(c)) { case conn_state_connected: conn_add_flags(c, MF_PLUG); conn_set_clienttag(c, CLIENTTAG_BNCHATBOT_UINT); { char const * temp = linestr; if (temp[0] == '\004') /* FIXME: no echo, ignore for now (we always do no echo) */ temp = &temp[1]; if (temp[0] == '\0') /* empty line */ { conn_set_state(c, conn_state_bot_username); /* don't look for ^D or reset tag and flags */ break; } conn_set_state(c, conn_state_bot_password); if (conn_set_loggeduser(c, temp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not set username to \"{}\"", conn_get_socket(c), temp); { char const * const msg = "\r\nPassword: "; if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } #if 1 /* don't echo */ packet_append_ntstring(rpacket, conn_get_loggeduser(c)); #endif packet_append_ntstring(rpacket, msg); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } break; case conn_state_bot_username: conn_set_state(c, conn_state_bot_password); if (conn_set_loggeduser(c, linestr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not set username to \"{}\"", conn_get_socket(c), linestr); { char const * const temp = "\r\nPassword: "; if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } #if 1 /* don't echo */ packet_append_ntstring(rpacket, linestr); #endif packet_append_ntstring(rpacket, temp); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } break; case conn_state_bot_password: { char const * const tempa = "\r\nLogin failed.\r\n\r\nUsername: "; char const * const tempb = "\r\nAccount has no bot access.\r\n\r\nUsername: "; char const * loggeduser = conn_get_loggeduser(c); t_account * account; char const * oldstrhash1; t_hash trypasshash1; t_hash oldpasshash1; char * testpass; if (!loggeduser) /* error earlier in login */ { /* no std::log message... */ conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if (connlist_find_connection_by_accountname(loggeduser)) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (already logged in)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if (!(account = accountlist_find_account(loggeduser))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (bad account)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if ((oldstrhash1 = account_get_pass(account))) { if (hash_set_str(&oldpasshash1, oldstrhash1) < 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (corrupted passhash1?)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } testpass = xstrdup(linestr); { strtolower(testpass); } if (bnet_hash(&trypasshash1, std::strlen(testpass), testpass) < 0) /* FIXME: force to lowercase */ { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (unable to hash password)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); xfree((void *)testpass); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } xfree((void *)testpass); if (hash_eq(trypasshash1, oldpasshash1) != 1) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (wrong password)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if (account_get_auth_botlogin(account) != 1) /* default to false */ { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (no bot access)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempb); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } else if (account_get_auth_lock(account) == 1) /* default to false */ { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (this account is locked)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempb); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" bot logged in (correct password)", conn_get_socket(c), loggeduser); #ifdef WITH_LUA if (lua_handle_user(c, NULL, NULL, luaevent_user_login) == 1) { // feature to break login from Lua conn_set_state(c, conn_state_destroy); break; } #endif } else { eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" bot logged in (no password)", conn_get_socket(c), loggeduser); } if (!(rpacket = packet_create(packet_class_raw))) /* if we got this far, let them std::log in even if this fails */ eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); else { packet_append_ntstring(rpacket, "\r\n"); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } conn_login(c, account, loggeduser); message_send_text(c, message_type_uniqueid, c, loggeduser); if (conn_set_channel(c, CHANNEL_NAME_CHAT) < 0) conn_set_channel(c, CHANNEL_NAME_BANNED); /* should not fail */ } break; case conn_state_loggedin: { t_channel const * channel; conn_set_idletime(c); if ((channel = conn_get_channel(c))) channel_message_log(channel, c, 1, linestr); /* we don't log game commands currently */ if (linestr[0] == '/') handle_command(c, linestr); else if (channel && !conn_quota_exceeded(c, linestr)) channel_message_send(channel, message_type_talk, c, linestr); /* else discard */ } break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown bot connection state {}", conn_get_socket(c), (int)conn_get_state(c)); } } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_bot.h000066400000000000000000000021501462640461400203310ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_BOT_PROTOS #define INCLUDED_HANDLE_BOT_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_bot_packet(t_connection * c, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_d2cs.cpp000066400000000000000000000335131462640461400207420ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_d2cs.h" #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/bn_type.h" #include "common/addr.h" #include "common/bnethash.h" #include "common/bnethashconv.h" #include "common/tag.h" #include "common/util.h" #include "realm.h" #include "prefs.h" #include "account_wrap.h" #include "game.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static int on_d2cs_accountloginreq(t_connection * c, t_packet const * packet); static int on_d2cs_charloginreq(t_connection * c, t_packet const * packet); static int on_d2cs_authreply(t_connection * c, t_packet const * packet); static int on_d2cs_gameinforeply(t_connection * c, t_packet const * packet); extern int handle_d2cs_packet(t_connection * c, t_packet const * packet) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (packet_get_class(packet) != packet_class_d2cs_bnetd) { eventlog(eventlog_level_error, __FUNCTION__, "got bad packet class {}", packet_get_class(packet)); return -1; } switch (conn_get_state(c)) { case conn_state_connected: switch (packet_get_type(packet)) { case D2CS_BNETD_AUTHREPLY: on_d2cs_authreply(c, packet); break; default: eventlog(eventlog_level_error, __FUNCTION__, "got unknown packet type {}", packet_get_type(packet)); break; } break; case conn_state_loggedin: switch (packet_get_type(packet)) { case D2CS_BNETD_ACCOUNTLOGINREQ: on_d2cs_accountloginreq(c, packet); break; case D2CS_BNETD_CHARLOGINREQ: on_d2cs_charloginreq(c, packet); break; case D2CS_BNETD_GAMEINFOREPLY: on_d2cs_gameinforeply(c, packet); break; default: eventlog(eventlog_level_error, __FUNCTION__, "got unknown packet type {}", packet_get_type(packet)); break; } break; default: eventlog(eventlog_level_error, __FUNCTION__, "got unknown connection state {}", conn_get_state(c)); break; } return 0; } static int on_d2cs_authreply(t_connection * c, t_packet const * packet) { t_packet * rpacket; unsigned int version; unsigned int try_version; unsigned int reply; char const * realmname; t_realm * realm; if (packet_get_size(packet) < sizeof(t_d2cs_bnetd_authreply)) { eventlog(eventlog_level_error, __FUNCTION__, "got bad packet size"); return -1; } if (!(realmname = packet_get_str_const(packet, sizeof(t_d2cs_bnetd_authreply), MAX_REALMNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "got bad realmname"); return -1; } if (!(realm = realmlist_find_realm(realmname))) { realm = realmlist_find_realm_by_ip(conn_get_addr(c)); /* should not fail - checked in handle_init_packet() handle_init.c */ eventlog(eventlog_level_warn, __FUNCTION__, "warn: realm name mismatch {} {}", realm_get_name(realm), realmname); if (!(prefs_allow_d2cs_setname())) { /* fail if allow_d2cs_setname = false */ eventlog(eventlog_level_error, __FUNCTION__, "d2cs not allowed to set realm name"); return -1; } if (realm_get_active(realm)) { /* fail if realm already active */ eventlog(eventlog_level_error, __FUNCTION__, "cannot set realm name to {} (realm already active)", realmname); return -1; } realm_set_name(realm, realmname); } version = prefs_get_d2cs_version(); try_version = bn_int_get(packet->u.d2cs_bnetd_authreply.version); if (version && version != try_version) { eventlog(eventlog_level_error, __FUNCTION__, "d2cs version mismatch 0x{:X} - 0x{:X}", try_version, version); reply = BNETD_D2CS_AUTHREPLY_BAD_VERSION; } else { reply = BNETD_D2CS_AUTHREPLY_SUCCEED; } if (reply == BNETD_D2CS_AUTHREPLY_SUCCEED) { eventlog(eventlog_level_info, __FUNCTION__, "d2cs {} authed", addr_num_to_ip_str(conn_get_addr(c))); conn_set_state(c, conn_state_loggedin); realm_active(realm, c); } else { eventlog(eventlog_level_error, __FUNCTION__, "failed to auth d2cs {}", addr_num_to_ip_str(conn_get_addr(c))); } if ((rpacket = packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket, sizeof(t_bnetd_d2cs_authreply)); packet_set_type(rpacket, BNETD_D2CS_AUTHREPLY); bn_int_set(&rpacket->u.bnetd_d2cs_authreply.h.seqno, 1); bn_int_set(&rpacket->u.bnetd_d2cs_authreply.reply, reply); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int on_d2cs_accountloginreq(t_connection * c, t_packet const * packet) { unsigned int sessionkey; unsigned int sessionnum; unsigned int salt; char const * account; char const * tname; t_connection * client; int reply; t_packet * rpacket; struct { bn_int salt; bn_int sessionkey; bn_int sessionnum; bn_int secret; bn_int passhash[5]; } temp; t_hash secret_hash; char const * pass_str; t_hash passhash; t_hash try_hash; if (packet_get_size(packet) < sizeof(t_d2cs_bnetd_accountloginreq)) { eventlog(eventlog_level_error, __FUNCTION__, "got bad packet size"); return -1; } if (!(account = packet_get_str_const(packet, sizeof(t_d2cs_bnetd_accountloginreq), MAX_USERNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "missing or too long account name"); return -1; } sessionkey = bn_int_get(packet->u.d2cs_bnetd_accountloginreq.sessionkey); sessionnum = bn_int_get(packet->u.d2cs_bnetd_accountloginreq.sessionnum); salt = bn_int_get(packet->u.d2cs_bnetd_accountloginreq.seqno); if (!(client = connlist_find_connection_by_sessionnum(sessionnum))) { eventlog(eventlog_level_error, __FUNCTION__, "sessionnum {} not found", sessionnum); reply = BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else if (sessionkey != conn_get_sessionkey(client)) { eventlog(eventlog_level_error, __FUNCTION__, "sessionkey {} not match", sessionkey); reply = BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else if (!(tname = conn_get_username(client))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL username"); reply = BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else if (strcasecmp(account, tname)) { eventlog(eventlog_level_error, __FUNCTION__, "username {} not match", account); reply = BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else { bn_int_set(&temp.salt, salt); bn_int_set(&temp.sessionkey, sessionkey); bn_int_set(&temp.sessionnum, sessionnum); bn_int_set(&temp.secret, conn_get_secret(client)); pass_str = account_get_pass(conn_get_account(client)); if (hash_set_str(&passhash, pass_str) < 0) { reply = BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } else { hash_to_bnhash((t_hash const *)&passhash, temp.passhash); bnet_hash(&secret_hash, sizeof(temp), &temp); bnhash_to_hash(packet->u.d2cs_bnetd_accountloginreq.secret_hash, &try_hash); if (hash_eq(try_hash, secret_hash) == 1) { eventlog(eventlog_level_debug, __FUNCTION__, "user {} loggedin on d2cs", account); reply = BNETD_D2CS_ACCOUNTLOGINREPLY_SUCCEED; } else { eventlog(eventlog_level_error, __FUNCTION__, "user {} hash not match", account); reply = BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED; } } } if ((rpacket = packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket, sizeof(t_bnetd_d2cs_accountloginreply)); packet_set_type(rpacket, BNETD_D2CS_ACCOUNTLOGINREPLY); bn_int_set(&rpacket->u.bnetd_d2cs_accountloginreply.h.seqno, bn_int_get(packet->u.d2cs_bnetd_accountloginreq.h.seqno)); bn_int_set(&rpacket->u.bnetd_d2cs_accountloginreply.reply, reply); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } #define CHAR_PORTRAIT_LEN 0x30 static int on_d2cs_charloginreq(t_connection * c, t_packet const * packet) { t_connection * client; char const * charname; char const * portrait; char const * clienttag; char * temp; unsigned int sessionnum; t_realm * realm; char const * realmname; unsigned int pos, reply; t_packet * rpacket; if (packet_get_size(packet) < sizeof(t_d2cs_bnetd_charloginreq)) { eventlog(eventlog_level_error, __FUNCTION__, "got bad packet size"); return -1; } sessionnum = bn_int_get(packet->u.d2cs_bnetd_charloginreq.sessionnum); pos = sizeof(t_d2cs_bnetd_charloginreq); if (!(charname = packet_get_str_const(packet, pos, MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "got bad character name"); return -1; } pos += std::strlen(charname) + 1; if (!(portrait = packet_get_str_const(packet, pos, CHAR_PORTRAIT_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "got bad character portrait"); return -1; } if (!(client = connlist_find_connection_by_sessionnum(sessionnum))) { eventlog(eventlog_level_error, __FUNCTION__, "user {} not found", sessionnum); reply = BNETD_D2CS_CHARLOGINREPLY_FAILED; } else if (!(clienttag = clienttag_uint_to_str(conn_get_clienttag(client)))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clienttag"); reply = BNETD_D2CS_CHARLOGINREPLY_FAILED; } else if (!(realm = conn_get_realm(client))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); reply = BNETD_D2CS_CHARLOGINREPLY_FAILED; } else { char revtag[8]; realmname = realm_get_name(realm); temp = (char*)xmalloc(std::strlen(clienttag) + std::strlen(realmname) + 1 + std::strlen(charname) + 1 + std::strlen(portrait) + 1); reply = BNETD_D2CS_CHARLOGINREPLY_SUCCEED; std::strcpy(revtag, clienttag); strreverse(revtag); std::sprintf(temp, "%4s%s,%s,%s", revtag, realmname, charname, portrait); conn_set_charname(client, charname); conn_set_realminfo(client, temp); xfree(temp); eventlog(eventlog_level_debug, __FUNCTION__, "loaded portrait for character {}", charname); } if ((rpacket = packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket, sizeof(t_bnetd_d2cs_charloginreply)); packet_set_type(rpacket, BNETD_D2CS_CHARLOGINREPLY); bn_int_set(&rpacket->u.bnetd_d2cs_charloginreply.h.seqno, bn_int_get(packet->u.d2cs_bnetd_charloginreq.h.seqno)); bn_int_set(&rpacket->u.bnetd_d2cs_charloginreply.reply, reply); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } extern int handle_d2cs_init(t_connection * c) { t_packet * packet; if ((packet = packet_create(packet_class_d2cs_bnetd))) { packet_set_size(packet, sizeof(t_bnetd_d2cs_authreq)); packet_set_type(packet, BNETD_D2CS_AUTHREQ); bn_int_set(&packet->u.bnetd_d2cs_authreq.h.seqno, 1); bn_int_set(&packet->u.bnetd_d2cs_authreq.sessionnum, conn_get_sessionnum(c)); conn_push_outqueue(c, packet); packet_del_ref(packet); } eventlog(eventlog_level_info, __FUNCTION__, "sent init packet to d2cs (sessionnum={})", conn_get_sessionnum(c)); return 0; } extern int send_d2cs_gameinforeq(t_connection * c) { t_packet * packet; t_game * game; t_realm * realm; if (!(c)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn"); return -1; } if (!(game = conn_get_game(c))) { eventlog(eventlog_level_error, __FUNCTION__, "conn had NULL game"); return -1; } if (!(realm = conn_get_realm(c))) { eventlog(eventlog_level_error, __FUNCTION__, "conn had NULL realm"); return -1; } if ((packet = packet_create(packet_class_d2cs_bnetd))) { packet_set_size(packet, sizeof(t_bnetd_d2cs_gameinforeq)); packet_set_type(packet, BNETD_D2CS_GAMEINFOREQ); bn_int_set(&packet->u.bnetd_d2cs_gameinforeq.h.seqno, 0); packet_append_string(packet, game_get_name(game)); conn_push_outqueue(realm_get_conn(realm), packet); packet_del_ref(packet); } return 0; } static int on_d2cs_gameinforeply(t_connection * c, t_packet const * packet) { t_game * game; char const * gamename; unsigned int difficulty; t_game_difficulty diff; if (!(c)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!(gamename = packet_get_str_const(packet, sizeof(t_d2cs_bnetd_gameinforeply), MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "missing or too long gamename"); return -1; } if (!(game = gamelist_find_game(gamename, CLIENTTAG_DIABLO2DV_UINT, game_type_diablo2closed)) && !(game = gamelist_find_game(gamename, CLIENTTAG_DIABLO2XP_UINT, game_type_diablo2closed))) { eventlog(eventlog_level_error, __FUNCTION__, "reply for unknown game \"{}\"", gamename); return -1; } difficulty = bn_byte_get(packet->u.d2cs_bnetd_gameinforeply.difficulty); switch (difficulty) { case 0: diff = game_difficulty_normal; break; case 1: diff = game_difficulty_nightmare; break; case 2: diff = game_difficulty_hell; break; default: diff = game_difficulty_none; } game_set_difficulty(game, diff); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_d2cs.h000066400000000000000000000021461462640461400204050ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_D2CS_H #define INCLUDED_HANDLE_D2CS_H #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace bnetd { extern int handle_d2cs_packet(t_connection * c, t_packet const * packet); extern int handle_d2cs_init(t_connection * c); extern int send_d2cs_gameinforeq(t_connection * c); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_file.cpp000066400000000000000000000073231462640461400210260ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999,2000 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_file.h" #include "compat/psock.h" #include "common/eventlog.h" #include "common/packet.h" #include "common/bn_type.h" #include "connection.h" #include "file.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern int handle_file_packet(t_connection * c, t_packet const * const packet) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", conn_get_socket(c)); return -1; } /* REMOVED BY UNDYING SOULZZ 4/3/02 */ /* if (packet_get_class(packet)!=packet_class_file) { eventlog(eventlog_level_error,__FUNCTION__,"[%d] got bad packet (class %d)",conn_get_socket(c),(int)packet_get_class(packet)); return -1; } */ switch (conn_get_state(c)) { case conn_state_connected: switch (packet_get_type(packet)) { case CLIENT_FILE_REQ: { char const * rawname; if (!(rawname = packet_get_str_const(packet, sizeof(t_client_file_req), MAX_FILENAME_STR))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad FILE_REQ (missing or too long filename)", conn_get_socket(c)); return -1; } file_send(c, rawname, bn_int_get(packet->u.client_file_req.adid), bn_int_get(packet->u.client_file_req.extensiontag), bn_int_get(packet->u.client_file_req.startoffset), 1); } break; case CLIENT_FILE_REQ2: { t_packet * rpacket = NULL; if ((rpacket = packet_create(packet_class_raw))) { packet_set_size(rpacket, sizeof(t_server_file_unknown1)); bn_int_set(&rpacket->u.server_file_unknown1.unknown, 0xdeadbeef); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } conn_set_state(c, conn_state_pending_raw); break; } default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown file packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); break; } break; case conn_state_pending_raw: switch (packet_get_type(packet)) { case CLIENT_FILE_REQ3: { char rawname[MAX_FILENAME_STR] = {}; psock_recv(conn_get_socket(c), rawname, MAX_FILENAME_STR, 0); file_send(c, rawname, 0, 0, 0, 1); } break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown file packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); break; } break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown file connection state {}", conn_get_socket(c), (int)conn_get_state(c)); } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_file.h000066400000000000000000000021531462640461400204670ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_FILE_PROTOS #define INCLUDED_HANDLE_FILE_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_file_packet(t_connection * c, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_init.cpp000066400000000000000000000116631462640461400210540ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_init.h" #include "common/init_protocol.h" #include "common/eventlog.h" #include "common/packet.h" #include "common/bn_type.h" #include "common/addr.h" #include "prefs.h" #include "connection.h" #include "realm.h" #include "handle_d2cs.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern int handle_init_packet(t_connection * c, t_packet const * const packet) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", conn_get_socket(c)); return -1; } if (packet_get_class(packet) != packet_class_init) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", conn_get_socket(c), (int)packet_get_class(packet)); return -1; } if ((prefs_get_max_conns_per_IP() != 0) && bn_byte_get(packet->u.client_initconn.cclass) != CLIENT_INITCONN_CLASS_D2CS_BNETD && (connlist_count_connections(conn_get_addr(c)) > prefs_get_max_conns_per_IP())) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] too many connections from address {} (closing connection)", conn_get_socket(c), addr_num_to_addr_str(conn_get_addr(c), conn_get_port(c))); return -1; } switch (packet_get_type(packet)) { case CLIENT_INITCONN: switch (bn_byte_get(packet->u.client_initconn.cclass)) { case CLIENT_INITCONN_CLASS_BNET: eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated bnet connection", conn_get_socket(c)); conn_set_state(c, conn_state_connected); conn_set_class(c, conn_class_bnet); break; case CLIENT_INITCONN_CLASS_FILE: eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated file download connection", conn_get_socket(c)); conn_set_state(c, conn_state_connected); conn_set_class(c, conn_class_file); break; case CLIENT_INITCONN_CLASS_BOT: eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated chat bot connection", conn_get_socket(c)); conn_set_state(c, conn_state_connected); conn_set_class(c, conn_class_bot); break; case CLIENT_INITCONN_CLASS_TELNET: eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated telnet connection", conn_get_socket(c)); conn_set_state(c, conn_state_connected); conn_set_class(c, conn_class_telnet); break; case CLIENT_INITCONN_CLASS_D2CS_BNETD: { eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated d2cs_bnetd connection", conn_get_socket(c)); if (!(realmlist_find_realm_by_ip(conn_get_addr(c)))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] d2cs connection from unknown ip address {}", conn_get_socket(c), addr_num_to_addr_str(conn_get_addr(c), conn_get_port(c))); return -1; } conn_set_state(c, conn_state_connected); conn_set_class(c, conn_class_d2cs_bnetd); if (handle_d2cs_init(c) < 0) { eventlog(eventlog_level_info, __FUNCTION__, "faild to init d2cs connection"); return -1; } } break; case CLIENT_INITCONN_CLASS_ENC: eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated encrypted connection (not supported)", conn_get_socket(c)); return -1; break; case CLIENT_INITCONN_CLASS_LOCALMACHINE: eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated connection from local computer to 127.0.0.1", conn_get_socket(c)); /* conn_set_state(c, conn_state_connected); conn_set_class(c, conn_class_localmachine; */ return -1; break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] client requested unknown class 0x{:02x} (length {}) (closing connection)", conn_get_socket(c), (unsigned int)bn_byte_get(packet->u.client_initconn.cclass), packet_get_size(packet)); return -1; } break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown init packet type 0x{:04x}, len {}", conn_get_socket(c), packet_get_type(packet), packet_get_size(packet)); return -1; } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_init.h000066400000000000000000000021531462640461400205130ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_INIT_PROTOS #define INCLUDED_HANDLE_INIT_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_init_packet(t_connection * c, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_irc.cpp000066400000000000000000000503441462640461400206650ustar00rootroot00000000000000/* * Copyright (C) 2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2005 Bryan Biedenkapp (gatekeep@gmail.com) * Copyright (C) 2006,2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_irc.h" #include #include #include #include "compat/strcasecmp.h" #include "common/irc_protocol.h" #include "common/eventlog.h" #include "common/bnethash.h" #include "common/tag.h" #include "common/util.h" #include "common/list.h" #include "common/addr.h" #include "common/xstring.h" #include "prefs.h" #include "command.h" #include "irc.h" #include "account.h" #include "account_wrap.h" #include "command_groups.h" #include "channel.h" #include "message.h" #include "tick.h" #include "topic.h" #include "server.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef int(*t_irc_command)(t_connection * conn, int numparams, char ** params, char * text); typedef struct { const char * irc_command_string; t_irc_command irc_command_handler; } t_irc_command_table_row; static int _handle_user_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_pass_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_privmsg_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_notice_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_quit_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_who_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_list_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_names_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_userhost_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_ison_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_whois_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_part_command(t_connection * conn, int numparams, char ** params, char * text); /* state "connected" handlers */ static const t_irc_command_table_row irc_con_command_table[] = { { "NICK", _handle_nick_command }, { "USER", _handle_user_command }, { "PING", _handle_ping_command }, { "PONG", _handle_pong_command }, { "PASS", _handle_pass_command }, { "PRIVMSG", _handle_privmsg_command }, { "NOTICE", _handle_notice_command }, { "QUIT", _handle_quit_command }, { NULL, NULL } }; /* state "logged in" handlers */ static const t_irc_command_table_row irc_log_command_table[] = { { "WHO", _handle_who_command }, { "LIST", _handle_list_command }, { "TOPIC", _handle_topic_command }, { "JOIN", _handle_join_command }, { "NAMES", _handle_names_command }, { "MODE", _handle_mode_command }, { "USERHOST", _handle_userhost_command }, { "ISON", _handle_ison_command }, { "WHOIS", _handle_whois_command }, { "PART", _handle_part_command }, { "KICK", _handle_kick_command }, { "TIME", _handle_time_command }, { NULL, NULL } }; extern int handle_irc_con_command(t_connection * conn, char const * command, int numparams, char ** params, char * text) { t_irc_command_table_row const *p; for (p = irc_con_command_table; p->irc_command_string != NULL; p++) { if (strcasecmp(command, p->irc_command_string) == 0) { if (p->irc_command_handler != NULL) return ((p->irc_command_handler)(conn, numparams, params, text)); } } return -1; } extern int handle_irc_log_command(t_connection * conn, char const * command, int numparams, char ** params, char * text) { t_irc_command_table_row const *p; for (p = irc_log_command_table; p->irc_command_string != NULL; p++) { if (strcasecmp(command, p->irc_command_string) == 0) { if (p->irc_command_handler != NULL) return ((p->irc_command_handler)(conn, numparams, params, text)); } } return -1; } extern int handle_irc_welcome(t_connection * conn) { char temp[MAX_IRC_MESSAGE_LEN]; std::time_t temptime; char const * tempname; char const * temptimestr; if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } tempname = conn_get_loggeduser(conn); if ((34 + std::strlen(tempname) + 1) <= MAX_IRC_MESSAGE_LEN) std::sprintf(temp, ":Welcome to the %s IRC Network %s", prefs_get_irc_network_name(), tempname); else std::sprintf(temp, ":Maximum length exceeded"); irc_send(conn, RPL_WELCOME, temp); if ((14 + std::strlen(server_get_hostname()) + 10 + std::strlen(PVPGN_SOFTWARE " " PVPGN_VERSION) + 1) <= MAX_IRC_MESSAGE_LEN) std::sprintf(temp, ":Your host is %s, running " PVPGN_SOFTWARE " " PVPGN_VERSION, server_get_hostname()); else std::sprintf(temp, ":Maximum length exceeded"); irc_send(conn, RPL_YOURHOST, temp); temptime = server_get_starttime(); /* FIXME: This should be build time */ temptimestr = std::ctime(&temptime); if ((25 + std::strlen(temptimestr) + 1) <= MAX_IRC_MESSAGE_LEN) std::sprintf(temp, ":This server was created %s", temptimestr); /* FIXME: is ctime() portable? */ else std::sprintf(temp, ":Maximum length exceeded"); irc_send(conn, RPL_CREATED, temp); /* we don't give mode information on MYINFO we give it on ISUPPORT */ if ((std::strlen(server_get_hostname()) + 7 + std::strlen(PVPGN_SOFTWARE " " PVPGN_VERSION) + 9 + 1) <= MAX_IRC_MESSAGE_LEN) std::sprintf(temp, "%s " PVPGN_SOFTWARE " " PVPGN_VERSION " - -", server_get_hostname()); else std::sprintf(temp, ":Maximum length exceeded"); irc_send(conn, RPL_MYINFO, temp); std::sprintf(temp, "NICKLEN=%d TOPICLEN=%d CHANNELLEN=%d PREFIX=%s CHANTYPES=" CHANNEL_TYPE " NETWORK=%s IRCD=" PVPGN_SOFTWARE, MAX_CHARNAME_LEN, MAX_TOPIC_LEN, MAX_CHANNELNAME_LEN, CHANNEL_PREFIX, prefs_get_irc_network_name()); if ((std::strlen(temp)) <= MAX_IRC_MESSAGE_LEN) irc_send(conn, RPL_ISUPPORT, temp); else { std::sprintf(temp, ":Maximum length exceeded"); irc_send(conn, RPL_ISUPPORT, temp); } irc_send_motd(conn); message_send_text(conn, message_type_notice, NULL, "This is an experimental service"); conn_set_state(conn, conn_state_bot_password); if (conn_get_ircpass(conn)) { message_send_text(conn, message_type_notice, NULL, "Trying to authenticate with PASS ..."); irc_authenticate(conn, conn_get_ircpass(conn)); } else { message_send_text(conn, message_type_notice, NULL, "No PASS command received. Please identify yourself by /msg NICKSERV identify ."); } return 0; } static int _handle_user_command(t_connection * conn, int numparams, char ** params, char * text) { /* RFC 2812 says: */ /* :*/ /* ircII and X-Chat say: */ /* mz SHODAN localhost :Marco Ziech */ /* BitchX says: */ /* mz +iws mz :Marco Ziech */ /* Don't bother with, params 1 and 2 anymore they don't contain what they should. */ char * user = NULL; char * realname = NULL; if ((numparams >= 3) && (params[0]) && (text)) { user = params[0]; realname = text; if (conn_get_user(conn)) { irc_send(conn, ERR_ALREADYREGISTRED, ":You are already registred"); } else { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got USER: user=\"{}\" realname=\"{}\"", conn_get_socket(conn), user, realname); conn_set_user(conn, user); conn_set_owner(conn, realname); if (conn_get_loggeduser(conn)) handle_irc_welcome(conn); /* only send the welcome if we have USER and NICK */ } } else { irc_send(conn, ERR_NEEDMOREPARAMS, "USER :Not enough parameters"); } return 0; } static int _handle_pass_command(t_connection * conn, int numparams, char ** params, char * text) { if ((!conn_get_ircpass(conn)) && (conn_get_state(conn) == conn_state_bot_username)) { t_hash h; if (numparams >= 1) { bnet_hash(&h, std::strlen(params[0]), params[0]); conn_set_ircpass(conn, hash_get_str(h)); } else irc_send(conn, ERR_NEEDMOREPARAMS, "PASS :Not enough parameters"); } else { irc_send(conn, ERR_ALREADYREGISTRED, ":Unauthorized command (already registered)"); } return 0; } static int _handle_privmsg_command(t_connection * conn, int numparams, char ** params, char * text) { if ((numparams >= 1) && (text)) { int i; char ** e; e = irc_get_listelems(params[0]); /* FIXME: support wildcards! */ /* start amadeo: code was sent by some unkown fellow of pvpgn (maybe u wanna give us your name for any credits), it adds nick-registration, i changed some things here and there... */ for (i = 0; ((e) && (e[i])); i++) { if (strcasecmp(e[i], "NICKSERV") == 0) { char * pass; pass = std::strchr(text, ' '); if (pass) *pass++ = '\0'; if (strcasecmp(text, "identify") == 0) { switch (conn_get_state(conn)) { case conn_state_bot_password: { if (pass) { t_hash h; strtolower(pass); bnet_hash(&h, std::strlen(pass), pass); irc_authenticate(conn, hash_get_str(h)); } else { message_send_text(conn, message_type_notice, NULL, "Syntax: IDENTIFY (max 16 characters)"); } break; } case conn_state_loggedin: { message_send_text(conn, message_type_notice, NULL, "You don't need to IDENTIFY"); break; } default:; eventlog(eventlog_level_trace, __FUNCTION__, "got /msg in unexpected connection state ({})", conn_state_get_str(conn_get_state(conn))); } } else if (strcasecmp(text, "register") == 0) { t_hash passhash; t_account * temp; char * username = (char *)conn_get_loggeduser(conn); if (account_check_name(username)<0) { message_send_text(conn, message_type_error, conn, "Account name contains invalid symbol!"); break; } if (!prefs_get_allow_new_accounts()){ message_send_text(conn, message_type_error, conn, "Account creation is not allowed"); break; } if (!pass || pass[0] == '\0' || (std::strlen(pass)>16)) { message_send_text(conn, message_type_error, conn, "Syntax: REGISTER (max 16 characters)"); break; } strtolower(pass); bnet_hash(&passhash, std::strlen(pass), pass); message_send_text(conn, message_type_info, conn, std::string("Trying to create account \"" + std::string(username) + "\" with password \"" + std::string(pass) + "\"").c_str()); temp = accountlist_create_account(username, hash_get_str(passhash)); if (!temp) { message_send_text(conn, message_type_error, conn, "Failed to create account!"); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account \"{}\" not created (failed)", conn_get_socket(conn), username); conn_unget_chatname(conn, username); break; } message_send_text(conn, message_type_info, conn, std::string("Account #" + std::to_string(account_get_uid(temp)) + " created.")); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] account \"{}\" created", conn_get_socket(conn), username); conn_unget_chatname(conn, username); } else { message_send_text(conn, message_type_notice, nullptr, "Invalid arguments for NICKSERV"); message_send_text(conn, message_type_notice, nullptr, std::string(":Unrecognized command \"" + std::string(text) + "\"").c_str()); } } else if (conn_get_state(conn) == conn_state_loggedin) { if (e[i][0] == '#') { /* channel message */ t_channel * channel; if ((channel = channellist_find_channel_by_name(irc_convert_ircname(e[i]), NULL, NULL))) { if ((std::strlen(text) >= 9) && (std::strncmp(text, "\001ACTION ", 8) == 0) && (text[std::strlen(text) - 1] == '\001')) { /* at least "\001ACTION \001" */ /* it's a CTCP ACTION message */ text = text + 8; text[std::strlen(text) - 1] = '\0'; channel_message_send(channel, message_type_emote, conn, text); } else { channel_message_log(channel, conn, 1, text); channel_message_send(channel, message_type_talk, conn, text); } } else { irc_send(conn, ERR_NOSUCHCHANNEL, ":No such channel"); } } else { /* whisper */ t_connection * user; if ((user = connlist_find_connection_by_accountname(e[i]))) { message_send_text(user, message_type_whisper, conn, text); } else { irc_send(conn, ERR_NOSUCHNICK, ":No such user"); } } } } if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "PRIVMSG :Not enough parameters"); return 0; } static int _handle_notice_command(t_connection * conn, int numparams, char ** params, char * text) { if ((numparams >= 1) && (text)) { int i; char ** e; e = irc_get_listelems(params[0]); /* FIXME: support wildcards! */ for (i = 0; ((e) && (e[i])); i++) { if (conn_get_state(conn) == conn_state_loggedin) { t_connection * user; if ((user = connlist_find_connection_by_accountname(e[i]))) { message_send_text(user, message_type_notice, conn, text); } else { irc_send(conn, ERR_NOSUCHNICK, ":No such user"); } } } if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "NOTICE :Not enough parameters"); return 0; } static int _handle_who_command(t_connection * conn, int numparams, char ** params, char * text) { if (numparams >= 1) { int i; char ** e; e = irc_get_listelems(params[0]); for (i = 0; ((e) && (e[i])); i++) { irc_who(conn, e[i]); } irc_send(conn, RPL_ENDOFWHO, ":End of WHO list"); /* RFC2812 only requires this to be sent if a list of names was given. Undernet seems to always send it, so do we :) */ if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "WHO :Not enough parameters"); return 0; } static int _handle_list_command(t_connection * conn, int numparams, char ** params, char * text) { std::string tmp; irc_send(conn, RPL_LISTSTART, "Channel :Users Names"); /* backward compatibility */ if (numparams == 0) { t_elem const * curr; class_topic Topic; LIST_TRAVERSE_CONST(channellist(), curr) { t_channel const * channel = (const t_channel*)elem_get_data(curr); char const * tempname = irc_convert_channel(channel, conn); std::string topicstr = Topic.get(channel_get_name(channel)); /* FIXME: AARON: only list channels like in /channels command */ tmp = std::string(tempname) + " " + std::to_string(channel_get_length(channel)) + " :" + topicstr; if (tmp.length() > MAX_IRC_MESSAGE_LEN) eventlog(eventlog_level_warn, __FUNCTION__, "LISTREPLY length exceeded"); irc_send(conn, RPL_LIST, tmp.c_str()); } } else if (numparams >= 1) { int i; char ** e; class_topic Topic; e = irc_get_listelems(params[0]); /* FIXME: support wildcards! */ for (i = 0; ((e) && (e[i])); i++) { char const * verytemp = irc_convert_ircname(e[i]); if (!verytemp) continue; /* something is wrong with the name ... */ t_channel const * channel = channellist_find_channel_by_name(verytemp, NULL, NULL); if (!channel) continue; /* channel doesn't exist */ std::string topicstr = Topic.get(channel_get_name(channel)); char const * tempname = irc_convert_channel(channel, conn); tmp = std::string(tempname) + " " + std::to_string(channel_get_length(channel)) + " :" + topicstr; if (tmp.length() > MAX_IRC_MESSAGE_LEN) eventlog(eventlog_level_warn, __FUNCTION__, "LISTREPLY length exceeded"); irc_send(conn, RPL_LIST, tmp.c_str()); } if (e) irc_unget_listelems(e); } irc_send(conn, RPL_LISTEND, ":End of LIST command"); return 0; } static int _handle_names_command(t_connection * conn, int numparams, char ** params, char * text) { t_channel * channel; if (numparams >= 1) { char ** e; char const * verytemp; int i; e = irc_get_listelems(params[0]); for (i = 0; ((e) && (e[i])); i++) { verytemp = irc_convert_ircname(e[i]); if (!verytemp) continue; /* something is wrong with the name ... */ channel = channellist_find_channel_by_name(verytemp, NULL, NULL); if (!channel) continue; /* channel doesn't exist */ irc_send_rpl_namreply(conn, channel); } if (e) irc_unget_listelems(e); } else if (numparams == 0) { irc_send_rpl_namreply(conn, NULL); } return 0; } static int _handle_userhost_command(t_connection * conn, int numparams, char ** params, char * text) { /* FIXME: Send RPL_USERHOST */ return 0; } static int _handle_quit_command(t_connection * conn, int numparams, char ** params, char * text) { conn_quit_channel(conn, text); conn_set_state(conn, conn_state_destroy); return 0; } static int _handle_ison_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; char first = 1; if (numparams >= 1) { int i; temp[0] = '\0'; for (i = 0; (i < numparams && (params) && (params[i])); i++) { if (connlist_find_connection_by_accountname(params[i])) { std::snprintf(temp, sizeof temp, "%s%s", first ? ":" : " ", params[i]); first = 0; } } irc_send(conn, RPL_ISON, temp); } else irc_send(conn, ERR_NEEDMOREPARAMS, "ISON :Not enough parameters"); return 0; } static int _handle_whois_command(t_connection * conn, int numparams, char ** params, char * text) { std::string tmp; if (numparams >= 1) { int i; char ** e = irc_get_listelems(params[0]); t_connection * c; t_channel * chan; for (i = 0; ((e) && (e[i])); i++) { if ((c = connlist_find_connection_by_accountname(e[i]))) { if (prefs_get_hide_addr() && !(account_get_command_groups(conn_get_account(conn)) & command_get_group("/admin-addr"))) tmp = std::string(e[i]) + " " + std::string(clienttag_uint_to_str(conn_get_clienttag(c))) + " hidden * :PvPGN user"; else tmp = std::string(e[i]) + " " + std::string(clienttag_uint_to_str(conn_get_clienttag(c))) + " " + std::string(addr_num_to_ip_str(conn_get_addr(c))) + " * :PvPGN user"; irc_send(conn, RPL_WHOISUSER, tmp.c_str()); if ((chan = conn_get_channel(conn))) { std::string flg; auto flags = conn_get_flags(c); if (flags & MF_BLIZZARD) flg = '@'; else if ((flags & MF_BNET) || (flags & MF_GAVEL)) flg = '%'; else if (flags & MF_VOICE) flg = '+'; else flg = ' '; tmp = std::string(e[i]) + " :" + flg + std::string(irc_convert_channel(chan, conn)); irc_send(conn, RPL_WHOISCHANNELS, tmp.c_str()); } } else irc_send(conn, ERR_NOSUCHNICK, ":No such nick/channel"); } irc_send(conn, RPL_ENDOFWHOIS, ":End of /WHOIS list"); if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "WHOIS :Not enough parameters"); return 0; } static int _handle_part_command(t_connection * conn, int numparams, char ** params, char * text) { conn_part_channel(conn); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_irc.h000066400000000000000000000025001462640461400203210ustar00rootroot00000000000000/* * Copyright (C) 2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_IRC_PROTOS #define INCLUDED_HANDLE_IRC_PROTOS #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace bnetd { extern int handle_irc_con_command(t_connection * conn, char const * command, int numparams, char ** params, char * text); extern int handle_irc_log_command(t_connection * conn, char const * command, int numparams, char ** params, char * text); extern int handle_irc_welcome(t_connection * conn); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_irc_common.cpp000066400000000000000000000260541462640461400222360ustar00rootroot00000000000000/* * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_irc_common.h" #include #include #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/util.h" #include "common/irc_protocol.h" #include "handle_irc.h" #include "handle_wol.h" #include "handle_wserv.h" #include "prefs.h" #include "command.h" #include "irc.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static int handle_irc_common_con_command(t_connection * conn, char const * command, int numparams, char ** params, char * text) { if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } switch (conn_get_class(conn)) { case conn_class_irc: return handle_irc_con_command(conn, command, numparams, params, text); case conn_class_wserv: return handle_wserv_con_command(conn, command, numparams, params, text); case conn_class_wol: case conn_class_wladder: case conn_class_wgameres: return handle_wol_con_command(conn, command, numparams, params, text); default: return handle_irc_con_command(conn, command, numparams, params, text); } } static int handle_irc_common_log_command(t_connection * conn, char const * command, int numparams, char ** params, char * text) { if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } switch (conn_get_class(conn)) { case conn_class_irc: return handle_irc_log_command(conn, command, numparams, params, text); case conn_class_wol: case conn_class_wgameres: return handle_wol_log_command(conn, command, numparams, params, text); default: return handle_irc_log_command(conn, command, numparams, params, text); } } static int handle_irc_common_set_class(t_connection * conn, char const * command, int numparams, char ** params, char * text) { if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (conn_get_class(conn) != conn_class_ircinit) { DEBUG0("FIXME: conn_get_class(conn) != conn_class_ircinit"); return -1; } else { if (strcasecmp(command, "VERCHK") == 0) { DEBUG0("Got WSERV packet"); if (std::strcmp(prefs_get_wolv2_addrs(), "") != 0) conn_set_class(conn, conn_class_wserv); else conn_set_state(conn, conn_state_destroy); return 0; } else if (strcasecmp(command, "CVERS") == 0) { DEBUG0("Got WOL packet"); /* FIXME: We can check it not by address but check if client is supported by tag_check_in_list() */ if ((std::strcmp(prefs_get_wolv1_addrs(), "") != 0) || (std::strcmp(prefs_get_wolv2_addrs(), "") != 0)) conn_set_class(conn, conn_class_wol); else conn_set_state(conn, conn_state_destroy); return 0; } else if ((strcasecmp(command, "LISTSEARCH") == 0) || (strcasecmp(command, "RUNGSEARCH") == 0) || (strcasecmp(command, "HIGHSCORE") == 0)) { DEBUG0("Got WOL Ladder packet"); if (std::strcmp(prefs_get_wolv2_addrs(), "") != 0) conn_set_class(conn, conn_class_wladder); /* is handled in handle_wol.* now */ else conn_set_state(conn, conn_state_destroy); return 0; } else if ((strcasecmp(command, "CRYPT") == 0) || (strcasecmp(command, "LOGIN") == 0)) { DEBUG0("Got GameSpy packet"); if (std::strcmp(prefs_get_irc_addrs(), "") != 0) conn_set_class(conn, conn_class_irc); // conn_set_class(conn,conn_class_gspy_peerchat); else conn_set_state(conn, conn_state_destroy); return 0; } else { DEBUG0("Got IRC packet"); if (std::strcmp(prefs_get_irc_addrs(), "") != 0) conn_set_class(conn, conn_class_irc); else conn_set_state(conn, conn_state_destroy); return 0; } } } static int handle_irc_common_line(t_connection * conn, char const * ircline) { /* [:prefix] [[param1] [param2] ... [paramN]] [:] */ char * line; /* copy of ircline */ char * prefix = NULL; /* optional; mostly NULL */ char * command; /* mandatory */ char ** params = NULL; /* optional (array of params) */ char * text = NULL; /* optional */ char * bnet_command = NULL; /* amadeo: used for battle.net.commands */ int unrecognized_before = 0; int linelen; /* amadeo: counter for stringlenghts */ int numparams = 0; char * tempparams; int i; if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!ircline) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL ircline"); return -1; } if (ircline[0] == '\0') { /** * PELISH: We dont send warning about that because it is client-side problem. * eventlog(eventlog_level_error,__FUNCTION__,"got empty ircline"); */ return -1; } //amadeo: code was sent by some unknown fellow of pvpgn, prevents buffer-overflow for // too long irc-lines //PELISH: According to RFC2812 we do truncation on 512 byte if (std::strlen(ircline) > MAX_IRC_MESSAGE_LEN) { char * tmp = (char *)ircline; eventlog(eventlog_level_warn, __FUNCTION__, "line too long, truncation..."); tmp[MAX_IRC_MESSAGE_LEN] = '\0'; } line = xstrdup(ircline); /* split the message */ if (line[0] == ':') { /* The prefix is optional and is rarely provided */ prefix = line; if (!(command = std::strchr(line, ' '))) { eventlog(eventlog_level_warn, __FUNCTION__, "got malformed line (missing command)"); xfree(line); return -1; } *command++ = '\0'; } else { /* In most cases command is the first thing on the line */ command = line; } tempparams = std::strchr(command, ' '); if (tempparams) { *tempparams++ = '\0'; if (tempparams[0] == ':') { text = tempparams + 1; /* theres just text, no params. skip the colon */ } else { for (i = 0; tempparams[i] != '\0'; i++) { if ((tempparams[i] == ' ') && (tempparams[i + 1] == ':')) { text = tempparams + i; *text++ = '\0'; text++; /* skip the colon */ break; /* text found, stop search */ } } params = irc_get_paramelems(tempparams); } } if (params) { /* count parameters */ for (numparams = 0; params[numparams]; numparams++); } { std::string paramtemp; bool first = true; for (i = 0; ((numparams > 0) && (params[i])); i++) { if (first) { first = false; } else { paramtemp.append(" "); } paramtemp.append("\"" + std::string(params[i]) + "\""); } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got \"{}\" \"{}\" [{}] \"{}\"", conn_get_socket(conn), ((prefix) ? (prefix) : ("")), command, paramtemp, ((text) ? (text) : (""))); } if (conn_get_class(conn) == conn_class_ircinit) { handle_irc_common_set_class(conn, command, numparams, params, text); } if (conn_get_state(conn) == conn_state_connected) { conn_set_state(conn, conn_state_bot_username); /* PELISH: What is this for? */ if ((conn_get_class(conn) != conn_class_wserv) && (conn_get_class(conn) != conn_class_wladder)) { t_timer_data temp; temp.n = prefs_get_irc_latency(); conn_test_latency(conn, std::time(NULL), temp); } } if (handle_irc_common_con_command(conn, command, numparams, params, text) != -1) {} else if (conn_get_state(conn) != conn_state_loggedin) { std::string tmp(":Unrecognized command \"" + std::string(command) + "\" (before login)"); if (tmp.length() > MAX_IRC_MESSAGE_LEN) irc_send(conn, ERR_UNKNOWNCOMMAND, tmp.c_str()); else irc_send(conn, ERR_UNKNOWNCOMMAND, ":Unrecognized command (before login)"); } else { /* command is handled later */ unrecognized_before = 1; } /* --- The following should only be executable after login --- */ if ((conn_get_state(conn) == conn_state_loggedin) && (unrecognized_before)) { if (handle_irc_common_log_command(conn, command, numparams, params, text) != -1) {} else if ((strstart(command, "LAG") != 0) && (strstart(command, "JOIN") != 0)){ linelen = std::strlen(ircline); bnet_command = (char*)xmalloc(linelen + 2); bnet_command[0] = '/'; std::strcpy(bnet_command + 1, ircline); handle_command(conn, bnet_command); xfree((void*)bnet_command); } } /* loggedin */ if (params) irc_unget_paramelems(params); xfree(line); return 0; } extern int handle_irc_common_packet(t_connection * conn, t_packet const * const packet) { unsigned int i; char ircline[MAX_IRC_MESSAGE_LEN]; char const * data; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if ((conn_get_class(conn) != conn_class_ircinit) && (conn_get_class(conn) != conn_class_irc) && (conn_get_class(conn) != conn_class_wol) && (conn_get_class(conn) != conn_class_wserv) && (conn_get_class(conn) != conn_class_wladder)) { eventlog(eventlog_level_error, __FUNCTION__, "FIXME: handle_irc_packet without any reason (conn->class != conn_class_irc/ircinit/wol/wserv...)"); return -1; } // eventlog(eventlog_level_debug,__FUNCTION__,"got \"%s\"",packet_get_raw_data_const(packet,0)); std::memset(ircline, 0, sizeof(ircline)); data = conn_get_ircline(conn); /* fetch current status */ if (data) std::snprintf(ircline, sizeof ircline, "%s", data); unsigned ircpos = std::strlen(ircline); data = (const char *)packet_get_raw_data_const(packet, 0); for (i = 0; i < packet_get_size(packet); i++) { if (data[i] == '\n') { /* end of line */ handle_irc_common_line(conn, ircline); std::memset(ircline, 0, sizeof(ircline)); ircpos = 0; } else { if (ircpos < MAX_IRC_MESSAGE_LEN - 1) ircline[ircpos++] = data[i]; else { ircpos++; /* for the statistic :) */ eventlog(eventlog_level_warn, __FUNCTION__, "[{}] client exceeded maximum allowed message length by {} characters", conn_get_socket(conn), ircpos - MAX_IRC_MESSAGE_LEN); if (ircpos > 100 + MAX_IRC_MESSAGE_LEN) { /* automatic flood protection */ eventlog(eventlog_level_error, __FUNCTION__, "[{}] excess flood", conn_get_socket(conn)); return -1; } } } } conn_set_ircline(conn, ircline); /* write back current status */ return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_irc_common.h000066400000000000000000000021121462640461400216700ustar00rootroot00000000000000/* * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_IRC_COMMON_PROTOS #define INCLUDED_HANDLE_IRC_COMMON_PROTOS #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace bnetd { extern int handle_irc_common_packet(t_connection * conn, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_telnet.cpp000066400000000000000000000302031462640461400213730ustar00rootroot00000000000000/* * Copyright (C) 2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_telnet.h" #include #include #include "common/eventlog.h" #include "common/packet.h" #include "common/bnethash.h" #include "common/xalloc.h" #include "common/tag.h" #include "common/xstring.h" #include "connection.h" #include "account.h" #include "account_wrap.h" #include "message.h" #include "channel.h" #include "command.h" #ifdef WITH_LUA #include "luainterface.h" #endif #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern int handle_telnet_packet(t_connection * c, t_packet const * const packet) { t_packet * rpacket; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection", conn_get_socket(c)); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", conn_get_socket(c)); return -1; } if (packet_get_class(packet) != packet_class_raw) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", conn_get_socket(c), (int)packet_get_class(packet)); return -1; } { char const * const linestr = packet_get_str_const(packet, 0, MAX_MESSAGE_LEN); if (packet_get_size(packet) < 2) /* empty line */ return 0; if (!linestr) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] line too long", conn_get_socket(c)); return 0; } switch (conn_get_state(c)) { case conn_state_connected: conn_add_flags(c, MF_PLUG); conn_set_clienttag(c, CLIENTTAG_BNCHATBOT_UINT); { char const * temp = linestr; if (temp[0] == '\004') /* FIXME: no echo, ignore for now (we always do no echo) */ temp = &temp[1]; if (temp[0] == '\0') /* empty line */ { conn_set_state(c, conn_state_bot_username); /* don't look for ^D or reset tag and flags */ break; } conn_set_state(c, conn_state_bot_password); if (conn_set_loggeduser(c, temp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not set username to \"{}\"", conn_get_socket(c), temp); { char const * const msg = "\r\nPassword: "; if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } #if 0 /* don't echo */ packet_append_ntstring(rpacket, conn_get_loggeduser(c)); #endif packet_append_ntstring(rpacket, msg); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } } break; case conn_state_bot_username: conn_set_state(c, conn_state_bot_password); if (conn_set_loggeduser(c, linestr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not set username to \"{}\"", conn_get_socket(c), linestr); { char const * const temp = "\r\nPassword: "; if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } #if 0 /* don't echo */ packet_append_ntstring(rpacket, linestr); #endif packet_append_ntstring(rpacket, temp); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } break; case conn_state_bot_password: { char const * const tempa = "\r\nLogin failed.\r\n\r\nUsername: "; char const * const tempb = "\r\nAccount has no bot access.\r\n\r\nUsername: "; char const * const loggeduser = conn_get_loggeduser(c); t_account * account; char const * oldstrhash1; t_hash trypasshash1; t_hash oldpasshash1; char * testpass; if (!loggeduser) /* error earlier in login */ { /* no std::log message... */ conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if (connlist_find_connection_by_accountname(loggeduser)) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (already logged in)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if (!(account = accountlist_find_account(loggeduser))) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (bad account)", conn_get_socket(c), loggeduser); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if ((oldstrhash1 = account_get_pass(account))) { if (hash_set_str(&oldpasshash1, oldstrhash1) < 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (corrupted passhash1?)", conn_get_socket(c), account_get_name(account)); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } testpass = xstrdup(linestr); { strtolower(testpass); } if (bnet_hash(&trypasshash1, std::strlen(testpass), testpass) < 0) /* FIXME: force to lowercase */ { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (unable to hash password)", conn_get_socket(c), account_get_name(account)); xfree(testpass); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } xfree(testpass); if (hash_eq(trypasshash1, oldpasshash1) != 1) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (wrong password)", conn_get_socket(c), account_get_name(account)); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempa); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } if (account_get_auth_botlogin(account) != 1) /* default to false */ { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (no bot access)", conn_get_socket(c), account_get_name(account)); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempb); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } else if (account_get_auth_lock(account) == 1) /* default to false */ { eventlog(eventlog_level_info, __FUNCTION__, "[{}] bot login for \"{}\" refused (this account is locked)", conn_get_socket(c), account_get_name(account)); conn_set_state(c, conn_state_bot_username); if (!(rpacket = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); break; } packet_append_ntstring(rpacket, tempb); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); break; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" bot logged in (correct password)", conn_get_socket(c), account_get_name(account)); #ifdef WITH_LUA if (lua_handle_user(c, NULL, NULL, luaevent_user_login) == 1) { // feature to break login from Lua conn_set_state(c, conn_state_destroy); break; } #endif } else eventlog(eventlog_level_info, __FUNCTION__, "[{}] \"{}\" bot logged in (no password)", conn_get_socket(c), account_get_name(account)); if (!(rpacket = packet_create(packet_class_raw))) /* if we got this far, let them std::log in even if this fails */ eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not create rpacket", conn_get_socket(c)); else { packet_append_ntstring(rpacket, "\r\n"); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } message_send_text(c, message_type_uniqueid, c, loggeduser); conn_login(c, account, loggeduser); if (conn_set_channel(c, CHANNEL_NAME_CHAT) < 0) conn_set_channel(c, CHANNEL_NAME_BANNED); /* should not fail */ } break; case conn_state_loggedin: { t_channel const * channel; conn_set_idletime(c); if ((channel = conn_get_channel(c))) channel_message_log(channel, c, 1, linestr); /* we don't log game commands currently */ if (linestr[0] == '/') handle_command(c, linestr); else if (channel && !conn_quota_exceeded(c, linestr)) channel_message_send(channel, message_type_talk, c, linestr); /* else discard */ } break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown telnet connection state {}", conn_get_socket(c), (int)conn_get_state(c)); } } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_telnet.h000066400000000000000000000021611462640461400210420ustar00rootroot00000000000000/* * Copyright (C) 2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_TELNET_PROTOS #define INCLUDED_HANDLE_TELNET_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_telnet_packet(t_connection * c, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_udp.cpp000066400000000000000000000125441462640461400207000ustar00rootroot00000000000000/* * Copyright (C) 1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_udp.h" #include "common/eventlog.h" #include "common/packet.h" #include "common/addr.h" #include "common/bn_type.h" #include "connection.h" #include "udptest_send.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern int handle_udp_packet(int usock, unsigned int src_addr, unsigned short src_port, t_packet const * const packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", usock); return -1; } if (packet_get_class(packet) != packet_class_udp) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", usock, (int)packet_get_class(packet)); return -1; } switch (packet_get_type(packet)) { case SERVER_UDPTEST: /* we might get these if a client is running on the same machine as us */ if (packet_get_size(packet) < sizeof(t_server_udptest)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UDPTEST packet (expected {} bytes, got {})", usock, sizeof(t_server_udptest), packet_get_size(packet)); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got UDPTEST packet from {} (myself?)", usock, addr_num_to_addr_str(src_addr, src_port)); return 0; case CLIENT_UDPPING: if (packet_get_size(packet) < sizeof(t_client_udpping)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UDPPING packet (expected {} bytes, got {})", usock, sizeof(t_client_udpping), packet_get_size(packet)); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got udpping unknown1={}", usock, bn_int_get(packet->u.client_udpping.unknown1)); return 0; case CLIENT_SESSIONADDR1: if (packet_get_size(packet) < sizeof(t_client_sessionaddr1)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad SESSIONADDR1 packet (expected {} bytes, got {})", usock, sizeof(t_client_sessionaddr1), packet_get_size(packet)); return -1; } { t_connection * c; if (!(c = connlist_find_connection_by_sessionkey(bn_int_get(packet->u.client_sessionaddr1.sessionkey)))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] address not set (no connection with session key 0x{:08x})", usock, bn_int_get(packet->u.client_sessionaddr1.sessionkey)); return -1; } if (conn_get_game_addr(c) != src_addr || conn_get_game_port(c) != src_port) eventlog(eventlog_level_info, __FUNCTION__, "[{}][{}] SESSIONADDR1 set new UDP address to {}", conn_get_socket(c), usock, addr_num_to_addr_str(src_addr, src_port)); conn_set_game_socket(c, usock); conn_set_game_addr(c, src_addr); conn_set_game_port(c, src_port); udptest_send(c); } return 0; case CLIENT_SESSIONADDR2: if (packet_get_size(packet) < sizeof(t_client_sessionaddr2)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad SESSIONADDR2 packet (expected {} bytes, got {})", usock, sizeof(t_client_sessionaddr2), packet_get_size(packet)); return -1; } { t_connection * c; if (!(c = connlist_find_connection_by_sessionnum(bn_int_get(packet->u.client_sessionaddr2.sessionnum)))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] address not set (no connection with session number {})", usock, bn_int_get(packet->u.client_sessionaddr2.sessionnum)); return -1; } if (conn_get_sessionkey(c) != bn_int_get(packet->u.client_sessionaddr2.sessionkey)) { eventlog(eventlog_level_error, __FUNCTION__, "[{}][{}] address not set (expected session key 0x{:08x}, got 0x{:08x})", conn_get_socket(c), usock, conn_get_sessionkey(c), bn_int_get(packet->u.client_sessionaddr2.sessionkey)); return -1; } if (conn_get_game_addr(c) != src_addr || conn_get_game_port(c) != src_port) eventlog(eventlog_level_info, __FUNCTION__, "[{}][{}] SESSIONADDR2 set new UDP address to {}", conn_get_socket(c), usock, addr_num_to_addr_str(src_addr, src_port)); conn_set_game_socket(c, usock); conn_set_game_addr(c, src_addr); conn_set_game_port(c, src_port); udptest_send(c); } return 0; case CLIENT_SEARCH_LAN_GAMES: //added by Spider { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got SEARCH_LAN_GAMES packet from {}", usock, addr_num_to_addr_str(src_addr, src_port)); return 0; } default: eventlog(eventlog_level_debug, __FUNCTION__, "[{}] got unknown udp packet type 0x{:04x}, len {} from {}", usock, packet_get_type(packet), packet_get_size(packet), addr_num_to_addr_str(src_addr, src_port)); } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_udp.h000066400000000000000000000022211462640461400203340ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_UDP_PROTOS #define INCLUDED_HANDLE_UDP_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_udp_packet(int usock, unsigned int src_addr, unsigned short src_port, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_wol.cpp000066400000000000000000001657721462640461400207250ustar00rootroot00000000000000/* * Copyright (C) 2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2005 Bryan Biedenkapp (gatekeep@gmail.com) * Copyright (C) 2006,2007,2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_wol.h" #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/irc_protocol.h" #include "common/eventlog.h" #include "common/bnethash.h" #include "common/tag.h" #include "common/util.h" #include "common/list.h" #include "common/addr.h" #include "common/trans.h" #include "common/xstring.h" #include "common/packet.h" #include "prefs.h" #include "command.h" #include "irc.h" #include "account.h" #include "account_wrap.h" #include "command_groups.h" #include "channel.h" #include "message.h" #include "tick.h" #include "topic.h" #include "server.h" #include "friends.h" #include "clan.h" #include "game.h" #include "anongame_wol.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef int(*t_wol_command)(t_connection * conn, int numparams, char ** params, char * text); typedef struct { const char * wol_command_string; t_wol_command wol_command_handler; } t_wol_command_table_row; static int _handle_user_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_pass_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_privmsg_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_quit_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_list_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_names_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_part_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_cvers_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_verchk_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_apgar_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_setopt_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_serial_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_squadinfo_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_clanbyname_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_setcodepage_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_getcodepage_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_setlocale_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_getlocale_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_getinsider_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_joingame_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_gameopt_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_finduser_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_finduserex_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_page_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_startg_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_advertr_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_advertc_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_chanchk_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_getbuddy_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_addbuddy_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_delbuddy_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_host_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_invmsg_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_invdel_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_userip_command(t_connection * conn, int numparams, char ** params, char * text); /* Ladder server commands (we will probalby move this commands to any another handle file */ static int _handle_listsearch_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_rungsearch_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_highscore_command(t_connection * conn, int numparams, char ** params, char * text); /* state "connected" handlers */ static const t_wol_command_table_row wol_con_command_table[] = { { "NICK", _handle_nick_command }, { "USER", _handle_user_command }, { "PING", _handle_ping_command }, { "PONG", _handle_pong_command }, { "PASS", _handle_pass_command }, { "PRIVMSG", _handle_privmsg_command }, { "QUIT", _handle_quit_command }, { "CVERS", _handle_cvers_command }, { "VERCHK", _handle_verchk_command }, { "APGAR", _handle_apgar_command }, { "SETOPT", _handle_setopt_command }, { "SERIAL", _handle_serial_command }, /* Ladder server commands */ { "LISTSEARCH", _handle_listsearch_command }, { "RUNGSEARCH", _handle_rungsearch_command }, { "HIGHSCORE", _handle_highscore_command }, { NULL, NULL } }; /* state "logged in" handlers */ static const t_wol_command_table_row wol_log_command_table[] = { { "LIST", _handle_list_command }, { "TOPIC", _handle_topic_command }, { "JOIN", _handle_join_command }, { "NAMES", _handle_names_command }, { "PART", _handle_part_command }, { "SQUADINFO", _handle_squadinfo_command }, { "CLANBYNAME", _handle_clanbyname_command }, { "SETCODEPAGE", _handle_setcodepage_command }, { "SETLOCALE", _handle_setlocale_command }, { "GETCODEPAGE", _handle_getcodepage_command }, { "GETLOCALE", _handle_getlocale_command }, { "GETINSIDER", _handle_getinsider_command }, { "JOINGAME", _handle_joingame_command }, { "GAMEOPT", _handle_gameopt_command }, { "FINDUSER", _handle_finduser_command }, { "FINDUSEREX", _handle_finduserex_command }, { "PAGE", _handle_page_command }, { "STARTG", _handle_startg_command }, { "ADVERTR", _handle_advertr_command }, { "ADVERTC", _handle_advertc_command }, { "CHANCHK", _handle_chanchk_command }, { "GETBUDDY", _handle_getbuddy_command }, { "ADDBUDDY", _handle_addbuddy_command }, { "DELBUDDY", _handle_delbuddy_command }, { "TIME", _handle_time_command }, { "KICK", _handle_kick_command }, { "MODE", _handle_mode_command }, { "HOST", _handle_host_command }, { "INVMSG", _handle_invmsg_command }, { "INVDEL", _handle_invdel_command }, { "USERIP", _handle_userip_command }, { NULL, NULL } }; extern int handle_wol_con_command(t_connection * conn, char const * command, int numparams, char ** params, char * text) { t_wol_command_table_row const *p; for (p = wol_con_command_table; p->wol_command_string != NULL; p++) { if (strcasecmp(command, p->wol_command_string) == 0) { if (p->wol_command_handler != NULL) return ((p->wol_command_handler)(conn, numparams, params, text)); } } return -1; } extern int handle_wol_log_command(t_connection * conn, char const * command, int numparams, char ** params, char * text) { t_wol_command_table_row const *p; for (p = wol_log_command_table; p->wol_command_string != NULL; p++) { if (strcasecmp(command, p->wol_command_string) == 0) { if (p->wol_command_handler != NULL) return ((p->wol_command_handler)(conn, numparams, params, text)); } } return -1; } static int handle_wol_authenticate(t_connection * conn, char const * passhash) { t_account * a; char const * tempapgar; char const * temphash; char const * username; if (!conn) { ERROR0("got NULL connection"); return 0; } if (!passhash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL passhash"); return 0; } username = conn_get_loggeduser(conn); if (!username) { /* redundant sanity check */ eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn->protocol.loggeduser"); return 0; } a = accountlist_find_account(username); if (!a) { /* FIXME: Send real error code */ message_send_text(conn, message_type_notice, NULL, "Authentication failed."); return 0; } tempapgar = conn_wol_get_apgar(conn); temphash = account_get_wol_apgar(a); if (connlist_find_connection_by_account(a) && prefs_get_kick_old_login() == 0) { irc_send(conn, ERR_NICKNAMEINUSE, std::string(std::string(conn_get_loggeduser(conn)) + " :Account is already in use!").c_str()); } else if (account_get_auth_lock(a) == 1) { /* FIXME: Send real error code */ message_send_text(conn, message_type_notice, NULL, "Authentication rejected (account is locked) "); } else { if (!temphash) { /* Account auto creating */ account_set_wol_apgar(a, tempapgar); temphash = account_get_wol_apgar(a); } if ((tempapgar) && (temphash) && (std::strcmp(temphash, tempapgar) == 0)) { /* LOGIN is OK. We sends motd */ conn_login(conn, a, username); conn_set_state(conn, conn_state_loggedin); irc_send_motd(conn); } else { irc_send(conn, RPL_BAD_LOGIN, ":You have specified an invalid password for that nickname."); /* bad APGAR */ conn_increment_passfail_count(conn); //std::sprintf(temp,":Closing Link %s[Some.host]:(Password needed for that nickname.)",conn_get_loggeduser(conn)); //message_send_text(conn,message_type_error,conn,temp); } } return 0; } extern int handle_wol_welcome(t_connection * conn) { /* This function need rewrite */ conn_set_state(conn, conn_state_bot_password); if (conn_wol_get_apgar(conn)) { handle_wol_authenticate(conn, conn_wol_get_apgar(conn)); } else { message_send_text(conn, message_type_notice, NULL, "No APGAR command received!"); } return 0; } static int handle_wol_send_claninfo(t_connection * conn, t_clan * clan) { unsigned int clanid; const char * clantag; const char * clanname; if (!conn) { ERROR0("got NULL connection"); return -1; } if (clan) { clanid = clan_get_clanid(clan); clantag = clantag_to_str(clan_get_clantag(clan)); clanname = clan_get_name(clan); irc_send(conn, RPL_BATTLECLAN, std::string(std::to_string(clanid) + "`" + std::string(clanname) + "`" + std::string(clantag) + "`0`0`1`0`0`0`0`0`0`0`x`x`x").c_str()); } else { irc_send(conn, ERR_IDNOEXIST, ":ID does not exist"); } return 0; } /* Commands: */ static int _handle_user_command(t_connection * conn, int numparams, char ** params, char * text) { /** * In WOL isnt used USER command (only for backward compatibility) * RFC 2812 says: * USER : * * There is WOL imput expected: * USER UserName HostName irc.westwood.com :RealName */ char * user = NULL; t_account * a; user = (char *)conn_get_loggeduser(conn); if (conn_get_user(conn)) { /* FIXME: Send real ERROR code/message */ irc_send(conn, ERR_ALREADYREGISTRED, ":You are already registred"); } else { eventlog(eventlog_level_debug, __FUNCTION__, "[{}][** WOL **] got USER: user=\"{}\"", conn_get_socket(conn), user); a = accountlist_find_account(user); if (!a) { /* Auto-create account */ t_account * tempacct; t_hash pass_hash; char * pass = xstrdup(conn_wol_get_apgar(conn)); /* FIXME: Do not use bnet passhash when we have wol passhash */ strtolower(pass); bnet_hash(&pass_hash, std::strlen(pass), pass); xfree((void *)pass); tempacct = accountlist_create_account(user, hash_get_str(pass_hash)); if (!tempacct) { /* FIXME: Send real ERROR code/message */ irc_send(conn, RPL_BAD_LOGIN, ":Account creating failed"); return 0; } conn_set_user(conn, user); conn_set_owner(conn, user); if (conn_get_loggeduser(conn)) handle_wol_welcome(conn); /* only send the welcome if we have USER and NICK */ } else { conn_set_user(conn, user); conn_set_owner(conn, user); if (conn_get_loggeduser(conn)) handle_wol_welcome(conn); /* only send the welcome if we have USER and NICK */ } } return 0; } static int _handle_pass_command(t_connection * conn, int numparams, char ** params, char * text) { /** * PASS is not used in WOL * only for backward compatibility sent client PASS supersecret * real password sent client by apgar command */ return 0; } static int _handle_privmsg_command(t_connection * conn, int numparams, char ** params, char * text) { if ((numparams >= 1) && (text)) { int i; char ** e; e = irc_get_listelems(params[0]); /* FIXME: support wildcards! */ /* start amadeo: code was sent by some unkown fellow of pvpgn (maybe u wanna give us your name for any credits), it adds nick-registration, i changed some things here and there... */ for (i = 0; ((e) && (e[i])); i++) { if (strcasecmp(e[i], "matchbot") == 0) { /* Anongames WOL support */ anongame_wol_privmsg(conn, numparams, params, text); } else if (conn_get_state(conn) == conn_state_loggedin) { if (e[i][0] == '#') { /* channel message */ t_channel * channel; //PELISH: We does not support talk for not inside-channel clients now but in WOL is that feature not needed if (channel = conn_get_channel(conn)) { if ((std::strlen(text) >= 9) && (std::strncmp(text, "\001ACTION ", 8) == 0) && (text[std::strlen(text) - 1] == '\001')) { /* at least "\001ACTION \001" */ /* it's a CTCP ACTION message */ text = text + 8; text[std::strlen(text) - 1] = '\0'; channel_message_send(channel, message_type_emote, conn, text); } else { if (text[0] == '/') { /* "/" commands (like "/help..." */ handle_command(conn, text); } else { channel_message_log(channel, conn, 1, text); channel_message_send(channel, message_type_talk, conn, text); } } } else { irc_send(conn, ERR_NOSUCHCHANNEL, std::string(std::string(e[0]) + " :No such channel").c_str()); } } else { /* whisper */ t_connection * user; if ((user = connlist_find_connection_by_accountname(e[i]))) { message_send_text(user, message_type_whisper, conn, text); } else { irc_send(conn, ERR_NOSUCHNICK, ":No such user"); } } } } if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "PRIVMSG :Not enough parameters"); return 0; } struct gamelist_data { unsigned tcount, counter; t_connection *conn; }; static int append_game_info(t_game* game, void* vdata) { char temp[MAX_IRC_MESSAGE_LEN]; char temp_a[MAX_IRC_MESSAGE_LEN]; gamelist_data* data = static_cast(vdata); t_channel * gamechannel; const char * gamename; std::string topicstr; std::memset(temp, 0, sizeof(temp)); std::memset(temp_a, 0, sizeof(temp_a)); data->tcount++; if (game_get_status(game) != game_status_open) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is not open", conn_get_socket(data->conn)); return 0; } if (game_get_clienttag(game) != conn_get_clienttag(data->conn)) { eventlog(eventlog_level_debug, __FUNCTION__, "[{}] not listing because game is for a different client", conn_get_socket(data->conn)); return 0; } if (!(gamechannel = game_get_channel(game))) { ERROR0("game have no channel"); return 0; } if (!(gamename = irc_convert_channel(gamechannel, data->conn))) { ERROR0("game have no name"); return 0; } class_topic Topic; topicstr = Topic.get(channel_get_name(gamechannel)); if (topicstr.empty() == false) { if (std::strlen(gamename) + 1 + 20 + 1 + 1 + std::strlen(topicstr.c_str()) > MAX_IRC_MESSAGE_LEN) { WARN0("LISTREPLY length exceeded"); return 0; } } else { if (std::strlen(gamename) + 1 + 20 + 1 + 1 > MAX_IRC_MESSAGE_LEN) { WARN0("LISTREPLY length exceeded"); return 0; } } /*** * WOLv1: * : 326 u #nick's_game 1 0 2 0 0 1122334455 128:: * WOLv2: * : 326 u #nick's_game 1 0 21 0 16777216 1122334455 128::g040 * : 326 u #nick's_game 1 0 41 1 2048 1122334455 128::g12P25,2097731398,0,0,0,WATERF~3.YRM // anon_game */ /** * The layout of the game list entry is something like this: * #game_channel_name currentusers maxplayers gameType gameIsTournment gameExtension longIP LOCK::topic */ std::strcat(temp, gamename); std::strcat(temp, " "); std::snprintf(temp_a, sizeof(temp_a), "%u ", game_get_ref(game)); /* curent players */ std::strcat(temp, temp_a); std::snprintf(temp_a, sizeof(temp_a), "%u ", game_get_maxplayers(game)); /* max players */ std::strcat(temp, temp_a); std::snprintf(temp_a, sizeof(temp_a), "%u ", channel_wol_get_game_type(gamechannel)); /* game type */ std::strcat(temp, temp_a); std::snprintf(temp_a, sizeof(temp_a), "%u ", (game_get_type(game) == game_type_ladder) ? 1 : 0); /* tournament */ std::strcat(temp, temp_a); std::snprintf(temp_a, sizeof(temp_a), "%s ", channel_wol_get_game_extension(gamechannel)); /* game extension */ std::strcat(temp, temp_a); std::snprintf(temp_a, sizeof(temp_a), "%u ", conn_get_addr(game_get_owner(game))); /* owner IP - FIXME: address translation here!! */ std::strcat(temp, temp_a); if (std::strcmp(game_get_pass(game), "") == 0) std::strcat(temp, "128"); /* game is unloocked 128 == no_pass */ else std::strcat(temp, "384"); /* game is loocked 384 == pass */ std::strcat(temp, "::"); if (topicstr.c_str()) { std::snprintf(temp_a, sizeof(temp_a), "%s", topicstr.c_str()); /* topic */ std::strcat(temp, temp_a); } data->counter++; irc_send(data->conn, RPL_GAME_CHANNEL, temp); return 0; } static int _handle_list_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; t_elem const * curr; irc_send(conn, RPL_LISTSTART, "Channel :Users Names"); /* backward compatibility */ if ((numparams == 0) || ((numparams == 2) && (params[0]) && (params[1]) && (std::strcmp(params[0], params[1]) != 0))) { /** * LIST all chat channels * Emperor sends as params[0] == -1 if want QuickMatch channels too, 0 if not. * This sends also NOX but we dunno why. * DUNE 2000 use params[0] to determine channels by channeltype */ LIST_TRAVERSE_CONST(channellist(), curr) { t_channel const * channel = (const t_channel*)elem_get_data(curr); char const * tempname; tempname = irc_convert_channel(channel, conn); if ((tag_check_wolv1(conn_get_clienttag(conn))) && (std::strlen(tempname) > MAX_WOLV1_CHANNELNAME_LEN)) continue; /* FIXME: Delete this if games are not in channels */ if ((channel_wol_get_game_type(channel) != 0)) continue; sprintf(temp, "%s %u ", tempname, channel_get_length(channel)); if (channel_get_flags(channel) & channel_flags_permanent) std::strcat(temp, "1"); /* Official channel */ else std::strcat(temp, "0"); /* User channel */ if (tag_check_wolv1(conn_get_clienttag(conn))) std::strcat(temp, ":"); /* WOLv1 ends by ":" FIXME: Should be an TOPIC after ":"*/ else std::strcat(temp, " 388"); /* WOLv2 ends by "388" */ if (std::strlen(temp) > MAX_IRC_MESSAGE_LEN) WARN0("LISTREPLY length exceeded"); irc_send(conn, RPL_CHANNEL, temp); } } /** * Known channel game types: * 0 = Westwood Chat channels, 1 = Command & Conquer Win95 channels, 2 = Red Alert Win95 channels, * 3 = Red Alert Counterstrike channels, 4 = Red Alert Aftermath channels, 5 = CnC Sole Survivor channels, * 12 = C&C Renegade channels, 14 = Dune 2000 channels, 16 = Nox channels, 18 = Tiberian Sun channels, * 21 = Red Alert 1 v 3.03 channels, 31 = Emperor: Battle for Dune, 33 = Red Alert 2, * 37 = Nox Quest channels, 38,39,40 = Quickgame channels, 41 = Yuri's Revenge */ if ((numparams == 0) || ((numparams == 2) && (params[0]) && (params[1]) && (std::strcmp(params[0], params[1]) == 0))) { eventlog(eventlog_level_debug, __FUNCTION__, "[** WOL **] LIST [Game]"); /* list games */ struct gamelist_data data; data.tcount = 0; data.counter = 0; data.conn = conn; gamelist_traverse(&append_game_info, &data, gamelist_source_none); DEBUG3("[{}] LIST sent {} of {} games", conn_get_socket(conn), data.counter, data.tcount); } irc_send(conn, RPL_LISTEND, ":End of LIST command"); return 0; } static int _handle_quit_command(t_connection * conn, int numparams, char ** params, char * text) { if (conn_get_channel(conn)) conn_quit_channel(conn, text); irc_send(conn, RPL_QUIT, ":goodbye"); conn_set_state(conn, conn_state_destroy); return 0; } static int _handle_names_command(t_connection * conn, int numparams, char ** params, char * text) { t_channel * channel; if (numparams >= 1) { char ** e; char const * verytemp; int i; e = irc_get_listelems(params[0]); for (i = 0; ((e) && (e[i])); i++) { verytemp = irc_convert_ircname(e[i]); if (!verytemp) continue; /* something is wrong with the name ... */ channel = channellist_find_channel_by_name(verytemp, NULL, NULL); if ((!channel) && (!(channel = conn_get_channel(conn)))) continue; /* channel doesn't exist */ irc_send_rpl_namreply(conn, channel); } if (e) irc_unget_listelems(e); } else if (numparams == 0) { irc_send_rpl_namreply(conn, NULL); } return 0; } static int _handle_part_command(t_connection * conn, int numparams, char ** params, char * text) { t_game * game; conn_part_channel(conn); if ((game = conn_get_game(conn)) && (game_get_status(game) == game_status_open)) conn_set_game(conn, NULL, NULL, NULL, game_type_none, 0); return 0; } /** * Fallowing commands are only in Westwood Online protocol */ static int _handle_cvers_command(t_connection * conn, int numparams, char ** params, char * text) { t_clienttag clienttag; /* Ignore command but set clienttag */ /** * Heres the imput expected: * CVERS [oldvernum] [SKU] * * SKU is specific number for any WOL client (Tiberian sun, RedAlert 2 etc.) * This is the best way to set clienttag, because CVERS is the first command which * client send to server. */ if (numparams == 2) { clienttag = tag_sku_to_uint(std::atoi(params[1])); if (clienttag != CLIENTTAG_WWOL_UINT) conn_set_clienttag(conn, clienttag); } else irc_send(conn, ERR_NEEDMOREPARAMS, "CVERS :Not enough parameters"); return 0; } static int _handle_verchk_command(t_connection * conn, int numparams, char ** params, char * text) { t_clienttag clienttag; /** * Heres the imput expected: * vercheck [SKU] [version] * * Heres the output expected: * * 1) Update non-existant: * :[servername] 379 [username] :none none none 1 [SKU] NONREQ * 2) Update existant: * :[servername] 379 [username] :none none none [oldversnum] [SKU] REQ */ if (numparams == 2) { clienttag = tag_sku_to_uint(std::atoi(params[0])); if (clienttag != CLIENTTAG_WWOL_UINT) conn_set_clienttag(conn, clienttag); std::string tmp(":none none none 1 " + std::string(params[0]) + " NONREQ"); eventlog(eventlog_level_debug, __FUNCTION__, "[** WOL **] VERCHK {}", tmp.c_str()); irc_send(conn, RPL_VERCHK_NONREQ, tmp.c_str()); } else irc_send(conn, ERR_NEEDMOREPARAMS, "VERCHK :Not enough parameters"); return 0; } static int _handle_apgar_command(t_connection * conn, int numparams, char ** params, char * text) { char * apgar = NULL; if ((numparams >= 1) && (params[0])) { apgar = params[0]; conn_wol_set_apgar(conn, apgar); } else irc_send(conn, ERR_NEEDMOREPARAMS, "APGAR :Not enough parameters"); return 0; } static int _handle_serial_command(t_connection * conn, int numparams, char ** params, char * text) { // Ignore command return 0; } static int _handle_squadinfo_command(t_connection * conn, int numparams, char ** params, char * text) { t_clan * clan; if ((numparams >= 1) && (params[0])) { if (std::strcmp(params[0], "0") == 0) { /* 0 == claninfo for itself */ clan = account_get_clan(conn_get_account(conn)); handle_wol_send_claninfo(conn, clan); } else { /* claninfo for clanid (params[0]) */ clan = clanlist_find_clan_by_clanid(std::atoi(params[0])); handle_wol_send_claninfo(conn, clan); } } else irc_send(conn, ERR_NEEDMOREPARAMS, "SQUADINFO :Not enough parameters"); return 0; } static int _handle_clanbyname_command(t_connection * conn, int numparams, char ** params, char * text) { t_clan * clan; if ((numparams >= 1) && (params[0])) { clan = account_get_clan(accountlist_find_account(params[0])); handle_wol_send_claninfo(conn, clan); } else irc_send(conn, ERR_NEEDMOREPARAMS, "CLANBYNAME :Not enough parameters"); return 0; } static int _handle_setopt_command(t_connection * conn, int numparams, char ** params, char * text) { char ** elems; /** * This is option for enabling/disabling Page and Find user. * * Heres the input expected: * SETOPT 17,32 * * First parameter: 16 == FindDisabled 17 == FindEnabled * Second parameter: 32 == PageDisabled 33 == PageEnabled */ if ((numparams >= 1) && (params[0])) { elems = irc_get_listelems(params[0]); if ((elems) && (elems[0]) && (elems[1])) { conn_wol_set_findme(conn, ((std::strcmp(elems[0], "17") == 0) ? true : false)); conn_wol_set_pageme(conn, ((std::strcmp(elems[1], "33") == 0) ? true : false)); } if (elems) irc_unget_listelems(elems); } else irc_send(conn, ERR_NEEDMOREPARAMS, "SETOPT :Not enough parameters"); return 0; } static int _handle_setcodepage_command(t_connection * conn, int numparams, char ** params, char * text) { char * codepage = NULL; if ((numparams >= 1) && (params[0])) { codepage = params[0]; conn_wol_set_codepage(conn, std::atoi(codepage)); irc_send(conn, RPL_SET_CODEPAGE, codepage); } else irc_send(conn, ERR_NEEDMOREPARAMS, "SETCODEPAGE :Not enough parameters"); return 0; } static int _handle_getcodepage_command(t_connection * conn, int numparams, char ** params, char * text) { std::string temp; if ((numparams >= 1) && (params[0])) { for (auto i = 0; i < numparams; i++) { int codepage = 0; t_connection * user = connlist_find_connection_by_accountname(params[i]); if (user) codepage = conn_wol_get_codepage(user); temp.append(std::string(params[i]) + "`" + std::to_string(codepage)); if (i < numparams - 1) temp.append("`"); } irc_send(conn, RPL_GET_CODEPAGE, temp.c_str()); } else irc_send(conn, ERR_NEEDMOREPARAMS, "GETCODEPAGE :Not enough parameters"); return 0; } static int _handle_setlocale_command(t_connection * conn, int numparams, char ** params, char * text) { t_account * account = conn_get_account(conn); int locale; if ((numparams >= 1) && (params[0])) { locale = std::atoi(params[0]); account_set_locale(account, locale); irc_send(conn, RPL_SET_LOCALE, params[0]); } else irc_send(conn, ERR_NEEDMOREPARAMS, "SETLOCALE :Not enough parameters"); return 0; } static int _handle_getlocale_command(t_connection * conn, int numparams, char ** params, char * text) { std::string temp; if ((numparams >= 1) && (params[0])) { for (auto i = 0; i < numparams; i++) { int locale = 0; t_account * account = accountlist_find_account(params[i]); if (account) locale = account_get_locale(account); temp.append(std::string(params[i]) + "`" + std::to_string(locale)); if (i < numparams - 1) temp.append("`"); } irc_send(conn, RPL_GET_LOCALE, temp.c_str()); } else irc_send(conn, ERR_NEEDMOREPARAMS, "GETLOCALE :Not enough parameters"); return 0; } static int _handle_getinsider_command(t_connection * conn, int numparams, char ** params, char * text) { /** * Here is imput expected: * GETINSIDER [nickname] * Here is output expected: * :[servername] 399 [nick] [nickname]`0 */ if ((numparams >= 1) && (params[0])) irc_send(conn, RPL_GET_INSIDER, std::string(std::string(params[0]) + "`0").c_str()); else irc_send(conn, ERR_NEEDMOREPARAMS, "GETINSIDER :Not enough parameters"); return 0; } static int _handle_joingame_command(t_connection * conn, int numparams, char ** params, char * text) { char _temp[MAX_IRC_MESSAGE_LEN]; std::memset(_temp, 0, sizeof(_temp)); /** * Basically this has 2 modes, Join Game and Create Game output is pretty much * the same...input and output of JOINGAME is listed below. By the way, there is a * hack in here, for Red Alert 1, it use's JOINGAME for some reason to join a lobby channel. * * Here is WOLv1 input expected: * JOINGAME [#Game_channel_name] [MinPlayers] [MaxPlayers] [channelType] 1 1 [gameIsTournament] * Knowed channelTypes (0-chat, 1-cnc, 2-ra1, 3-racs, 4-raam, 5-solsurv... listed in tag.cpp) * * Here is WOLv2 input expected: * JOINGAME [#Game_channel_name] [MinPlayers] [MaxPlayers] [channelType] unknown unknown [gameIsTournament] [gameExtension] [password_optional] * * Heres the output expected: * user!WWOL@hostname JOINGAME [MinPlayers] [MaxPlayers] [channelType] unknown clanID [longIP] [gameIsTournament] :[#Game_channel_name] */ if ((numparams == 2) || (numparams == 3)) { char ** e; e = irc_get_listelems(params[0]); if ((e) && (e[0])) { char const * gamename = irc_convert_ircname(e[0]); // char * old_channel_name = NULL; t_game * game; t_game_type gametype; t_channel * channel; t_channel * old_channel = conn_get_channel(conn); char gamepass[MAX_GAMEPASS_LEN]; std::memset(gamepass, 0, sizeof(gamepass)); if ((conn_get_clienttag(conn) == CLIENTTAG_REDALERT_UINT) && (channellist_find_channel_by_name(gamename, NULL, NULL) != NULL) && (gamelist_find_game_available(gamename, conn_get_clienttag(conn), game_type_all) == NULL)) { /* BUG in Red Alert 1 v3.03e - forwarding to _handle_join_command */ DEBUG0("BUG in RA1 v3.03e - forwarding to _handle_join_command"); _handle_join_command(conn, numparams, params, text); if (e) irc_unget_listelems(e); return 0; } if (!(gamename) || !(game = gamelist_find_game_available(gamename, conn_get_clienttag(conn), game_type_all))) { irc_send(conn, ERR_GAMEHASCLOSED, std::string(std::string(e[0]) + " :Game channel has closed").c_str()); if (e) irc_unget_listelems(e); return 0; } channel = game_get_channel(game); if (game_get_ref(game) == game_get_maxplayers(game)) { irc_send(conn, ERR_CHANNELISFULL, std::string(std::string(e[0]) + " :Channel is full.").c_str()); if (e) irc_unget_listelems(e); return 0; } if (channel_check_banning(channel, conn)) { irc_send(conn, ERR_BANNEDFROMCHAN, std::string(std::string(e[0]) + " :You are banned from that channel.").c_str()); if (e) irc_unget_listelems(e); return 0; } if (std::strcmp(game_get_pass(game), "") != 0) { if ((numparams == 3) && (params[2]) && (std::strcmp(params[2], game_get_pass(game)) == 0)) { std::strcpy(gamepass, params[2]); } else { irc_send(conn, ERR_BADCHANNELKEY, std::string(std::string(e[0]) + ":Bad password").c_str()); if (e) irc_unget_listelems(e); return 0; } } gametype = game_get_type(game); if ((conn_set_game(conn, gamename, gamepass, "", gametype, 0)) < 0) { irc_send(conn, ERR_GAMEHASCLOSED, std::string(std::string(e[0]) + " :JOINGAME failed").c_str()); } else { /*conn_set_channel()*/ channel = game_get_channel(game); conn_set_channel_var(conn, channel); channel_add_connection(channel, conn); channel = conn_get_channel(conn); if (channel != old_channel) { if (tag_check_wolv1(conn_get_clienttag(conn))) { /* WOLv1 JOINGAME message */ std::sprintf(_temp, "%u %u %u 1 1 %u :%s", channel_get_min(channel), game_get_maxplayers(game), channel_wol_get_game_type(channel), ((game_get_type(game) == game_type_ladder) ? 1 : 0), irc_convert_channel(channel, conn)); } else { /* WOLv2 JOINGAME message with BATTLECLAN support */ t_clan * clan = account_get_clan(conn_get_account(conn)); unsigned int clanid = 0; if (clan) clanid = clan_get_clanid(clan); std::sprintf(_temp, "%u %u %u 1 %u %u %u :%s", channel_get_min(channel), game_get_maxplayers(game), channel_wol_get_game_type(channel), clanid, conn_get_addr(conn), ((game_get_type(game) == game_type_ladder) ? 1 : 0), irc_convert_channel(channel, conn)); } channel_set_userflags(conn); /* we have to send the JOINGAME acknowledgement */ channel_message_send(channel, message_wol_joingame, conn, _temp); irc_send_topic(conn, channel); irc_send_rpl_namreply(conn, channel); } else { irc_send(conn, ERR_GAMEHASCLOSED, std::string(std::string(e[0]) + " :JOINGAME failed").c_str()); } } } if (e) irc_unget_listelems(e); return 0; } else if ((numparams >= 7)) { char ** e; eventlog(eventlog_level_debug, __FUNCTION__, "[** WOL **] JOINGAME: * Create * ({}, {})", params[0], params[1]); if ((numparams == 7)) { /* WOLv1 JOINGAME Create */ std::snprintf(_temp, sizeof(_temp), "%s %s %s %s 0 %s :%s", params[1], params[2], params[3], params[4], params[6], params[0]); } /* WOLv2 JOINGAME Create */ else if ((numparams >= 8)) { t_clan * clan = account_get_clan(conn_get_account(conn)); unsigned int clanid = 0; if (clan) clanid = clan_get_clanid(clan); std::snprintf(_temp, sizeof(_temp), "%s %s %s %s %u %u %s :%s", params[1], params[2], params[3], params[4], clanid, conn_get_addr(conn), params[6], params[0]); } eventlog(eventlog_level_debug, __FUNCTION__, "[** WOL **] JOINGAME [Game Options] ({})", _temp); e = irc_get_listelems(params[0]); if ((e) && (e[0])) { char const * gamename = irc_convert_ircname(e[0]); t_game_type gametype; char gamepass[MAX_GAMEPASS_LEN]; std::memset(gamepass, 0, sizeof(gamepass)); if (std::strcmp(params[6], "1") == 0) gametype = game_type_ladder; else gametype = game_type_ffa; // gametype = game_type_none; if ((numparams >= 8) && (params[8])) { std::snprintf(gamepass, sizeof gamepass, "%s", params[8]); } if ((!(gamename)) || ((conn_set_game(conn, gamename, gamepass, "", gametype, 0))<0)) { irc_send(conn, ERR_NOSUCHCHANNEL, ":JOINGAME failed"); /* FIXME: be more precise; what is the real error code for that? */ } else { t_game * game = conn_get_game(conn); t_channel * channel = channel_create(gamename, gamename, 0, 0, 1, 1, prefs_get_chanlog(), NULL, NULL, (prefs_get_maxusers_per_channel() > 0) ? prefs_get_maxusers_per_channel() : -1, 0, 0, 0, NULL); game_set_channel(game, channel); conn_set_channel_var(conn, channel); channel_add_connection(channel, conn); channel_set_userflags(conn); game_set_maxplayers(game, std::atoi(params[2])); channel_set_min(channel, std::atoi(params[1])); // HACK: Currently, this is the best way to set the channel game type... channel_wol_set_game_type(channel, std::atoi(params[3])); if (params[7]) channel_wol_set_game_extension(channel, params[7]); else channel_wol_set_game_extension(channel, "0"); // we have to send the JOINGAME acknowledgement message_send_text(conn, message_wol_joingame, conn, _temp); irc_send_topic(conn, channel); irc_send_rpl_namreply(conn, channel); } } if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "JOINGAME :Not enough parameters"); return 0; } static int _handle_gameopt_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; /** * Basically this has 2 modes as like in PRIVMSG - whisper and talk. What is in * text is pretty much unknown, we just dump this to the client to deal with... * * Heres the output expected (when gameopt is channel talk): * user!WWOL@hostname GAMEOPT #game_channel_name :gameOptions * * Heres the output expected (when gameopt is whispered): * user!WWOL@hostname GAMEOPT sender_nick_name :gameOptions */ if ((numparams >= 1) && (params[0]) && (text)) { int i; char ** e; t_connection * user; t_channel * channel; e = irc_get_listelems(params[0]); /* FIXME: support wildcards! */ for (i = 0; ((e) && (e[i])); i++) { if (e[i][0] == '#') { /* channel gameopt */ if (channel = conn_get_channel(conn)) { channel_message_send(channel, message_type_gameopt_talk, conn, text); } else { std::snprintf(temp, sizeof(temp), "%s :No such channel", params[0]); irc_send(conn, ERR_NOSUCHCHANNEL, temp); } } else { /* user gameopt */ if ((user = connlist_find_connection_by_accountname(e[i]))) { message_send_text(user, message_type_gameopt_whisper, conn, text); } else { std::snprintf(temp, sizeof(temp), "%s :No such nick", e[i]); irc_send(conn, ERR_NOSUCHNICK, temp); } } } if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "GAMEOPT :Not enough parameters"); return 0; } static int _handle_finduser_command(t_connection * conn, int numparams, char ** params, char * text) { char _temp[MAX_IRC_MESSAGE_LEN]; char const * wolname = NULL; std::memset(_temp, 0, sizeof(_temp)); if ((numparams >= 1) && (params[0])) { t_connection * user; if ((user = connlist_find_connection_by_accountname(params[0])) && (conn_wol_get_findme(user))) { wolname = irc_convert_channel(conn_get_channel(user), conn); std::snprintf(_temp, sizeof(_temp), "0 :%s", wolname); /* User found in channel wolname */ } else std::snprintf(_temp, sizeof(_temp), "1 :"); /* user not loged or have not allowed find */ irc_send(conn, RPL_FIND_USER, _temp); } else irc_send(conn, ERR_NEEDMOREPARAMS, "FINDUSER :Not enough parameters"); return 0; } static int _handle_finduserex_command(t_connection * conn, int numparams, char ** params, char * text) { char _temp[MAX_IRC_MESSAGE_LEN]; char const * wolname = NULL; std::memset(_temp, 0, sizeof(_temp)); if ((numparams >= 1) && (params[0])) { t_connection * user; if ((user = connlist_find_connection_by_accountname(params[0])) && (conn_wol_get_findme(user))) { wolname = irc_convert_channel(conn_get_channel(user), conn); std::snprintf(_temp, sizeof(_temp), "0 :%s,0", wolname); /* User found in channel wolname */ } else std::snprintf(_temp, sizeof(_temp), "1 :"); /* user not loged or have not allowed find */ irc_send(conn, RPL_FIND_USER_EX, _temp); } else irc_send(conn, ERR_NEEDMOREPARAMS, "FINDUSEREX :Not enough parameters"); return 0;; } static int _handle_page_command(t_connection * conn, int numparams, char ** params, char * text) { char _temp[MAX_IRC_MESSAGE_LEN]; bool paged = false; if ((numparams >= 1) && (params[0]) && (text)) { t_connection * user; if (std::strcmp(params[0], "0") == 0) { /* PAGE for MY BATTLECLAN */ t_clan * clan = account_get_clan(conn_get_account(conn)); if ((clan) && (clan_send_message_to_online_members(clan, message_type_page, conn, text) >= 1)) paged = true; } else if ((user = connlist_find_connection_by_accountname(params[0])) && (conn_wol_get_pageme(user))) { message_send_text(user, message_type_page, conn, text); paged = true; } if (paged) std::snprintf(_temp, sizeof(_temp), "0 :"); /* Page was succesfull */ else std::snprintf(_temp, sizeof(_temp), "1 :"); /* User not loged in or have not allowed page */ irc_send(conn, RPL_PAGE, _temp); } else irc_send(conn, ERR_NEEDMOREPARAMS, "PAGE :Not enough parameters"); return 0; } static int _handle_startg_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; char _temp_a[MAX_IRC_MESSAGE_LEN]; t_channel * channel; t_game * game; /** * Imput expected: * STARTG [channel_name] [nick1](,nick2_optional) * * Heres the output expected (this can have up-to 8 entries (ie 8 players): * (we are assuming for this example that user1 is the game owner) * * WOLv1: * :user1!WWOL@hostname STARTG u :owner_ip gameNumber time_t * * WOLv2: * :user1!WWOL@hostname STARTG u :user1 xxx.xxx.xxx.xxx user2 xxx.xxx.xxx.xxx :gameNumber time_t */ if ((numparams >= 2) && (params[1])) { int i; std::memset(temp, 0, sizeof(temp)); char ** e = irc_get_listelems(params[1]); /* FIXME: support wildcards! */ if (!(game = conn_get_game(conn))) { ERROR0("conn has not game"); irc_unget_listelems(e); return 0; } std::strcat(temp, ":"); if (tag_check_wolv1(conn_get_clienttag(conn))) { t_connection * user; channel = conn_get_channel(conn); for (user = channel_get_first(channel); user; user = channel_get_next()) { char const * name = conn_get_chatname(user); if (std::strcmp(conn_get_chatname(conn), name) != 0) { std::snprintf(temp, sizeof(temp), "%s ", addr_num_to_ip_str(conn_get_addr(game_get_owner(game)))); } } } else { for (i = 0; ((e) && (e[i])); i++) { t_connection * user; const char * addr = NULL; if ((user = connlist_find_connection_by_accountname(e[i]))) { addr = addr_num_to_ip_str(conn_get_addr(user)); } std::snprintf(_temp_a, sizeof(_temp_a), "%s %s ", e[i], addr); std::strcat(temp, _temp_a); } std::strcat(temp, ":"); } game_set_status(game, game_status_started); std::snprintf(_temp_a, sizeof(_temp_a), "%u %" PRId64, game_get_id(game), static_cast(game_get_start_time(game))); std::strcat(temp, _temp_a); for (i = 0; ((e) && (e[i])); i++) { t_connection * user; if ((user = connlist_find_connection_by_accountname(e[i]))) { message_send_text(user, message_wol_start_game, conn, temp); } } if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "STARTG :Not enough parameters"); return 0; } static int _handle_advertr_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; std::memset(temp, 0, sizeof(temp)); /** * Heres the imput expected * ADVERTR [channel] * * Heres the output expected * :[servername] ADVERTR 5 [channel] */ if ((numparams >= 1) && (params[0])) { std::snprintf(temp, sizeof(temp), "5 %s", params[0]); message_send_text(conn, message_wol_advertr, conn, temp); } else irc_send(conn, ERR_NEEDMOREPARAMS, "ADVERTR :Not enough parameters"); return 0; } static int _handle_advertc_command(t_connection * conn, int numparams, char ** params, char * text) { /* FIXME: Not implemented yet */ return 0; } static int _handle_chanchk_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; t_channel * channel; std::memset(temp, 0, sizeof(temp)); /** * Heres the imput expected * chanchk [channel] * * Heres the output expected * :[servername] CHANCHK [channel] */ if ((numparams >= 1) && (params[0])) { if ((channel = channellist_find_channel_by_name(irc_convert_ircname(params[0]), NULL, NULL))) { std::snprintf(temp, sizeof temp, "%s", params[0]); message_send_text(conn, message_wol_chanchk, conn, temp); } else { /* FIXME: This is not dumped from original servers... this is probably wrong */ std::snprintf(temp, sizeof(temp), "%s :No such channel", params[0]); irc_send(conn, ERR_NOSUCHCHANNEL, temp); } } else irc_send(conn, ERR_NEEDMOREPARAMS, "CHANCHK :Not enough parameters"); return 0; } static int _handle_getbuddy_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; char _temp[MAX_IRC_MESSAGE_LEN]; char const * friend_name; t_account * my_acc; t_account * friend_acc; t_list * flist; t_friend * fr; int num; unsigned int uid; int i; std::memset(temp, 0, sizeof(temp)); std::memset(_temp, 0, sizeof(_temp)); /** * Heres the output expected * :[servername] 333 [user] [buddy_name1]`[buddy_name2]` * * Without names: * :[servername] 333 [user] */ my_acc = conn_get_account(conn); num = account_get_friendcount(my_acc); flist = account_get_friends(my_acc); if (flist != NULL) { for (i = 0; i < num; i++) { if ((!(uid = account_get_friend(my_acc, i))) || (!(fr = friendlist_find_uid(flist, uid)))) { eventlog(eventlog_level_error, __FUNCTION__, "friend uid in list"); continue; } friend_acc = friend_get_account(fr); friend_name = account_get_name(friend_acc); std::snprintf(_temp, sizeof(_temp), "%s`", friend_name); std::strcat(temp, _temp); } } irc_send(conn, RPL_GET_BUDDY, temp); return 0; } static int _handle_addbuddy_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; t_account * my_acc; t_account * friend_acc; std::memset(temp, 0, sizeof(temp)); /** * Heres the imput expected * ADDBUDDY [buddy_name] * * Heres the output expected * :[servername] 334 [user] [buddy_name] */ if ((numparams >= 1) && (params[0])) { my_acc = conn_get_account(conn); if (friend_acc = accountlist_find_account(params[0])) { account_add_friend(my_acc, friend_acc); /* FIXME: Check if add friend is done if not then send right message */ std::snprintf(temp, sizeof(temp), "%s", params[0]); irc_send(conn, RPL_ADD_BUDDY, temp); } else { std::snprintf(temp, sizeof(temp), "%s :No such nick", params[0]); irc_send(conn, ERR_NOSUCHNICK, temp); /* NOTE: this is not dumped from WOL, this not shows message but in Emperor doesnt gives name to list, in RA2 have no efect */ } } else irc_send(conn, ERR_NEEDMOREPARAMS, "ADDBUDDY :Not enough parameters"); return 0; } static int _handle_delbuddy_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; char const * friend_name; t_account * my_acc; int num; std::memset(temp, 0, sizeof(temp)); /** * Heres the imput expected * DELBUDDY [buddy_name] * * Heres the output expected * :[servername] 335 [user] [buddy_name] */ if ((numparams >= 1) && (params[0])) { friend_name = params[0]; my_acc = conn_get_account(conn); num = account_remove_friend2(my_acc, friend_name); /** * FIXME: Check if remove friend is done if not then send right message * Btw I dont know another then RPL_DEL_BUDDY message yet. */ std::snprintf(temp, sizeof(temp), "%s", friend_name); irc_send(conn, RPL_DEL_BUDDY, temp); } else irc_send(conn, ERR_NEEDMOREPARAMS, "DELBUDDY :Not enough parameters"); return 0; } static int _handle_host_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; t_connection * user; std::memset(temp, 0, sizeof(temp)); if ((numparams >= 1) && (params[0])) { if ((user = connlist_find_connection_by_accountname(params[0]))) { std::snprintf(temp, sizeof(temp), ": %s", text); message_send_text(user, message_type_host, conn, temp); } else { std::snprintf(temp, sizeof(temp), "%s :No such nick", params[0]); irc_send(conn, ERR_NOSUCHNICK, temp); } } else irc_send(conn, ERR_NEEDMOREPARAMS, "HOST :Not enough parameters"); return 0; } static int _handle_invmsg_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; char ** e; t_connection * user; int i; /** * Here is the imput expected: * INVMSG [channel] [unknown] [invited],[invited2_optional] * [unknown] can be 1 or 2 * * Here is the output expected: * :user!WWOL@hostname INVMSG [invited] [channel] [unknown] */ if ((numparams >= 3) && (params[0]) && (params[1]) && (params[2])) { std::memset(temp, 0, sizeof(temp)); e = irc_get_listelems(params[2]); for (i = 0; ((e) && (e[i])); i++) { if ((user = connlist_find_connection_by_accountname(e[i]))) { std::snprintf(temp, sizeof(temp), "%s %s", params[0], params[1]); /* FIXME: set user to linvitelist! */ message_send_text(user, message_type_invmsg, conn, temp); } } if (e) irc_unget_listelems(e); } else { irc_send(conn, ERR_NEEDMOREPARAMS, "INVMSG :Not enough parameters"); } return 0; } static int _handle_invdel_command(t_connection * conn, int numparams, char ** params, char * text) { /* FIXME: Not implemented yet */ return 0; } static int _handle_userip_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; t_connection * user; const char * addr = NULL; std::memset(temp, 0, sizeof(temp)); if ((numparams >= 1) && (params[0])) { if ((user = connlist_find_connection_by_accountname(params[0]))) { addr = addr_num_to_ip_str(conn_get_addr(user)); //FIXME: We are not sure of first parameter. It can be also nickname of command sender std::snprintf(temp, sizeof(temp), "%s %s", params[0], addr); message_send_text(conn, message_wol_userip, conn, temp); } else { std::snprintf(temp, sizeof(temp), "%s :No such nick", params[0]); irc_send(conn, ERR_NOSUCHNICK, temp); } } else irc_send(conn, ERR_NEEDMOREPARAMS, "USERIP :Not enough parameters"); return 0; } /** * LADDER Server commands: */ static int _ladder_send(t_connection * conn, char const * command) { char data[MAX_IRC_MESSAGE_LEN + 1]; unsigned len = 0; t_packet* const p = packet_create(packet_class_raw); if (!p) { return -1; } if (command) len = (std::strlen(command) + 6); if (len > MAX_IRC_MESSAGE_LEN) { eventlog(eventlog_level_error, __FUNCTION__, "message to send is too large ({} bytes)", len); packet_del_ref(p); return -1; } std::sprintf(data, "\r\n\r\n\r\n%s", command); packet_set_size(p, 0); packet_append_data(p, data, len); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] sent \"{}\"", conn_get_socket(conn), data); conn_push_outqueue(conn, p); packet_del_ref(p); /* In ladder server we must destroy connection after send packet */ conn_set_state(conn, conn_state_destroy); return 0; } static int _ladder_is_integer(char * test) { for (char const* ptr = test; *ptr; ++ptr) { if (!std::isdigit(*ptr)) { return 0; /* Is not integer */ } else return 1; /* Is integer */ } return -1; /* We shouldn't get here */ } static int _handle_listsearch_command(t_connection * conn, int numparams, char ** params, char * text) { char ** e; int i = 0; unsigned rank = 0; unsigned points = 0; unsigned wins = 0; unsigned losses = 0; unsigned disconnects = 0; char temp[MAX_IRC_MESSAGE_LEN]; char data[MAX_IRC_MESSAGE_LEN]; t_account * cl_account; t_clienttag cl_tag; t_ladder_id id = ladder_id_solo; std::memset(data, 0, sizeof(data)); if ((numparams >= 1) && (params[0]) && (text)) { cl_tag = tag_sku_to_uint(std::atoi(params[0])); if (e = irc_get_ladderelems(text)) { //TIMESTAMP 1147130452 //TOTAL 12033 //NOTFOUND // TIMESTAMP 1188740860 // 'TOTAL 27466 /* std::sprintf(temp,"TIMESTAMP %lu\n", std::time(NULL)); std::strcat(data,temp); std::sprintf(temp,"TOTAL 88\n"); std::strcat(data,temp);*/ for (i = 0; e[i]; i++) { /* Now we have in e[i] names */ if (e[i] && (std::strcmp(e[i], ":") != 0)) { cl_account = accountlist_find_account(e[i]); if (cl_account && cl_tag && (rank = account_get_ladder_rank(cl_account, cl_tag, id))) { points = account_get_ladder_points(cl_account, cl_tag, id); wins = account_get_ladder_wins(cl_account, cl_tag, id); losses = account_get_ladder_losses(cl_account, cl_tag, id); disconnects = account_get_ladder_disconnects(cl_account, cl_tag, id); std::sprintf(temp, "%u %s %u %u %u 0 %u\r\n", rank, e[i], points, wins, losses, disconnects); std::strcat(data, temp); } else std::strcat(data, "NOTFOUND\r\n"); } } irc_unget_ladderelems(e); _ladder_send(conn, data); } } else { WARN0("Not enough parameters"); conn_set_state(conn, conn_state_destroy); return 0; } return 0; } static int _handle_rungsearch_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; char data[MAX_IRC_MESSAGE_LEN]; unsigned rank = 0; unsigned points = 0; unsigned wins = 0; unsigned losses = 0; unsigned disconnects = 0; t_account * cl_account; t_clienttag cl_tag; t_ladder_id id = ladder_id_solo; std::memset(data, 0, sizeof(data)); if ((numparams >= 4) && (params[0]) && (params[1]) && (params[3])) { cl_tag = tag_sku_to_uint(std::atoi(params[3])); if ((cl_tag != CLIENTTAG_TIBERNSUN_UINT) && (cl_tag != CLIENTTAG_TIBSUNXP_UINT) && (cl_tag != CLIENTTAG_REDALERT2_UINT) && (cl_tag != CLIENTTAG_YURISREV_UINT)) { // PELISH: We are not supporting ladders for all WOL clients yet std::strcat(data, "\r\n"); _ladder_send(conn, data); DEBUG1("Wants rung search for SKU {}", params[3]); return 0; } if (_ladder_is_integer(params[0]) == 0) { /* rungsearch want to line for one player (nick is in params[0]) */ cl_account = accountlist_find_account(params[0]); if (cl_account && cl_tag && (rank = account_get_ladder_rank(cl_account, cl_tag, id))) { points = account_get_ladder_points(cl_account, cl_tag, id); wins = account_get_ladder_wins(cl_account, cl_tag, id); losses = account_get_ladder_losses(cl_account, cl_tag, id); disconnects = account_get_ladder_disconnects(cl_account, cl_tag, id); std::sprintf(temp, "%u %s %u %u %u 0 %u\r\n", rank, params[0], points, wins, losses, disconnects); } else std::sprintf(temp, "\r\n"); _ladder_send(conn, temp); } else { /* Standard RUNG search */ unsigned start = std::atoi(params[0]); unsigned count = std::atoi(params[1]); eventlog(eventlog_level_debug, __FUNCTION__, "Start({}) Count({})", start, count); LadderList* ladderList = NULL; ladderList = ladders.getLadderList(LadderKey(id, cl_tag, ladder_sort_default, ladder_time_default)); for (unsigned int i = start; i < start + count; i++) { const LadderReferencedObject* referencedObject = NULL; cl_account = NULL; if (((referencedObject = ladderList->getReferencedObject(i))) && (cl_account = referencedObject->getAccount())) { rank = account_get_ladder_rank(cl_account, cl_tag, id); points = account_get_ladder_points(cl_account, cl_tag, id); wins = account_get_ladder_wins(cl_account, cl_tag, id); losses = account_get_ladder_losses(cl_account, cl_tag, id); disconnects = account_get_ladder_disconnects(cl_account, cl_tag, id); std::sprintf(temp, "%u %s %u %u %u 0 %u\r\n", rank, account_get_name(cl_account), points, wins, losses, disconnects); std::strcat(data, temp); } else { std::strcat(data, "\r\n"); _ladder_send(conn, data); return 0; } } _ladder_send(conn, data); } } else { WARN0("Not enough parameters"); conn_set_state(conn, conn_state_destroy); return 0; } return 0; } static int _handle_highscore_command(t_connection * conn, int numparams, char ** params, char * text) { /* char ** e; int i = 0; unsigned rank = 2; unsigned points = 258; unsigned wins = 0; unsigned losses = 0; unsigned unknown = 0; // Here is nuber before Nick and Honor Badges in Yuri (1-999) unsigned disconnects = 0; char temp[MAX_IRC_MESSAGE_LEN]; char data[MAX_IRC_MESSAGE_LEN]; t_account * cl_account; t_clienttag cltag; std::memset(temp,0,sizeof(temp)); std::memset(data,0,sizeof(data)); if (text) e = irc_get_ladderelems(text); if (params[0]) cltag = tag_sku_to_uint(std::atoi(params[0])); for (i=0;e[i];i++) { if (e[i] && (std::strcmp(e[i], ":") != 0)) { cl_account = accountlist_find_account(e[i]); if (cl_account) { wins = account_get_normal_wins(cl_account, cltag); losses = account_get_normal_losses(cl_account, cltag); disconnects = account_get_normal_disconnects(cl_account, cltag); std::sprintf(temp,"%u %s %u %u %u %u %u\r\n",rank+i,e[i],points,wins,losses,unknown,disconnects); std::strcat(data,temp); } else std::strcat(data,"NOTFOUND\r\n"); } } if (e) irc_unget_ladderelems(e); _ladder_send(conn,data); */ conn_set_state(conn, conn_state_destroy); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_wol.h000066400000000000000000000024241462640461400203520ustar00rootroot00000000000000/* * Copyright (C) 2006,2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_WOL_PROTOS #define INCLUDED_HANDLE_WOL_PROTOS #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace bnetd { extern int handle_wol_con_command(t_connection * conn, char const * command, int numparams, char ** params, char * text); extern int handle_wol_log_command(t_connection * conn, char const * command, int numparams, char ** params, char * text); extern int handle_wol_welcome(t_connection * conn); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_wol_gameres.cpp000066400000000000000000003521331462640461400224150ustar00rootroot00000000000000/* * Copyright (C) 2008,2009 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_wol_gameres.h" #include #include #include "common/wol_gameres_protocol.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/tag.h" #include "common/bn_type.h" #include "connection.h" #include "game.h" #include "account.h" #ifdef WIN32_GUI #include #endif #include "common/setup_after.h" namespace pvpgn { namespace bnetd { /* handlers prototypes */ static int _client_sern(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sdfx(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_idno(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_gsku(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_dcon(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_lcon(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_type(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_trny(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_oosy(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_fini(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_dura(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cred(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_shrt(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_supr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_mode(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bamr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crat(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_aipl(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unit(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_scen(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmpl(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pngs(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pngr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plrs(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_spid(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_time(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_afps(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_proc(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_memo(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_vidm(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sped(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_vers(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_date(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_base(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tibr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_shad(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_flag(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tech(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_acco(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_brok(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_etim(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pspd(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_smem(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_svid(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_snam(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_gmap(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_dsvr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); /* Renegade Player related functions */ static int _client_pnam(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ploc(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_team(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pscr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ppts(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ptim(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_phlt(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pkil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ekil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_akil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_shot(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_hedf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_torf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_armf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_legf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crtf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pups(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_vkil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_vtim(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nkfv(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_squi(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pcrd(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bkil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_hedr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_torr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_armr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_legr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crtr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_flgc(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); /* Player related functions */ static int _client_nam0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nam1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nam2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nam3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nam4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nam5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nam6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_nam7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ipa7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cid7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_sid7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_tid7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_cmp7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_col7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_crd7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_inl7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unl7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_pll7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_bll7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_ink7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_unk7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_plk7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blk7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); static int _client_blc7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); typedef int(*t_wol_gamerestag)(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data); typedef struct { t_tag wol_gamerestag_uint; t_wol_gamerestag wol_gamerestag_handler; } t_wol_gamerestag_table_row; /* handler table */ static const t_wol_gamerestag_table_row wol_gamreres_htable[] = { { CLIENT_SERN_UINT, _client_sern }, { CLIENT_SDFX_UINT, _client_sdfx }, { CLIENT_IDNO_UINT, _client_idno }, { CLIENT_GSKU_UINT, _client_gsku }, { CLIENT_DCON_UINT, _client_dcon }, { CLIENT_LCON_UINT, _client_lcon }, { CLIENT_TYPE_UINT, _client_type }, { CLIENT_TRNY_UINT, _client_trny }, { CLIENT_OOSY_UINT, _client_oosy }, { CLIENT_FINI_UINT, _client_fini }, { CLIENT_DURA_UINT, _client_dura }, { CLIENT_CRED_UINT, _client_cred }, { CLIENT_SHRT_UINT, _client_shrt }, { CLIENT_SUPR_UINT, _client_supr }, { CLIENT_MODE_UINT, _client_mode }, { CLIENT_BAMR_UINT, _client_bamr }, { CLIENT_CRAT_UINT, _client_crat }, { CLIENT_AIPL_UINT, _client_aipl }, { CLIENT_UNIT_UINT, _client_unit }, { CLIENT_SCEN_UINT, _client_scen }, { CLIENT_CMPL_UINT, _client_cmpl }, { CLIENT_PNGS_UINT, _client_pngs }, { CLIENT_PNGR_UINT, _client_pngr }, { CLIENT_PLRS_UINT, _client_plrs }, { CLIENT_SPID_UINT, _client_spid }, { CLIENT_TIME_UINT, _client_time }, { CLIENT_AFPS_UINT, _client_afps }, { CLIENT_PROC_UINT, _client_proc }, { CLIENT_MEMO_UINT, _client_memo }, { CLIENT_VIDM_UINT, _client_vidm }, { CLIENT_SPED_UINT, _client_sped }, { CLIENT_VERS_UINT, _client_vers }, { CLIENT_DATE_UINT, _client_date }, { CLIENT_BASE_UINT, _client_base }, { CLIENT_TIBR_UINT, _client_tibr }, { CLIENT_SHAD_UINT, _client_shad }, { CLIENT_FLAG_UINT, _client_flag }, { CLIENT_TECH_UINT, _client_tech }, { CLIENT_BROK_UINT, _client_brok }, { CLIENT_ACCO_UINT, _client_acco }, { CLIENT_ETIM_UINT, _client_etim }, { CLIENT_PSPD_UINT, _client_pspd }, { CLIENT_SMEM_UINT, _client_smem }, { CLIENT_SVID_UINT, _client_svid }, { CLIENT_SNAM_UINT, _client_snam }, { CLIENT_GMAP_UINT, _client_gmap }, { CLIENT_DSVR_UINT, _client_dsvr }, /* Renegade Player related tags */ { CLIENT_PNAM_UINT, _client_pnam }, { CLIENT_PLOC_UINT, _client_ploc }, { CLIENT_TEAM_UINT, _client_team }, { CLIENT_PSCR_UINT, _client_pscr }, { CLIENT_PPTS_UINT, _client_ppts }, { CLIENT_PTIM_UINT, _client_ptim }, { CLIENT_PHLT_UINT, _client_phlt }, { CLIENT_PKIL_UINT, _client_pkil }, { CLIENT_EKIL_UINT, _client_ekil }, { CLIENT_AKIL_UINT, _client_akil }, { CLIENT_SHOT_UINT, _client_shot }, { CLIENT_HEDF_UINT, _client_hedf }, { CLIENT_TORF_UINT, _client_torf }, { CLIENT_ARMF_UINT, _client_armf }, { CLIENT_LEGF_UINT, _client_legf }, { CLIENT_CRTF_UINT, _client_crtf }, { CLIENT_PUPS_UINT, _client_pups }, { CLIENT_VKIL_UINT, _client_vkil }, { CLIENT_VTIM_UINT, _client_vtim }, { CLIENT_NKFV_UINT, _client_nkfv }, { CLIENT_SQUI_UINT, _client_squi }, { CLIENT_PCRD_UINT, _client_pcrd }, { CLIENT_BKIL_UINT, _client_bkil }, { CLIENT_HEDR_UINT, _client_hedr }, { CLIENT_TORR_UINT, _client_torr }, { CLIENT_ARMR_UINT, _client_armr }, { CLIENT_LEGR_UINT, _client_legr }, { CLIENT_CRTR_UINT, _client_crtr }, { CLIENT_FLGC_UINT, _client_flgc }, /* Player related tags */ { CLIENT_NAM0_UINT, _client_nam0 }, { CLIENT_NAM1_UINT, _client_nam1 }, { CLIENT_NAM2_UINT, _client_nam2 }, { CLIENT_NAM3_UINT, _client_nam3 }, { CLIENT_NAM4_UINT, _client_nam4 }, { CLIENT_NAM5_UINT, _client_nam5 }, { CLIENT_NAM6_UINT, _client_nam6 }, { CLIENT_NAM7_UINT, _client_nam7 }, { CLIENT_IPA0_UINT, _client_ipa0 }, { CLIENT_IPA1_UINT, _client_ipa1 }, { CLIENT_IPA2_UINT, _client_ipa2 }, { CLIENT_IPA3_UINT, _client_ipa3 }, { CLIENT_IPA4_UINT, _client_ipa4 }, { CLIENT_IPA5_UINT, _client_ipa5 }, { CLIENT_IPA6_UINT, _client_ipa6 }, { CLIENT_IPA7_UINT, _client_ipa7 }, { CLIENT_CID0_UINT, _client_cid0 }, { CLIENT_CID1_UINT, _client_cid1 }, { CLIENT_CID2_UINT, _client_cid2 }, { CLIENT_CID3_UINT, _client_cid3 }, { CLIENT_CID4_UINT, _client_cid4 }, { CLIENT_CID5_UINT, _client_cid5 }, { CLIENT_CID6_UINT, _client_cid6 }, { CLIENT_CID7_UINT, _client_cid7 }, { CLIENT_SID0_UINT, _client_sid0 }, { CLIENT_SID1_UINT, _client_sid1 }, { CLIENT_SID2_UINT, _client_sid2 }, { CLIENT_SID3_UINT, _client_sid3 }, { CLIENT_SID4_UINT, _client_sid4 }, { CLIENT_SID5_UINT, _client_sid5 }, { CLIENT_SID6_UINT, _client_sid6 }, { CLIENT_SID7_UINT, _client_sid7 }, { CLIENT_TID0_UINT, _client_tid0 }, { CLIENT_TID1_UINT, _client_tid1 }, { CLIENT_TID2_UINT, _client_tid2 }, { CLIENT_TID3_UINT, _client_tid3 }, { CLIENT_TID4_UINT, _client_tid4 }, { CLIENT_TID5_UINT, _client_tid5 }, { CLIENT_TID6_UINT, _client_tid6 }, { CLIENT_TID7_UINT, _client_tid7 }, { CLIENT_CMP0_UINT, _client_cmp0 }, { CLIENT_CMP1_UINT, _client_cmp1 }, { CLIENT_CMP2_UINT, _client_cmp2 }, { CLIENT_CMP3_UINT, _client_cmp3 }, { CLIENT_CMP4_UINT, _client_cmp4 }, { CLIENT_CMP5_UINT, _client_cmp5 }, { CLIENT_CMP6_UINT, _client_cmp6 }, { CLIENT_CMP7_UINT, _client_cmp7 }, { CLIENT_COL0_UINT, _client_col0 }, { CLIENT_COL1_UINT, _client_col1 }, { CLIENT_COL2_UINT, _client_col2 }, { CLIENT_COL3_UINT, _client_col3 }, { CLIENT_COL4_UINT, _client_col4 }, { CLIENT_COL5_UINT, _client_col5 }, { CLIENT_COL6_UINT, _client_col6 }, { CLIENT_COL7_UINT, _client_col7 }, { CLIENT_CRD0_UINT, _client_crd0 }, { CLIENT_CRD1_UINT, _client_crd1 }, { CLIENT_CRD2_UINT, _client_crd2 }, { CLIENT_CRD3_UINT, _client_crd3 }, { CLIENT_CRD4_UINT, _client_crd4 }, { CLIENT_CRD5_UINT, _client_crd5 }, { CLIENT_CRD6_UINT, _client_crd6 }, { CLIENT_CRD7_UINT, _client_crd7 }, { CLIENT_INB0_UINT, _client_inb0 }, { CLIENT_INB1_UINT, _client_inb1 }, { CLIENT_INB2_UINT, _client_inb2 }, { CLIENT_INB3_UINT, _client_inb3 }, { CLIENT_INB4_UINT, _client_inb4 }, { CLIENT_INB5_UINT, _client_inb5 }, { CLIENT_INB6_UINT, _client_inb6 }, { CLIENT_INB7_UINT, _client_inb7 }, { CLIENT_UNB0_UINT, _client_unb0 }, { CLIENT_UNB1_UINT, _client_unb1 }, { CLIENT_UNB2_UINT, _client_unb2 }, { CLIENT_UNB3_UINT, _client_unb3 }, { CLIENT_UNB4_UINT, _client_unb4 }, { CLIENT_UNB5_UINT, _client_unb5 }, { CLIENT_UNB6_UINT, _client_unb6 }, { CLIENT_UNB7_UINT, _client_unb7 }, { CLIENT_PLB0_UINT, _client_plb0 }, { CLIENT_PLB1_UINT, _client_plb1 }, { CLIENT_PLB2_UINT, _client_plb2 }, { CLIENT_PLB3_UINT, _client_plb3 }, { CLIENT_PLB4_UINT, _client_plb4 }, { CLIENT_PLB5_UINT, _client_plb5 }, { CLIENT_PLB6_UINT, _client_plb6 }, { CLIENT_PLB7_UINT, _client_plb7 }, { CLIENT_BLB0_UINT, _client_blb0 }, { CLIENT_BLB1_UINT, _client_blb1 }, { CLIENT_BLB2_UINT, _client_blb2 }, { CLIENT_BLB3_UINT, _client_blb3 }, { CLIENT_BLB4_UINT, _client_blb4 }, { CLIENT_BLB5_UINT, _client_blb5 }, { CLIENT_BLB6_UINT, _client_blb6 }, { CLIENT_BLB7_UINT, _client_blb7 }, { CLIENT_INL0_UINT, _client_inl0 }, { CLIENT_INL1_UINT, _client_inl1 }, { CLIENT_INL2_UINT, _client_inl2 }, { CLIENT_INL3_UINT, _client_inl3 }, { CLIENT_INL4_UINT, _client_inl4 }, { CLIENT_INL5_UINT, _client_inl5 }, { CLIENT_INL6_UINT, _client_inl6 }, { CLIENT_INL7_UINT, _client_inl7 }, { CLIENT_UNL0_UINT, _client_unl0 }, { CLIENT_UNL1_UINT, _client_unl1 }, { CLIENT_UNL2_UINT, _client_unl2 }, { CLIENT_UNL3_UINT, _client_unl3 }, { CLIENT_UNL4_UINT, _client_unl4 }, { CLIENT_UNL5_UINT, _client_unl5 }, { CLIENT_UNL6_UINT, _client_unl6 }, { CLIENT_UNL7_UINT, _client_unl7 }, { CLIENT_PLL0_UINT, _client_pll0 }, { CLIENT_PLL1_UINT, _client_pll1 }, { CLIENT_PLL2_UINT, _client_pll2 }, { CLIENT_PLL3_UINT, _client_pll3 }, { CLIENT_PLL4_UINT, _client_pll4 }, { CLIENT_PLL5_UINT, _client_pll5 }, { CLIENT_PLL6_UINT, _client_pll6 }, { CLIENT_PLL7_UINT, _client_pll7 }, { CLIENT_BLL0_UINT, _client_bll0 }, { CLIENT_BLL1_UINT, _client_bll1 }, { CLIENT_BLL2_UINT, _client_bll2 }, { CLIENT_BLL3_UINT, _client_bll3 }, { CLIENT_BLL4_UINT, _client_bll4 }, { CLIENT_BLL5_UINT, _client_bll5 }, { CLIENT_BLL6_UINT, _client_bll6 }, { CLIENT_BLL7_UINT, _client_bll7 }, { CLIENT_INK0_UINT, _client_ink0 }, { CLIENT_INK1_UINT, _client_ink1 }, { CLIENT_INK2_UINT, _client_ink2 }, { CLIENT_INK3_UINT, _client_ink3 }, { CLIENT_INK4_UINT, _client_ink4 }, { CLIENT_INK5_UINT, _client_ink5 }, { CLIENT_INK6_UINT, _client_ink6 }, { CLIENT_INK7_UINT, _client_ink7 }, { CLIENT_UNK0_UINT, _client_unk0 }, { CLIENT_UNK1_UINT, _client_unk1 }, { CLIENT_UNK2_UINT, _client_unk2 }, { CLIENT_UNK3_UINT, _client_unk3 }, { CLIENT_UNK4_UINT, _client_unk4 }, { CLIENT_UNK5_UINT, _client_unk5 }, { CLIENT_UNK6_UINT, _client_unk6 }, { CLIENT_UNK7_UINT, _client_unk7 }, { CLIENT_PLK0_UINT, _client_plk0 }, { CLIENT_PLK1_UINT, _client_plk1 }, { CLIENT_PLK2_UINT, _client_plk2 }, { CLIENT_PLK3_UINT, _client_plk3 }, { CLIENT_PLK4_UINT, _client_plk4 }, { CLIENT_PLK5_UINT, _client_plk5 }, { CLIENT_PLK6_UINT, _client_plk6 }, { CLIENT_PLK7_UINT, _client_plk7 }, { CLIENT_BLK0_UINT, _client_blk0 }, { CLIENT_BLK1_UINT, _client_blk1 }, { CLIENT_BLK2_UINT, _client_blk2 }, { CLIENT_BLK3_UINT, _client_blk3 }, { CLIENT_BLK4_UINT, _client_blk4 }, { CLIENT_BLK5_UINT, _client_blk5 }, { CLIENT_BLK6_UINT, _client_blk6 }, { CLIENT_BLK7_UINT, _client_blk7 }, { CLIENT_BLC0_UINT, _client_blc0 }, { CLIENT_BLC1_UINT, _client_blc1 }, { CLIENT_BLC2_UINT, _client_blc2 }, { CLIENT_BLC3_UINT, _client_blc3 }, { CLIENT_BLC4_UINT, _client_blc4 }, { CLIENT_BLC5_UINT, _client_blc5 }, { CLIENT_BLC6_UINT, _client_blc6 }, { CLIENT_BLC7_UINT, _client_blc7 }, { 1, NULL } }; static int handle_wolgameres_tag(t_wol_gameres_result * game_result, t_tag gamerestag, wol_gameres_type type, int size, void const * data) { t_wol_gamerestag_table_row const *p; for (p = wol_gamreres_htable; p->wol_gamerestag_uint != 1; p++) { if (gamerestag == p->wol_gamerestag_uint) { if (p->wol_gamerestag_handler != NULL) return ((p->wol_gamerestag_handler)(game_result, type, size, data)); } } return -1; } static wol_gameres_type wol_gameres_type_from_int(int type) { switch (type) { case DATA_TYPE_BYTE: return wol_gameres_type_byte; case DATA_TYPE_BOOL: return wol_gameres_type_bool; case DATA_TYPE_TIME: return wol_gameres_type_time; case DATA_TYPE_INT: return wol_gameres_type_int; case DATA_TYPE_STRING: return wol_gameres_type_string; case DATA_TYPE_BIGINT: return wol_gameres_type_bigint; default: WARN1("type {} is not defined", type); return wol_gameres_type_unknown; } } static unsigned long wol_gameres_get_long_from_data(int size, const void * data) { unsigned int temp = 0; const char * chdata; int i = 0; if (!data) { ERROR0("got NULL data"); return 0; } if (size < 4) return 0; chdata = (const char*)data; while (i < size) { temp = temp + (unsigned int)bn_int_nget(*((bn_int *)chdata)); i += 4; if (i < size) { *(chdata++); *(chdata++); *(chdata++); *(chdata++); } } return temp; } static t_wol_gameres_result * gameres_result_create() { t_wol_gameres_result * gameres_result; gameres_result = (t_wol_gameres_result*)xmalloc(sizeof(t_wol_gameres_result)); gameres_result->game = NULL; gameres_result->results = NULL; gameres_result->senderid = -1; gameres_result->myaccount = NULL; return gameres_result; } static int gameres_result_destroy(t_wol_gameres_result * gameres_result) { if (!gameres_result) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL gameres_result"); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "destroying gameres_result"); xfree(gameres_result); return 0; } extern int handle_wol_gameres_packet(t_connection * c, t_packet const * const packet) { unsigned offset; t_tag wgtag; char wgtag_str[5]; unsigned datatype; unsigned datalen; void const * data; wol_gameres_type type; t_wol_gameres_result * gameres_result; DEBUG2("[{}] got WOL Gameres packet length {}", conn_get_socket(c), packet_get_size(packet)); offset = sizeof(t_wolgameres_header); if ((unsigned int)bn_int_nget(*((bn_int *)packet_get_data_const(packet, offset, 4))) == 0) offset += 4; /* Just trying to get RNGD working */ gameres_result = gameres_result_create(); while (offset < packet_get_size(packet)) { if (packet_get_size(packet) < offset + 4) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad WOL Gameres packet (missing tag)", conn_get_socket(c)); return -1; } wgtag = (unsigned int)bn_int_nget(*((bn_int *)packet_get_data_const(packet, offset, 4))); offset += 4; if (packet_get_size(packet) < offset + 2) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad WOL Gameres packet (missing datatype)", conn_get_socket(c)); return -1; } datatype = (unsigned int)bn_short_nget(*((bn_short *)packet_get_data_const(packet, offset, 2))); offset += 2; if (packet_get_size(packet) < offset + 2) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad WOL Gameres packet (missing datalen)", conn_get_socket(c)); return -1; } datalen = (unsigned int)bn_short_nget(*((bn_short *)packet_get_data_const(packet, offset, 2))); offset += 2; if (packet_get_size(packet) < offset + 1) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad WOL Gameres packet (missing data)", conn_get_socket(c)); return -1; } data = packet_get_data_const(packet, offset, 1); type = wol_gameres_type_from_int(datatype); if (handle_wolgameres_tag(gameres_result, wgtag, type, datalen, data) != 0) { char ch_data[255]; /* FIXME: this is not so good */ tag_uint_to_str(wgtag_str, wgtag); switch (type) { case wol_gameres_type_bool: case wol_gameres_type_byte: std::snprintf(ch_data, sizeof(ch_data), "%" PRIu8, bn_byte_get(*((bn_byte *)data))); break; case wol_gameres_type_int: case wol_gameres_type_time: std::snprintf(ch_data, sizeof(ch_data), "%" PRIu32, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_string: std::snprintf(ch_data, sizeof(ch_data), "%s", (char*)data); break; case wol_gameres_type_bigint: std::snprintf(ch_data, sizeof(ch_data), "%lu", wol_gameres_get_long_from_data(datalen, data)); break; default: std::snprintf(ch_data, sizeof(ch_data), "UNKNOWN"); break; } eventlog(eventlog_level_warn, __FUNCTION__, "[{}] got unknown WOL Gameres tag: {}, data type {}, data lent {}, data {}", conn_get_socket(c), wgtag_str, datatype, datalen, ch_data); } datalen = 4 * ((datalen + 3) / 4); offset += datalen; } if (!(gameres_result->game)) { ERROR0("game not found (game == NULL)"); return -1; } if (!(gameres_result->myaccount)) { ERROR0("have not account of sender"); return -1; } if (!(gameres_result->results)) { ERROR0("have not results of game"); return -1; } game_set_report(gameres_result->game, gameres_result->myaccount, "head", "body"); if (game_set_reported_results(gameres_result->game, gameres_result->myaccount, gameres_result->results) < 0) xfree((void *)gameres_result->results); conn_set_game(account_get_conn(gameres_result->myaccount), NULL, NULL, NULL, game_type_none, 0); gameres_result_destroy(gameres_result); return 0; } /** * WOL Gamreres tag handlers */ static int _client_sern(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* SER# */ return 0; } static int _client_sdfx(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { int sdfx; switch (type) { case wol_gameres_type_bool: sdfx = (unsigned int)bn_byte_get(*((bn_byte *)data)); break; default: WARN1("got unknown gameres type {} for SDFX", static_cast(type)); return -1; } if (sdfx) DEBUG0("SDFX == true"); else DEBUG0("SDFX == false"); return 0; } static int _client_idno(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int gameidnumber = 0; t_game * game; switch (type) { case wol_gameres_type_int: case wol_gameres_type_time: gameidnumber = (unsigned int)bn_int_nget(*((bn_int *)data)); break; default: WARN1("got unknown gameres type {} for IDNO", static_cast(type)); break; } if ((gameidnumber) && (game = gamelist_find_game_byid(gameidnumber))) { //&& (game_get_status(game) & game_status_started)) { DEBUG2("found started game \"{}\" for gameid {}", game_get_name(game), gameidnumber); game_result->game = game; game_result->results = (t_game_result*)xmalloc(sizeof(t_game_result)* game_get_count(game)); } return 0; } static int _client_gsku(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int sku; const t_game * game = game_result->game; switch (type) { case wol_gameres_type_int: sku = (unsigned int)bn_int_nget(*((bn_int *)data)); break; default: WARN1("got unknown gameres type {} for GSKU", static_cast(type)); return -1; } if ((sku) && (game) && (game_get_clienttag(game) == tag_sku_to_uint(sku))) { DEBUG1("got proper WOL game resolution of {}", clienttag_get_title(tag_sku_to_uint(sku))); } return 0; } static int _client_dcon(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int dcon; switch (type) { case wol_gameres_type_int: dcon = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("DCON is: {}", dcon); break; default: WARN1("got unknown gameres type {} for DCON", static_cast(type)); break; } return 0; } static int _client_lcon(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int lcon; switch (type) { case wol_gameres_type_int: lcon = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("LCON is: {}", lcon); break; default: WARN1("got unknown gameres type {} for LCON", static_cast(type)); break; } return 0; } static int _client_type(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { int gametype; switch (type) { case wol_gameres_type_bool: gametype = (unsigned int)bn_byte_get(*((bn_byte *)data)); DEBUG1("TYPE is: {}", gametype); break; default: WARN1("got unknown gameres type {} for TYPE", static_cast(type)); break; } return 0; } static int _client_trny(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool tournament; switch (type) { case wol_gameres_type_bool: tournament = (bool)bn_byte_get(*((bn_byte *)data)); break; case wol_gameres_type_int: tournament = (bool)bn_int_nget(*((bn_int *)data)); break; case wol_gameres_type_string: if (std::strcmp("TY ", (const char *)data) == 0) /* for RNGD */ tournament = true; else if (std::strcmp("TN ", (const char *)data) == 0) /* for RNGD */ tournament = false; else { WARN1("got unknown string for TRNY: {}", (char *)data); tournament = false; } break; default: WARN1("got unknown gameres type {} for TRNY", static_cast(type)); break; } if (tournament) DEBUG0("game was tournament"); else DEBUG0("game was not tournament"); return 0; } static int _client_oosy(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool outofsync; switch (type) { case wol_gameres_type_bool: outofsync = (bool)bn_byte_get(*((bn_byte *)data)); break; default: WARN1("got unknown gameres type {} for OOSY", static_cast(type)); break; } if (outofsync) DEBUG0("Game has Out of Sync YES"); else DEBUG0("Game has Out of Sync NO"); return 0; } static int _client_fini(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* Not implemented yet */ return 0; } static int _client_dura(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int duration; /* Game dureation in seconds??? */ // DURA : Game Duration // DURA 00 06 00 04 [00 00 01 F4] // DURA 00 06 00 04 [00 00 00 06] switch (type) { case wol_gameres_type_int: case wol_gameres_type_time: duration = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Game duration {} seconds", duration); break; default: WARN1("got unknown gameres type {} for DURA", static_cast(type)); break; } return 0; } static int _client_cred(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int credits; switch (type) { case wol_gameres_type_int: credits = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Game was start with {} credits", credits); break; default: WARN1("got unknown gameres type {} for CRED", static_cast(type)); break; } return 0; } static int _client_shrt(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool shortgame; switch (type) { case wol_gameres_type_bool: shortgame = (bool)bn_byte_get(*((bn_byte *)data)); break; case wol_gameres_type_int: shortgame = (bool)bn_int_nget(*((bn_int *)data)); break; default: WARN1("got unknown gameres type {} for SHRT", static_cast(type)); return -1; } if (shortgame) DEBUG0("Game has shortgame ON"); else DEBUG0("Game has shortgame OFF"); return 0; } static int _client_supr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool superweapons; switch (type) { case wol_gameres_type_bool: superweapons = (bool)bn_byte_get(*((bn_byte *)data)); break; case wol_gameres_type_int: superweapons = (bool)bn_int_nget(*((bn_int *)data)); break; default: WARN1("got unknown gameres type {} for SUPR", static_cast(type)); return -1; } if (superweapons) DEBUG0("Game has superweapons ON"); else DEBUG0("Game has superweapons OFF"); return 0; } static int _client_mode(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ switch (type) { case wol_gameres_type_string: DEBUG1("Game mode: {}", (char *)data); /* For RNGD */ break; default: WARN1("got unknown gameres type {} for MODE", static_cast(type)); break; } return 0; } static int _client_bamr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* Not implemented yet */ return 0; } static int _client_crat(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool crates; switch (type) { case wol_gameres_type_bool: crates = (bool)bn_byte_get(*((bn_byte *)data)); break; case wol_gameres_type_int: crates = (bool)bn_int_nget(*((bn_int *)data)); break; case wol_gameres_type_string: if (std::strcmp("ON", (const char *)data) == 0) crates = true; else if (std::strcmp("OFF", (const char *)data) == 0) crates = false; else { WARN1("got unknown string for CRAT: {}", (char *)data); crates = false; } break; default: WARN1("got unknown gameres type {} for CRAT", static_cast(type)); return -1; } if (crates) DEBUG0("Game has crates ON"); else DEBUG0("Game has crates OFF"); return 0; } static int _client_aipl(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int aicount; if (type == wol_gameres_type_int) { aicount = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Game has {} AI players", aicount); } else { WARN1("got unknown gameres type {} for AIPL", static_cast(type)); return 0; } return 0; } static int _client_unit(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int units; switch (type) { case wol_gameres_type_int: units = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Game was started with {} units", units); break; default: WARN1("got unknown gameres type {} for UNIT", static_cast(type)); break; } return 0; } static int _client_scen(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_string: DEBUG1("Secnario {}", (char *)data); break; case wol_gameres_type_int: DEBUG1("Secnario num {}", (unsigned int)bn_int_nget(*((bn_int *)data))); break; default: WARN1("got unknown gameres type {} for SCEN", static_cast(type)); break; } return 0; } static int _client_cmpl(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { int cmpl; switch (type) { case wol_gameres_type_byte: cmpl = (unsigned int)bn_byte_get(*((bn_byte *)data)); DEBUG1("CMPL is: {}", cmpl); break; default: WARN1("got unknown gameres type {} for CMPL", static_cast(type)); break; } return 0; } static int _client_pngs(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int pngs; switch (type) { case wol_gameres_type_int: pngs = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("PNGS {} ", pngs); break; default: WARN1("got unknown gameres type {} for PNGS", static_cast(type)); break; } return 0; } static int _client_pngr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int pngr; switch (type) { case wol_gameres_type_int: pngr = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("PNGR {} ", pngr); break; default: WARN1("got unknown gameres type {} for PNGR", static_cast(type)); break; } return 0; } static int _client_plrs(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int playercount = 0; switch (type) { case wol_gameres_type_int: case wol_gameres_type_time: playercount = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Player count {} ", playercount); break; default: WARN1("got unknown gameres type {} for PLRS", static_cast(type)); break; } return 0; } static int _client_spid(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { int spid = -1; switch (type) { case wol_gameres_type_int: spid = (int)bn_int_nget(*((bn_int *)data)); break; default: WARN1("got unknown gameres type {} for SPID", static_cast(type)); break; } if (spid != -1) { DEBUG1("Setting sender ID to {}", spid); game_result->senderid = spid; } return 0; } static int _client_time(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* PELISH: Time when was game started. This time is WOL STARTG packet time +-2 sec */ std::time_t time; switch (type) { case wol_gameres_type_string: DEBUG1("Game was start at {} ", (char *)data); break; case wol_gameres_type_time: time = (std::time_t) bn_int_nget(*((bn_int *)data)); DEBUG1("Game was start at {} ", ctime(&time)); break; default: WARN1("got unknown gameres type {} for TIME", static_cast(type)); break; } return 0; } static int _client_afps(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int afps; switch (type) { case wol_gameres_type_int: afps = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Average Frames per Second {} ", afps); break; default: WARN1("got unknown gameres type {} for AFPS", static_cast(type)); break; } return 0; } static int _client_proc(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_string: DEBUG1("Procesor {}", (char *)data); break; default: WARN1("got unknown gameres type {} for PROC", static_cast(type)); break; } return 0; } static int _client_memo(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* Not implemented yet */ return 0; } static int _client_vidm(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* Not implemented yet */ return 0; } static int _client_sped(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int speed; //SPED 00 01 00 01 [00 00 00 00] if (type == wol_gameres_type_byte) { speed = (unsigned int)bn_byte_get(*((bn_byte *)data)); } else if (type == wol_gameres_type_int) { speed = (unsigned int)bn_int_nget(*((bn_int *)data)); } else { WARN1("got unknown gameres type {} for SPED", static_cast(type)); return 0; } DEBUG1("Game speed {}", speed); return 0; } static int _client_vers(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int version; if (type == wol_gameres_type_string) { DEBUG1("Version of client {}", (char *)data); } else if (type == wol_gameres_type_int) { version = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Version of client {}", version); } else WARN1("got unknown gameres type {} for VERS", static_cast(type)); return 0; } static int _client_date(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { //WOLv1: DATE 00 14 00 08 52 11 ea 00 01 c5 cd a9 switch (type) { case wol_gameres_type_string: DEBUG1("Date {}", (char *)data); break; default: WARN1("got unknown gameres type {} for DATE", static_cast(type)); break; } return 0; } static int _client_base(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool bases; //WOLv1 BASE 00 07 00 03 ON switch (type) { case wol_gameres_type_bool: bases = (bool)bn_byte_get(*((bn_byte *)data)); break; case wol_gameres_type_string: if (std::strcmp("ON", (const char *)data) == 0) bases = true; else if (std::strcmp("OFF", (const char *)data) == 0) bases = false; else { WARN1("got unknown string for BASE: {}", (char *)data); bases = false; } break; default: WARN1("got unknown gameres type {} for BASE", static_cast(type)); break; } if (bases) DEBUG0("Game has bases ON"); else DEBUG0("Game has bases OFF"); return 0; } static int _client_tibr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool tiberium; if (type == wol_gameres_type_bool) { tiberium = (bool)bn_byte_get(*((bn_byte *)data)); if (tiberium) DEBUG0("Game has tiberium ON"); else DEBUG0("Game has tiberium OFF"); } else if (type == wol_gameres_type_string) { DEBUG1("Game has tiberium {}", (char *)data); } else WARN1("got unknown gameres type {} for TIBR", static_cast(type)); return 0; } static int _client_shad(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool shadows; switch (type) { case wol_gameres_type_bool: shadows = (bool)bn_byte_get(*((bn_byte *)data)); break; case wol_gameres_type_string: if (std::strcmp("ON", (const char *)data) == 0) shadows = true; else if (std::strcmp("OFF", (const char *)data) == 0) shadows = false; else { WARN1("got unknown string for SHAD: {}", (char *)data); shadows = false; } break; default: WARN1("got unknown gameres type {} for SHAD", static_cast(type)); break; } if (shadows) DEBUG0("Game has shadows ON"); else DEBUG0("Game has shadows OFF"); return 0; } static int _client_flag(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { bool captureflag; switch (type) { case wol_gameres_type_bool: captureflag = (bool)bn_byte_get(*((bn_byte *)data)); break; case wol_gameres_type_string: if (std::strcmp("ON", (const char *)data) == 0) captureflag = true; else if (std::strcmp("OFF", (const char *)data) == 0) captureflag = false; else { WARN1("got unknown string for FLAG: {}", (char *)data); captureflag = false; } break; default: WARN1("got unknown gameres type {} for FLAG", static_cast(type)); return -1; } if (captureflag) DEBUG0("Game has capture the flag ON"); else DEBUG0("Game has capture the flag OFF"); return 0; } static int _client_tech(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int techlevel; if (type == wol_gameres_type_int) { techlevel = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Game has techlevel {}", techlevel); } else WARN1("got unknown gameres type {} for TECH", static_cast(type)); return 0; } static int _client_brok(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int brok; /* This tag sends YURI */ //BROK 00 06 00 04 00 00 00 02 if (type == wol_gameres_type_int) { brok = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Game has BROK {}", brok); } else WARN1("got unknown gameres type {} for BROK", static_cast(type)); return 0; } static int _client_acco(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int acco; /* This tag sends YURI */ switch (type) { case wol_gameres_type_int: acco = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Game has ACCO {}", acco); break; default: WARN1("got unknown gameres type {} for ACCO", static_cast(type)); break; } return 0; } static int _client_etim(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ /* Elapsed time??? this tag got unknown data type 0x0014 */ return 0; } static int _client_pspd(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ unsigned int procesorspeed; switch (type) { case wol_gameres_type_int: procesorspeed = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Server have processor speed: {} MHz", procesorspeed); break; default: WARN1("got unknown gameres type {} for PSPD", static_cast(type)); break; } return 0; } static int _client_smem(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ unsigned int servermemory; switch (type) { case wol_gameres_type_int: servermemory = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Server have {} memory", servermemory); break; default: WARN1("got unknown gameres type {} for SMEM", static_cast(type)); break; } return 0; } static int _client_svid(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ /* Server ID ??? this tag got unknown data type 0x0014 */ return 0; } static int _client_snam(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ switch (type) { case wol_gameres_type_string: DEBUG1("Server name: {}", (char *)data); break; default: WARN1("got unknown gameres type {} for SNAM", static_cast(type)); break; } return 0; } static int _client_gmap(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ switch (type) { case wol_gameres_type_string: DEBUG1("Game map: {}", (char *)data); break; default: WARN1("got unknown gameres type {} for GMAP", static_cast(type)); break; } return 0; } static int _client_dsvr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { /* This tag sends RNGD only */ unsigned int dsvr; switch (type) { case wol_gameres_type_byte: dsvr = (unsigned int)bn_byte_get(*((bn_byte *)data)); DEBUG1("DSVR: {}", dsvr); break; default: WARN1("got unknown gameres type {} for SCEN", static_cast(type)); break; } return 0; } /* Renegade player tag handlers */ static int _client_pnam(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_string: DEBUG1("Player name: {}", (char *)data); break; default: WARN1("got unknown gameres type {} for PNAM", static_cast(type)); break; } return 0; } static int _client_ploc(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int playerloc; switch (type) { case wol_gameres_type_int: playerloc = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Player locale: {}", playerloc); break; default: WARN1("got unknown gameres type {} for PLOC", static_cast(type)); break; } return 0; } static int _client_team(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int team; switch (type) { case wol_gameres_type_int: team = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Team: {}", team); break; default: WARN1("got unknown gameres type {} for TEAM", static_cast(type)); break; } return 0; } static int _client_pscr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int score; switch (type) { case wol_gameres_type_int: score = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Players score: {}", score); break; default: WARN1("got unknown gameres type {} for PSCR", static_cast(type)); break; } return 0; } static int _client_ppts(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int points; switch (type) { case wol_gameres_type_time: /* PELISH: THIS is not a BUG! This is Renegade developers bug */ points = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Points {}", points); break; default: WARN1("got unknown gameres type {} for PPTS", static_cast(type)); break; } return 0; } static int _client_ptim(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int playertime; switch (type) { case wol_gameres_type_int: playertime = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Player game time {}", playertime); break; default: WARN1("got unknown gameres type {} for PTIM", static_cast(type)); break; } return 0; } static int _client_phlt(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int health; switch (type) { case wol_gameres_type_int: health = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Health: {}", health); break; default: WARN1("got unknown gameres type {} for PHLT", static_cast(type)); break; } return 0; } static int _client_pkil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int kills; switch (type) { case wol_gameres_type_int: kills = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Kills: {}", kills); break; default: WARN1("got unknown gameres type {} for PKIL", static_cast(type)); break; } return 0; } static int _client_ekil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int enemy_kills; switch (type) { case wol_gameres_type_int: enemy_kills = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Enemy kills: {}", enemy_kills); break; default: WARN1("got unknown gameres type {} for EKIL", static_cast(type)); break; } return 0; } static int _client_akil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int ally_kills; switch (type) { case wol_gameres_type_int: ally_kills = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Ally kills {}", ally_kills); break; default: WARN1("got unknown gameres type {} for AKIL", static_cast(type)); break; } return 0; } static int _client_shot(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int shots; switch (type) { case wol_gameres_type_int: shots = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Shots {}", shots); break; default: WARN1("got unknown gameres type {} for SHOT", static_cast(type)); break; } return 0; } static int _client_hedf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int hedf; switch (type) { case wol_gameres_type_int: hedf = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("hedf {}", hedf); break; default: WARN1("got unknown gameres type {} for HEDF", static_cast(type)); break; } return 0; } static int _client_torf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int torf; switch (type) { case wol_gameres_type_int: torf = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("torf {}", torf); break; default: WARN1("got unknown gameres type {} for TORF", static_cast(type)); break; } return 0; } static int _client_armf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int armf; switch (type) { case wol_gameres_type_int: armf = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("armf {}", armf); break; default: WARN1("got unknown gameres type {} for ARMF", static_cast(type)); break; } return 0; } static int _client_legf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int legf; switch (type) { case wol_gameres_type_int: legf = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("legf {}", legf); break; default: WARN1("got unknown gameres type {} for LEGF", static_cast(type)); break; } return 0; } static int _client_crtf(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int crtf; switch (type) { case wol_gameres_type_int: crtf = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("crtf {}", crtf); break; default: WARN1("got unknown gameres type {} for CRTF", static_cast(type)); break; } return 0; } static int _client_pups(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int pups; switch (type) { case wol_gameres_type_int: pups = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("pups {}", pups); break; default: WARN1("got unknown gameres type {} for PUPS", static_cast(type)); break; } return 0; } static int _client_vkil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int victory_kills; switch (type) { case wol_gameres_type_int: victory_kills = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Victory kills {}", victory_kills); break; default: WARN1("got unknown gameres type {} for VKIL", static_cast(type)); break; } return 0; } static int _client_vtim(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int victory_time; switch (type) { case wol_gameres_type_int: victory_time = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("Victory time {}", victory_time); break; default: WARN1("got unknown gameres type {} for VTIM", static_cast(type)); break; } return 0; } static int _client_nkfv(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int nkfv; switch (type) { case wol_gameres_type_int: nkfv = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("nkfv {}", nkfv); break; default: WARN1("got unknown gameres type {} for NKFV", static_cast(type)); break; } return 0; } static int _client_squi(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int squi; switch (type) { case wol_gameres_type_int: squi = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("squi {}", squi); break; default: WARN1("got unknown gameres type {} for SQUI", static_cast(type)); break; } return 0; } static int _client_pcrd(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int pcrd; switch (type) { case wol_gameres_type_int: pcrd = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("pcrd {}", pcrd); break; default: WARN1("got unknown gameres type {} for PCRD", static_cast(type)); break; } return 0; } static int _client_bkil(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int bkil; switch (type) { case wol_gameres_type_int: bkil = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("bkil {}", bkil); break; default: WARN1("got unknown gameres type {} for BKIL", static_cast(type)); break; } return 0; } static int _client_hedr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int hedr; switch (type) { case wol_gameres_type_int: hedr = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("hedr {}", hedr); break; default: WARN1("got unknown gameres type {} for HEDR", static_cast(type)); break; } return 0; } static int _client_torr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int torr; switch (type) { case wol_gameres_type_int: torr = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("torr {}", torr); break; default: WARN1("got unknown gameres type {} for TORR", static_cast(type)); break; } return 0; } static int _client_armr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int armr; switch (type) { case wol_gameres_type_int: armr = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("armr {}", armr); break; default: WARN1("got unknown gameres type {} for ARMR", static_cast(type)); break; } return 0; } static int _client_legr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int legr; switch (type) { case wol_gameres_type_int: legr = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("legr {}", legr); break; default: WARN1("got unknown gameres type {} for LEGR", static_cast(type)); break; } return 0; } static int _client_crtr(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int crtr; switch (type) { case wol_gameres_type_int: crtr = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("crtr {}", crtr); break; default: WARN1("got unknown gameres type {} for CRTR", static_cast(type)); break; } return 0; } static int _client_flgc(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { unsigned int flgc; switch (type) { case wol_gameres_type_int: flgc = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG1("flgc {}", flgc); break; default: WARN1("got unknown gameres type {} for FLGC", static_cast(type)); break; } return 0; } /* player related tag handlers */ static int _cl_nam_general(t_wol_gameres_result * game_result, int num, wol_gameres_type type, int size, void const * data) { int senderid = game_result->senderid; t_account * account = accountlist_find_account((char const *)data); DEBUG2("Name of player {}: {}", num, static_cast(data)); game_result->otheraccount = account; if (senderid == num) { DEBUG1("Packet was sent by {}", (char *)data); game_result->myaccount = account; } return 0; } static int _client_nam0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 0, type, size, data); } static int _client_nam1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 1, type, size, data); } static int _client_nam2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 2, type, size, data); } static int _client_nam3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 3, type, size, data); } static int _client_nam4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 4, type, size, data); } static int _client_nam5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 5, type, size, data); } static int _client_nam6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 6, type, size, data); } static int _client_nam7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_nam_general(game_result, 7, type, size, data); } static int _cl_ipa_general(t_wol_gameres_result * game_result, int num, wol_gameres_type type, int size, void const * data) { unsigned int ipaddress; switch (type) { case wol_gameres_type_int: ipaddress = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG2("IP address of player{}: {}", num, ipaddress); break; default: WARN2("got unknown gameres type {} for IPA{}", static_cast(type), num); break; } return 0; } static int _client_ipa0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 0, type, size, data); } static int _client_ipa1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 1, type, size, data); } static int _client_ipa2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 2, type, size, data); } static int _client_ipa3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 3, type, size, data); } static int _client_ipa4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 4, type, size, data); } static int _client_ipa5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 5, type, size, data); } static int _client_ipa6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 6, type, size, data); } static int _client_ipa7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ipa_general(game_result, 7, type, size, data); } static int _cl_cid_general(t_wol_gameres_result * game_result, int num, wol_gameres_type type, int size, void const * data) { unsigned int clanid; switch (type) { case wol_gameres_type_int: clanid = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG2("Clan ID of player{}: {}", num, clanid); break; default: WARN2("got unknown gameres type {} for CID{}", static_cast(type), num); break; } return 0; } static int _client_cid0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 0, type, size, data); } static int _client_cid1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 1, type, size, data); } static int _client_cid2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 2, type, size, data); } static int _client_cid3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 3, type, size, data); } static int _client_cid4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 4, type, size, data); } static int _client_cid5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 5, type, size, data); } static int _client_cid6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 6, type, size, data); } static int _client_cid7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cid_general(game_result, 7, type, size, data); } static int _cl_sid_general(t_wol_gameres_result * game_result, int num, wol_gameres_type type, int size, void const * data) { unsigned int sideid; switch (type) { case wol_gameres_type_int: sideid = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG2("Side ID of player{}: {}", num, sideid); break; default: WARN2("got unknown gameres type {} for SID{}", static_cast(type), num); break; } return 0; } static int _client_sid0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 0, type, size, data); } static int _client_sid1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 1, type, size, data); } static int _client_sid2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 2, type, size, data); } static int _client_sid3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 3, type, size, data); } static int _client_sid4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 4, type, size, data); } static int _client_sid5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 5, type, size, data); } static int _client_sid6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 6, type, size, data); } static int _client_sid7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_sid_general(game_result, 7, type, size, data); } static int _cl_tid_general(t_wol_gameres_result * game_result, int num, wol_gameres_type type, int size, void const * data) { unsigned int teamid; switch (type) { case wol_gameres_type_int: teamid = (unsigned int)bn_int_nget(*((bn_int *)data)); DEBUG2("Team ID of player{}: {}", num, teamid); break; default: WARN2("got unknown gameres type {} for TID{}", static_cast(type), num); break; } return 0; } static int _client_tid0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 0, type, size, data); } static int _client_tid1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 1, type, size, data); } static int _client_tid2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 2, type, size, data); } static int _client_tid3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 3, type, size, data); } static int _client_tid4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 4, type, size, data); } static int _client_tid5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 5, type, size, data); } static int _client_tid6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 6, type, size, data); } static int _client_tid7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_tid_general(game_result, 7, type, size, data); } static int _cl_cmp_general(t_wol_gameres_result * game_result, int num, wol_gameres_type type, int size, void const * data) { //CMPx 00 06 00 04 00 00 02 00 //0x0100=WIN 0x0200(0x0210)=LOSE 0x0300=DISCONECT t_account * other_account = game_result->otheraccount; t_game * game = game_result->game; t_game_result result; t_game_result * results = game_result->results; int resultnum; switch (type) { case wol_gameres_type_int: resultnum = (unsigned int)bn_int_nget(*((bn_int *)data)); break; default: WARN2("got unknown gameres type {} for CMP{}", static_cast(type), num); return 0; } DEBUG2("Got {} player resultnum {}", num, resultnum); resultnum &= 0x0000FF00; resultnum = resultnum >> 8; if ((!game) || (!other_account)) { ERROR0("got corrupt gameres packet - game == NULL || other_account == NULL"); return 0; } unsigned int i = 0; for (; i < game_get_count(game); i++) { if (game_get_player(game, i) == other_account) break; } switch (resultnum) { case 1: DEBUG0("WIN"); result = game_result_win; break; case 2: DEBUG0("LOSS"); result = game_result_loss; break; case 3: DEBUG0("DISCONECT"); result = game_result_disconnect; break; default: DEBUG2("Got wrong {} player resultnum {}", num, resultnum); result = game_result_disconnect; break; } if (results) { results[i] = result; game_result->results = results; DEBUG0("game result was set"); } return 0; } static int _client_cmp0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 0, type, size, data); } static int _client_cmp1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 1, type, size, data); } static int _client_cmp2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 2, type, size, data); } static int _client_cmp3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 3, type, size, data); } static int _client_cmp4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 4, type, size, data); } static int _client_cmp5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 5, type, size, data); } static int _client_cmp6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 6, type, size, data); } static int _client_cmp7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_cmp_general(game_result, 7, type, size, data); } static int _cl_col_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_int: DEBUG2("Player {} colour num: {}", num, (unsigned int)bn_int_nget(*((bn_int *)data))); break; default: WARN2("got unknown gameres type {} for COL{}", static_cast(type), num); break; } return 0; } static int _client_col0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(0, type, size, data); } static int _client_col1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(1, type, size, data); } static int _client_col2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(2, type, size, data); } static int _client_col3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(3, type, size, data); } static int _client_col4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(4, type, size, data); } static int _client_col5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(5, type, size, data); } static int _client_col6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(6, type, size, data); } static int _client_col7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_col_general(7, type, size, data); } static int _cl_crd_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} had {} credits on end of game", num, (unsigned int)bn_int_nget(*((bn_int *)data))); break; default: WARN2("got unknown gameres type {} for CRD{}", static_cast(type), num); break; } return 0; } static int _client_crd0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(0, type, size, data); } static int _client_crd1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(1, type, size, data); } static int _client_crd2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(2, type, size, data); } static int _client_crd3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(3, type, size, data); } static int _client_crd4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(4, type, size, data); } static int _client_crd5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(5, type, size, data); } static int _client_crd6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(6, type, size, data); } static int _client_crd7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_crd_general(7, type, size, data); } static int _cl_inb_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} build {} infantry", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} build {} infantry", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for INB{}", static_cast(type), num); break; } return 0; } static int _client_inb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(0, type, size, data); } static int _client_inb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(1, type, size, data); } static int _client_inb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(2, type, size, data); } static int _client_inb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(3, type, size, data); } static int _client_inb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(4, type, size, data); } static int _client_inb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(5, type, size, data); } static int _client_inb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(6, type, size, data); } static int _client_inb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inb_general(7, type, size, data); } static int _cl_unb_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} build {} units", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} build {} units", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for UNB{}", static_cast(type), num); break; } return 0; } static int _client_unb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(0, type, size, data); } static int _client_unb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(1, type, size, data); } static int _client_unb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(2, type, size, data); } static int _client_unb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(3, type, size, data); } static int _client_unb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(4, type, size, data); } static int _client_unb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(5, type, size, data); } static int _client_unb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(6, type, size, data); } static int _client_unb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unb_general(7, type, size, data); } static int _cl_plb_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} build {} planes", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} build {} planes", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for PLB{}", static_cast(type), num); break; } return 0; } static int _client_plb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(0, type, size, data); } static int _client_plb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(1, type, size, data); } static int _client_plb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(2, type, size, data); } static int _client_plb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(3, type, size, data); } static int _client_plb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(4, type, size, data); } static int _client_plb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(5, type, size, data); } static int _client_plb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(6, type, size, data); } static int _client_plb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plb_general(7, type, size, data); } static int _cl_blb_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} build {} buildings", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} build {} buildings", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for BLB{}", static_cast(type), num); break; } return 0; } static int _client_blb0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(0, type, size, data); } static int _client_blb1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(1, type, size, data); } static int _client_blb2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(2, type, size, data); } static int _client_blb3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(3, type, size, data); } static int _client_blb4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(4, type, size, data); } static int _client_blb5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(5, type, size, data); } static int _client_blb6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(6, type, size, data); } static int _client_blb7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blb_general(7, type, size, data); } static int _cl_inl_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} lost {} infantry", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} lost {} infantry", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for INL{}", static_cast(type), num); break; } return 0; } static int _client_inl0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(0, type, size, data); } static int _client_inl1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(1, type, size, data); } static int _client_inl2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(2, type, size, data); } static int _client_inl3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(3, type, size, data); } static int _client_inl4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(4, type, size, data); } static int _client_inl5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(5, type, size, data); } static int _client_inl6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(6, type, size, data); } static int _client_inl7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_inl_general(7, type, size, data); } static int _cl_unl_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} lost {} units", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} lost {} units", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for UNL{}", static_cast(type), num); break; } return 0; } static int _client_unl0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(0, type, size, data); } static int _client_unl1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(1, type, size, data); } static int _client_unl2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(2, type, size, data); } static int _client_unl3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(3, type, size, data); } static int _client_unl4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(4, type, size, data); } static int _client_unl5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(5, type, size, data); } static int _client_unl6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(6, type, size, data); } static int _client_unl7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unl_general(7, type, size, data); } static int _cl_pll_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} lost {} planes", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} lost {} planes", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for PLL{}", static_cast(type), num); break; } return 0; } static int _client_pll0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(0, type, size, data); } static int _client_pll1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(1, type, size, data); } static int _client_pll2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(2, type, size, data); } static int _client_pll3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(3, type, size, data); } static int _client_pll4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(4, type, size, data); } static int _client_pll5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(5, type, size, data); } static int _client_pll6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(6, type, size, data); } static int _client_pll7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_pll_general(7, type, size, data); } static int _cl_bll_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} lost {} buildings", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} lost {} buildings", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for BLL{}", static_cast(type), num); break; } return 0; } static int _client_bll0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(0, type, size, data); } static int _client_bll1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(1, type, size, data); } static int _client_bll2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(2, type, size, data); } static int _client_bll3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(3, type, size, data); } static int _client_bll4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(4, type, size, data); } static int _client_bll5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(5, type, size, data); } static int _client_bll6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(6, type, size, data); } static int _client_bll7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_bll_general(7, type, size, data); } static int _cl_ink_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} killed {} infantry", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} killed {} infantry", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for INK{}", static_cast(type), num); break; } return 0; } static int _client_ink0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(0, type, size, data); } static int _client_ink1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(1, type, size, data); } static int _client_ink2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(2, type, size, data); } static int _client_ink3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(3, type, size, data); } static int _client_ink4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(4, type, size, data); } static int _client_ink5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(5, type, size, data); } static int _client_ink6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(6, type, size, data); } static int _client_ink7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_ink_general(7, type, size, data); } static int _cl_unk_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} killed {} units", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} killed {} units", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for UNK{}", static_cast(type), num); break; } return 0; } static int _client_unk0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(0, type, size, data); } static int _client_unk1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(1, type, size, data); } static int _client_unk2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(2, type, size, data); } static int _client_unk3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(3, type, size, data); } static int _client_unk4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(4, type, size, data); } static int _client_unk5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(5, type, size, data); } static int _client_unk6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(6, type, size, data); } static int _client_unk7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_unk_general(7, type, size, data); } static int _cl_plk_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} killed {} planes", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} killed {} planes", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for PLK{}", static_cast(type), num); break; } return 0; } static int _client_plk0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(0, type, size, data); } static int _client_plk1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(1, type, size, data); } static int _client_plk2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(2, type, size, data); } static int _client_plk3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(3, type, size, data); } static int _client_plk4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(4, type, size, data); } static int _client_plk5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(5, type, size, data); } static int _client_plk6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(6, type, size, data); } static int _client_plk7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_plk_general(7, type, size, data); } static int _cl_blk_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_time: DEBUG2("Player {} killed {} buildings", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} killed {} buildings", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for BLK{}", static_cast(type), num); break; } return 0; } static int _client_blk0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(0, type, size, data); } static int _client_blk1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(1, type, size, data); } static int _client_blk2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(2, type, size, data); } static int _client_blk3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(3, type, size, data); } static int _client_blk4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(4, type, size, data); } static int _client_blk5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(5, type, size, data); } static int _client_blk6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(6, type, size, data); } static int _client_blk7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blk_general(7, type, size, data); } static int _cl_blc_general(int num, wol_gameres_type type, int size, void const * data) { switch (type) { case wol_gameres_type_int: DEBUG2("Player {} captured {} buildings", num, bn_int_nget(*((bn_int *)data))); break; case wol_gameres_type_bigint: DEBUG2("Player {} captured {} buildings", num, wol_gameres_get_long_from_data(size, data)); break; default: WARN2("got unknown gameres type {} for BLC{}", static_cast(type), num); break; } return 0; } static int _client_blc0(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(0, type, size, data); } static int _client_blc1(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(1, type, size, data); } static int _client_blc2(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(2, type, size, data); } static int _client_blc3(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(3, type, size, data); } static int _client_blc4(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(4, type, size, data); } static int _client_blc5(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(5, type, size, data); } static int _client_blc6(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(6, type, size, data); } static int _client_blc7(t_wol_gameres_result * game_result, wol_gameres_type type, int size, void const * data) { return _cl_blc_general(7, type, size, data); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_wol_gameres.h000066400000000000000000000031421462640461400220530ustar00rootroot00000000000000/* * Copyright (C) 2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_WOL_GAMERES_PROTOS #define INCLUDED_HANDLE_WOL_GAMERES_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include "game.h" #include "account.h" #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { typedef enum : int { wol_gameres_type_unknown = 0, wol_gameres_type_byte = 1, wol_gameres_type_bool = 2, wol_gameres_type_time = 5, wol_gameres_type_int = 6, wol_gameres_type_string = 7, wol_gameres_type_bigint = 20 } wol_gameres_type; typedef struct wol_gameres_result { t_game * game; t_game_result * results; int senderid; t_account * myaccount; t_account * otheraccount; } t_wol_gameres_result; extern int handle_wol_gameres_packet(t_connection * c, t_packet const * const packet); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_wserv.cpp000066400000000000000000000207541462640461400212600ustar00rootroot00000000000000/* * Copyright (C) 2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2005 Bryan Biedenkapp (gatekeep@gmail.com) * Copyright (C) 2006,2007,2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "handle_wserv.h" #include #include #include #include "compat/strcasecmp.h" #include "common/irc_protocol.h" #include "common/eventlog.h" #include "common/tag.h" #include "common/util.h" #include "common/list.h" #include "common/addr.h" #include "common/trans.h" #include "prefs.h" #include "irc.h" #include "message.h" #include "tick.h" #include "server.h" #include "autoupdate.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef int(*t_wserv_command)(t_connection * conn, int numparams, char ** params, char * text); typedef struct { const char * wserv_command_string; t_wserv_command wserv_command_handler; } t_wserv_command_table_row; static int _handle_verchk_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_lobcount_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_whereto_command(t_connection * conn, int numparams, char ** params, char * text); static int _handle_quit_command(t_connection * conn, int numparams, char ** params, char * text); /* state "connected" handlers (on Westwood ServServ server are not loged-in) */ static const t_wserv_command_table_row wserv_con_command_table[] = { { "VERCHK", _handle_verchk_command }, { "LOBCOUNT", _handle_lobcount_command }, { "WHERETO", _handle_whereto_command }, { "QUIT", _handle_quit_command }, { NULL, NULL } }; extern int handle_wserv_con_command(t_connection * conn, char const * command, int numparams, char ** params, char * text) { t_wserv_command_table_row const *p; for (p = wserv_con_command_table; p->wserv_command_string != NULL; p++) { if (strcasecmp(command, p->wserv_command_string) == 0) { if (p->wserv_command_handler != NULL) return ((p->wserv_command_handler)(conn, numparams, params, text)); } } return -1; } static int _handle_verchk_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; t_clienttag clienttag; char const *ftphostname; char const *ftpusername; char const *ftppassword; /** * Heres the imput expected: * vercheck [SKU] [version] * * Here is output expected for ServServ server: * 1) Update non-existant: * :[servername] 602 [username] :Update record non-existant * 2) Update existant: * :[servername] 606 [username] :[ftpserveraddr] [ftpusername] [ftppaswd] [path] [file.rtp] [newversion] [SKU] REQ */ if (numparams >= 2) { clienttag = tag_sku_to_uint(std::atoi(params[0])); if (clienttag != CLIENTTAG_WWOL_UINT) conn_set_clienttag(conn, clienttag); const char* const filestring = autoupdate_check(ARCHTAG_WINX86_UINT, clienttag, TAG_UNKNOWN_UINT, params[1], params[0]); if (filestring) { //:westwood-patch.ea.com update world96 lore3/1.003 65539_65536_6400.rtp 65539 6400 REQ ftphostname = prefs_get_wol_autoupdate_serverhost(); ftpusername = prefs_get_wol_autoupdate_username(); ftppassword = prefs_get_wol_autoupdate_password(); std::snprintf(temp, sizeof(temp), ":%s %s %s %s 131075 %s REQ", ftphostname, ftpusername, ftppassword, filestring, params[0]); irc_send(conn, RPL_UPDATE_FTP, temp); xfree((void*)filestring); } else { irc_send(conn, RPL_UPDATE_NONEX, ":Update record non-existant"); } } else irc_send(conn, ERR_NEEDMOREPARAMS, "VERCHK :Not enough parameters"); return 0; } static int _handle_lobcount_command(t_connection * conn, int numparams, char ** params, char * text) { /* Ignore command but, return 1 */ irc_send(conn, RPL_LOBCOUNT, "1"); return 0; } static int _handle_whereto_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; /* Casted to avoid warnings */ const char * wolip; const char * wolname = prefs_get_servername(); const char * woltimezone = prefs_get_wol_timezone(); const char * wollong = prefs_get_wol_longitude(); const char * wollat = prefs_get_wol_latitude(); { /* trans support */ unsigned short port = conn_get_real_local_port(conn); unsigned int addr = conn_get_real_local_addr(conn); trans_net(conn_get_addr(conn), &addr, &port); wolip = addr_num_to_ip_str(addr); } //irc_send(conn,RPL_UPDATE_EXIST,":You must update before connecting!"); // Check if it's an allowed client type if (!tag_check_in_list(conn_get_clienttag(conn), prefs_get_allowed_clients())) { // This is for anyone game but not for Emperor if (conn_get_clienttag(conn) != CLIENTTAG_EMPERORBD_UINT) { // a.xwis.net 4009 RA2 // c.xwis.net 4000 TSUN // c.xwis.net 4010 RNGD // a.xwis.net 4010 YURI // snprintf(temp, sizeof(temp), ":a.xwis.net 4009 '0:%s' %s %s %s", wolname, woltimezone, wollong, wollat); // snprintf(temp, sizeof(temp), ":c.xwis.net 4000 '0:%s' %s %s %s", wolname, woltimezone, wollong, wollat); // snprintf(temp, sizeof(temp), ":c.xwis.net 4010 '0:%s' %s %s %s", wolname, woltimezone, wollong, wollat); // snprintf(temp, sizeof(temp), ":a.xwis.net 4010 '0:%s' %s %s %s", wolname, woltimezone, wollong, wollat); std::snprintf(temp, sizeof(temp), ":%s %d '0:%s' %s %s %s", wolip, BNETD_WOLV2_PORT, wolname, woltimezone, wollong, wollat); irc_send(conn, RPL_WOLSERV, temp); } // Only for Emperor: Battle for Dune if (conn_get_clienttag(conn) == CLIENTTAG_EMPERORBD_UINT) { std::snprintf(temp, sizeof(temp), ":%s %d '0:Emperor %s' %s %s %s", wolip, BNETD_WOLV2_PORT, wolname, woltimezone, wollong, wollat); irc_send(conn, RPL_WOLSERV, temp); } // Only for CnC Renegade if ((conn_get_clienttag(conn) == CLIENTTAG_RENEGADE_UINT) || (conn_get_clienttag(conn) == CLIENTTAG_RENGDFDS_UINT)) { snprintf(temp, sizeof(temp), ":%s 0 'Ping server' %s %s %s", wolip, woltimezone, wollong, wollat); irc_send(conn, RPL_PINGSERVER, temp); //I dont know for what is this server...? (used in renegade and yuri) //snprintf(temp, sizeof(temp), ":%s 4321 'Port Mangler' %s %s %s", wolip, woltimezone, wollong, wollat); //irc_send(conn,RPL_MANGLERSERV,temp); // on official server list is for Renegade also this server: //:noxchat1.wol.abn-sjc.ea.com 613 UserName :ea4.str.ea.com 0 '0,1,2,3,4,5,6,7,8,9,10:EA Ticket Server' -8 36.1083 -115.0582 } // There are servers for anyone game // FIXME: Check if is WOLv1 supported std::snprintf(temp, sizeof(temp), ":%s %d 'Live chat server' %s %s %s", wolip, BNETD_WOLV1_PORT, woltimezone, wollong, wollat); irc_send(conn, RPL_WOLSERV, temp); } // If game is not allowed than we still send this servers std::snprintf(temp, sizeof(temp), ":%s %d 'Gameres server' %s %s %s", wolip, BNETD_WGAMERES_PORT, woltimezone, wollong, wollat); irc_send(conn, RPL_GAMERESSERV, temp); std::snprintf(temp, sizeof(temp), ":%s %d 'Ladder server' %s %s %s", wolip, BNETD_WOLV2_PORT, woltimezone, wollong, wollat); irc_send(conn, RPL_LADDERSERV, temp); // There is Word Domination Tour server for Firestorm (maybe for future coding) //snprintf(temp, sizeof(temp), ":%s %d 'WDT server' %s %s %s", wolip, BNETD_WOLV2_PORT, woltimezone, wollong, wollat); //I dont know for what is this server...? //irc_send(conn,RPL_WDTSERV,temp); return 0; } static int _handle_quit_command(t_connection * conn, int numparams, char ** params, char * text) { irc_send(conn, RPL_QUIT, ":goodbye"); conn_set_state(conn, conn_state_destroy); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/handle_wserv.h000066400000000000000000000021431462640461400207150ustar00rootroot00000000000000/* * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HANDLE_WSERV_PROTOS #define INCLUDED_HANDLE_WSERV_PROTOS #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace bnetd { extern int handle_wserv_con_command(t_connection * conn, char const * command, int numparams, char ** params, char * text); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/handlers.h000066400000000000000000000020551462640461400200360ustar00rootroot00000000000000/* * Copyright (C) 2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __HANDLERS_H__ #define __HANDLERS_H__ #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace bnetd { typedef int(*t_handler)(t_connection *, t_packet const * const); typedef struct { int type; t_handler handler; } t_htable_row; } } #endif /* __HANDLERS_H__ */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/helpfile.cpp000066400000000000000000000203311462640461400203560ustar00rootroot00000000000000/* * Copyright (C) 2000 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "helpfile.h" #include #include #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/util.h" #include "common/xalloc.h" #include "common/xstring.h" #include "message.h" #include "command_groups.h" #include "account_wrap.h" #include "connection.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static std::map hfd_list; static std::FILE* hfd = NULL; /* helpfile descriptor */ static int list_commands(t_connection *); static std::FILE* get_hfd(t_connection * c); static std::FILE* get_hfd(t_connection * c) { t_gamelang lang = conn_get_gamelang_localized(c); std::map::iterator it = hfd_list.find(lang); if (it != hfd_list.end()) { return it->second; } // return enUS if language is not specified in language list return hfd_list[languages[0].gamelang]; } extern int helpfile_init(char const *filename) { if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } // iterate language list for (std::size_t i = 0; i < languages.size(); i++) { // get hfd of all localized help files std::string helpfile = i18n_filename(filename, languages[i].gamelang); if (!(hfd_list[languages[i].gamelang] = std::fopen(helpfile.c_str(), "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open help file \"{}\" for reading (std::fopen: {})", helpfile, std::strerror(errno)); return -1; } } return 0; } extern int helpfile_unload(void) { // destroy file handles for (std::map::iterator it = hfd_list.begin(); it != hfd_list.end(); ++it) { if (it->second != NULL) { if (std::fclose(it->second) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close help file after reading (std::fclose: {})", std::strerror(errno)); it->second = NULL; } } // clear list hfd_list.clear(); return 0; } extern int handle_help_command(t_connection * c, char const * text) { std::FILE* hfd = get_hfd(c); unsigned int i, j; char cmd[MAX_COMMAND_LEN]; if (hfd == NULL) { /* an error ocured opening readonly the help file, helpfile_unload was called, or helpfile_init hasn't been called */ message_send_text(c, message_type_error, c, localize(c, "Oops ! There is a problem with the help file. Please contact the administrator of the server.")); return -1; } for (i = 0; text[i] != ' ' && text[i] != '\0'; i++); /* skip command */ for (; text[i] == ' '; i++); if (text[i] == '/') /* skip / in front of command (if present) */ i++; for (j = 0; text[i] != ' ' && text[i] != '\0'; i++) /* get comm */ if (j < sizeof(cmd)-1) cmd[j++] = text[i]; cmd[j] = '\0'; /* just read the whole file and dump only the commands */ if (cmd[0] == '\0') { list_commands(c); return 0; } describe_command(c, cmd); return 0; } static int list_commands(t_connection * c) { std::FILE* hfd = get_hfd(c); char * line; int i; message_send_text(c, message_type_info, c, localize(c, "Chat commands : ")); std::rewind(hfd); while ((line = file_get_line(hfd)) != NULL) { for (i = 0; line[i] == ' '; i++); /* skip spaces in front of %command */ if (line[i] == '%') /* is this a command ? */ { char *p, *buffer; int al; int skip; unsigned int length, position; /* ok. now we must see if there are any aliases */ length = MAX_COMMAND_LEN + 1; position = 0; buffer = (char*)xmalloc(length + 1); /* initial memory allocation = pretty fair */ p = line + i; do { al = 0; skip = 0; for (i = 1; p[i] != ' ' && p[i] != '\0' && p[i] != '#'; i++); /* skip command */ if (p[i] == ' ') al = 1; /* we have something after the command.. must remember that */ p[i] = '\0'; /* end the string at the end of the command */ p[0] = '/'; /* change the leading character (% or space) read from the help file to / */ if (!(command_get_group(p) & account_get_command_groups(conn_get_account(c)))) skip = 1; if (length < std::strlen(p) + position + 1) /* if we don't have enough space in the buffer then get some */ length = std::strlen(p) + position + 1; /* the new length */ buffer = (char*)xrealloc(buffer, length + 1); buffer[position++] = ' '; /* put a space before each alias */ /* add the alias to the output string */ std::strcpy(buffer + position, p); position += std::strlen(p); if (al) { for (; p[i + 1] == ' '; i++); /* skip spaces */ if (p[i + 1] == '\0' || p[i + 1] == '#') { al = 0; continue; } p += i; /* jump to the next command */ } } while (al); if (!skip) message_send_text(c, message_type_info, c, buffer); /* print out the buffer */ xfree(buffer); } } file_get_line(NULL); // clear file_get_line buffer return 0; } extern int describe_command(t_connection * c, char const * cmd) { std::FILE* hfd = get_hfd(c); char * line; int i; /* ok. the client requested help for a specific command */ std::rewind(hfd); while ((line = file_get_line(hfd)) != NULL) { for (i = 0; line[i] == ' '; i++); /* skip spaces in front of %command */ if (line[i] == '%') /* is this a command ? */ { char *p; int al; /* ok. now we must see if there are any aliases */ p = line + i; do { al = 0; for (i = 1; p[i] != ' ' && p[i] != '\0' && p[i] != '#'; i++); /* skip command */ if (p[i] == ' ') al = 1; /* we have something after the command.. must remember that */ p[i] = '\0'; /* end the string at the end of the command */ if (strcasecmp(cmd, p + 1) == 0) /* is this the command the user asked for help ? */ { while ((line = file_get_line(hfd)) != NULL) { /* write everything until we get another % or EOF */ for (i = 0; line[i] == ' '; i++); /* skip spaces in front of a possible % */ if (line[i] == '%') { break; /* we reached another command */ } if (line[0] != '#' && line[i] != '\0') { /* is this a whole line comment ? */ /* truncate the line when a comment starts */ for (; line[i] != '\0' && line[i] != '#'; i++); if (line[i] == '#') line[i] = '\0'; // replace tabs with 3 spaces char threeSpace[4] = " "; char tabCharacter[3] = "\t"; char *tmp = str_replace(line, tabCharacter, threeSpace); line = tmp; // if text starts with slash then make it colored int j = 0; for (; line[j] == ' ' || line[j] == '\t'; j++); i18n_convert(c, line); if (line[j] == '/') message_send_text(c, message_type_error, c, line); else message_send_text(c, message_type_info, c, line); } } return 0; } if (al) { for (; p[i + 1] == ' '; i++); /* skip spaces */ if (p[i + 1] == '\0') { al = 0; continue; } p += i; /* jump to the next command */ } } while (al); } } file_get_line(NULL); // clear file_get_line buffer /* no description was found for this command. inform the user */ message_send_text(c, message_type_error, c, localize(c, "No help available for that command")); return -1; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/helpfile.h000066400000000000000000000022671462640461400200330ustar00rootroot00000000000000/* * Copyright (C) 2000 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef INCLUDED_HELPFILE_H #define INCLUDED_HELPFILE_H #ifndef JUST_NEED_TYPES #define JUST_NEED_TYPES #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int helpfile_init(char const * filename); extern int helpfile_unload(void); extern int handle_help_command(t_connection *, char const *); extern int describe_command(t_connection * c, char const * cmd); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/i18n.cpp000066400000000000000000000370401462640461400173520ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include "compat/strcasecmp.h" #include "compat/pdir.h" #include "common/token.h" #include "common/list.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/xstring.h" #include "common/util.h" #include "common/tag.h" #include "common/pugixml.h" #include "common/format.h" #include "account.h" #include "connection.h" #include "message.h" #include "helpfile.h" #include "channel.h" #include "prefs.h" #include "account_wrap.h" #include "command.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { const char * commonfile = "common.xml"; // filename template, actually file name is "common-{lang}.xml" /* Array with string translations, each string has array with pair language=translation { original = { { language = translate }, ... }, ... } */ std::map > translations = std::map >(); const char * _find_string(char const * text, t_gamelang gamelang); // http://msdn.microsoft.com/en-us/goglobal/bb896001.aspx // https://www.microsoft.com/resources/msdn/goglobal/default.mspx std::vector languages = { { GAMELANG_ENGLISH_UINT, "English", { "USA" } } /* enUS */ /* other countries should be loaded from common.xml of other languages */ }; int convert_utf8_to_windows1251(const char* utf8, char* windows1251, size_t n); int convert_windows1252_to_utf8(char * windows1252); extern int i18n_reload(void) { translations.clear(); i18n_load(); return 0; } extern int i18n_load(void) { std::string lang_filename; pugi::xml_document doc; std::string original, translate; std::vector files = dir_getfiles(prefs_get_i18ndir(), ".xml", true); // load common.xml from each directory for (int i = 0; i < files.size(); ++i) { lang_filename = files[i]; //i18n_filename(prefs_get_localizefile(), languages[i]); if (FILE *f = fopen(lang_filename.c_str(), "r")) { fclose(f); if (!doc.load_file(lang_filename.c_str())) { ERROR1("could not parse localization file \"{}\"", lang_filename); continue; } } else { // file not exists, ignore it continue; } // root node pugi::xml_node root_node = doc.child("root"); pugi::xml_node meta_node = root_node.child("meta"); // read language tag std::string lang_name = meta_node.child("language").child_value(); std::string lang_tag = meta_node.child("language").attribute("tag").as_string(); t_gamelang lang_tag_uint = tag_str_to_uint(lang_tag.c_str()); std::vector countries; // read all country codes for the current language pugi::xml_node country_nodes = root_node.child("meta").child("countries"); for (pugi::xml_node node = country_nodes.child("country"); node; node = node.next_sibling("country")) { countries.push_back( node.child_value() ); } // if the tag not found in languages then add it bool found; language_find_by_tag(lang_tag_uint, found); if (!found) { languages.push_back({ lang_tag_uint, xstrdup(lang_name.c_str()), countries }); } // read xml strings to map pugi::xml_node item_nodes = root_node.child("items"); for (pugi::xml_node node = item_nodes.child("item"); node; node = node.next_sibling("item")) { original = node.child_value("original"); if (original[0] == '\0') continue; //std::map >::iterator it = translations.find(original); // if not found then init //if (it == translations.end()) // translations[original] = std::map(); // check if translate string has a reference to another translation if (pugi::xml_attribute attr = node.child("translate").attribute("refid")) { if (pugi::xml_node n = item_nodes.find_child_by_attribute("id", attr.value())) translate = n.child_value("translate"); else { translate = original; //WARN2("could not find translate reference refid=\"{}\", use original string ({})", attr.value(), lang_filename.c_str()); } } else { translate = node.child_value("translate"); if (translate[0] == '\0') { translate = original; //WARN2("empty translate for \"{}\", use original string ({})", original.c_str(), lang_filename.c_str()); } } translations[original][lang_tag_uint] = translate; } INFO1("localization file loaded \"{}\"", lang_filename); } return 0; } extern std::string _localize(t_connection * c, const char * func, const char * fmt, const fmt::ArgList &args) { const char *format = fmt; std::string output(fmt); t_gamelang lang; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got bad connection"); return format; } try { if (lang = conn_get_gamelang_localized(c)) if (!(format = _find_string(fmt, lang))) format = fmt; // if not found use original output = fmt::format(format, args); char tmp[MAX_MESSAGE_LEN]; std::snprintf(tmp, sizeof tmp, "%s", output.c_str()); i18n_convert(c, tmp); output = tmp; } catch (const std::exception& e) { WARN2("Can't format translation string \"{}\" ({})", fmt, e.what()); } return output; } /* Find localized text for the given language */ const char * _find_string(char const * text, t_gamelang gamelang) { std::map >::iterator it = translations.find(text); if (it != translations.end()) { if (!it->second[gamelang].empty()) return it->second[gamelang].c_str(); } return NULL; } /* Add a locale tag into filename example: motd.txt -> motd-ruRU.txt */ extern std::string i18n_filename(const char * filename, t_tag gamelang) { std::string path = fmt::format("{}/{}/{}", prefs_get_i18ndir(), tag_uint_to_str2(gamelang), filename); // if localized file not found struct stat sfile = {}; if (stat(path.c_str(), &sfile) < 0) { // use default file path = fmt::format("{}/{}", prefs_get_i18ndir(), filename); } return path; } extern t_gamelang conn_get_gamelang_localized(t_connection * c) { t_gamelang gamelang = conn_get_gamelang(c); // force localize by user country if (prefs_get_localize_by_country()) if (const char * country = conn_get_country(c)) gamelang = gamelang_get_by_country(country); // if user set own language if (t_account * a = conn_get_account(c)) if (const char * l = account_get_userlang(a)) gamelang = tag_str_to_uint(l); // FIXME: Russian text displays not correctly directly in game in non-russian Starcraft // but most Russians use English Starcraft. // Instead of next hack we can add "if (conn_get_game(c) {}" in i18n_convert() and return transliterated text // // Return English text in game for Russian if (t_clienttag clienttag = conn_get_clienttag(c)) if (gamelang == GAMELANG_RUSSIAN_UINT && conn_get_game(c) && (clienttag == CLIENTTAG_STARCRAFT_UINT || clienttag == CLIENTTAG_BROODWARS_UINT || clienttag == CLIENTTAG_STARJAPAN_UINT || clienttag == CLIENTTAG_SHAREWARE_UINT || clienttag == CLIENTTAG_DIABLORTL_UINT || clienttag == CLIENTTAG_DIABLOSHR_UINT || clienttag == CLIENTTAG_WARCIIBNE_UINT)) { gamelang = GAMELANG_ENGLISH_UINT; } return gamelang; } /* Set custom user language (command) */ extern int handle_language_command(t_connection * c, char const *text) { // split command args std::vector args = split_command(text, 3); if (args[1].empty()) { // display command help describe_command(c, args[0].c_str()); std::string out = " "; std::string lang_str; // display available language list for (int i = 0; i < languages.size(); i++) { lang_str = tag_uint_to_str2(languages[i].gamelang); // select with brackets current user language if (languages[i].gamelang == conn_get_gamelang_localized(c)) out += "[" + lang_str + "]"; else out += lang_str; if (i < languages.size() - 1) out += ", "; } message_send_text(c, message_type_info, c, out.c_str()); return -1; } const char * userlang = args[1].c_str(); // validate given language bool found; t_language lang = language_find_by_tag(tag_str_to_uint(userlang), found); if (!found) { message_send_text(c, message_type_error, c, localize(c, "Bad language code.")); return -1; } if (t_account * account = conn_get_account(c)) { account_set_userlang(account, tag_uint_to_str2(lang.gamelang)); message_send_text(c, message_type_error, c, localize(c, "Set your language to {} ({})", tag_uint_to_str2(lang.gamelang), lang.name)); } return 0; } /* Return language tag by country code (for example: "USA") If not found then return default language (enUS) */ extern t_language language_find_by_country(const char * code, bool &found) { found = false; if (!code || code[0] == '\0') return languages[0]; for (int i = 0; i < languages.size(); i++) { for (int j = 0; j < languages[i].countries.size(); j++) if (strcasecmp(code, languages[i].countries[j].c_str()) == 0) { found = true; return languages[i]; } } return languages[0]; // default } /* Return language tag by gamelang If not found then return default language (enUS) */ extern t_language language_find_by_tag(t_tag gamelang, bool &found) { found = false; t_language lang; for (int i = 0; i < languages.size(); i++) { if (strcasecmp(tag_uint_to_str2(languages[i].gamelang).c_str(), tag_uint_to_str2(gamelang).c_str()) == 0) { found = true; return languages[i]; } } return languages[0]; } extern t_language language_get_by_country(const char * code) { bool found; return language_find_by_country(code, found); } extern t_gamelang gamelang_get_by_country(const char * code) { bool found; return language_find_by_country(code, found).gamelang; } extern t_language language_get_by_tag(t_gamelang gamelang) { bool found; return language_find_by_tag(gamelang, found); } /* Convert text with user native encoding */ extern char * i18n_convert(t_connection * c, char * buf) { t_gamelang gamelang = conn_get_gamelang_localized(c); t_clienttag clienttag = conn_get_clienttag(c); switch (gamelang) { case GAMELANG_RUSSIAN_UINT: // All Blizzard games except Warcraft 3 if (clienttag == CLIENTTAG_STARCRAFT_UINT || clienttag == CLIENTTAG_BROODWARS_UINT || clienttag == CLIENTTAG_STARJAPAN_UINT || clienttag == CLIENTTAG_SHAREWARE_UINT || clienttag == CLIENTTAG_DIABLORTL_UINT || clienttag == CLIENTTAG_DIABLOSHR_UINT || clienttag == CLIENTTAG_WARCIIBNE_UINT || clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT) { convert_utf8_to_windows1251(buf, buf, MAX_MESSAGE_LEN); } // There is an additional conversion in Starcraft and Diablo 2 if (clienttag == CLIENTTAG_STARCRAFT_UINT || clienttag == CLIENTTAG_BROODWARS_UINT || clienttag == CLIENTTAG_STARJAPAN_UINT || clienttag == CLIENTTAG_SHAREWARE_UINT || clienttag == CLIENTTAG_DIABLO2DV_UINT || clienttag == CLIENTTAG_DIABLO2XP_UINT) { convert_windows1252_to_utf8(buf); } break; default: break; } return buf; } /* * Text conversion for Russian text */ #ifndef REGION_CONVERT_RUSSIAN typedef struct ConvLetter { unsigned char win1251; int unicode; } Letter; static Letter g_letters[] = { { 0x82, 0x201A }, { 0x83, 0x0453 }, { 0x84, 0x201E }, { 0x85, 0x2026 }, { 0x86, 0x2020 }, { 0x87, 0x2021 }, { 0x88, 0x20AC }, { 0x89, 0x2030 }, { 0x8A, 0x0409 }, { 0x8B, 0x2039 }, { 0x8C, 0x040A }, { 0x8D, 0x040C }, { 0x8E, 0x040B }, { 0x8F, 0x040F }, { 0x90, 0x0452 }, { 0x91, 0x2018 }, { 0x92, 0x2019 }, { 0x93, 0x201C }, { 0x94, 0x201D }, { 0x95, 0x2022 }, { 0x96, 0x2013 }, { 0x97, 0x2014 }, { 0x99, 0x2122 }, { 0x9A, 0x0459 }, { 0x9B, 0x203A }, { 0x9C, 0x045A }, { 0x9D, 0x045C }, { 0x9E, 0x045B }, { 0x9F, 0x045F }, { 0xA0, 0x00A0 }, { 0xA1, 0x040E }, { 0xA2, 0x045E }, { 0xA3, 0x0408 }, { 0xA4, 0x00A4 }, { 0xA5, 0x0490 }, { 0xA6, 0x00A6 }, { 0xA7, 0x00A7 }, { 0xA8, 0x0401 }, { 0xA9, 0x00A9 }, { 0xAA, 0x0404 }, { 0xAB, 0x00AB }, { 0xAC, 0x00AC }, { 0xAD, 0x00AD }, { 0xAE, 0x00AE }, { 0xAF, 0x0407 }, { 0xB0, 0x00B0 }, { 0xB1, 0x00B1 }, { 0xB2, 0x0406 }, { 0xB3, 0x0456 }, { 0xB4, 0x0491 }, { 0xB5, 0x00B5 }, { 0xB6, 0x00B6 }, { 0xB7, 0x00B7 }, { 0xB8, 0x0451 }, { 0xB9, 0x2116 }, { 0xBA, 0x0454 }, { 0xBB, 0x00BB }, { 0xBC, 0x0458 }, { 0xBD, 0x0405 }, { 0xBE, 0x0455 }, { 0xBF, 0x0457 } }; // https://code.google.com/p/convert-utf8-to-cp1251/ int convert_utf8_to_windows1251(const char* utf8, char* windows1251, size_t n) { int i = 0; int j = 0; for (; i < (int)n && utf8[i] != 0; ++i) { char prefix = utf8[i]; char suffix = utf8[i + 1]; if ((prefix & 0x80) == 0) { windows1251[j] = (char)prefix; ++j; } else if ((~prefix) & 0x20) { int first5bit = prefix & 0x1F; first5bit <<= 6; int sec6bit = suffix & 0x3F; int unicode_char = first5bit + sec6bit; if (unicode_char >= 0x410 && unicode_char <= 0x44F) { windows1251[j] = (char)(unicode_char - 0x350); } else if (unicode_char >= 0x80 && unicode_char <= 0xFF) { windows1251[j] = (char)(unicode_char); } else if (unicode_char >= 0x402 && unicode_char <= 0x403) { windows1251[j] = (char)(unicode_char - 0x382); } else { int count = sizeof(g_letters) / sizeof(Letter); for (int k = 0; k < count; ++k) { if (unicode_char == g_letters[k].unicode) { windows1251[j] = g_letters[k].win1251; goto NEXT_LETTER; } } // can't convert this char return 0; } NEXT_LETTER: ++i; ++j; } else { // can't convert this chars return 0; } } windows1251[j] = 0; return 1; } int convert_windows1252_to_utf8(char * windows1252) { int i = 0, j = 0; unsigned char c, temp[512]; while (windows1252[i] != 0) { c = windows1252[i]; if (c < 128) { temp[j] = c; j++; } else { temp[j] = (c >> 6) | 192; temp[j + 1] = (c & 63) | 128; j += 2; } i++; } temp[j] = 0; memcpy(windows1252, temp, j + 1); return 0; } #endif } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/i18n.h000066400000000000000000000044471462640461400170240ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_LOCALIZATION_TYPES #define INCLUDED_LOCALIZATION_TYPES namespace pvpgn { namespace bnetd { typedef struct { t_gamelang gamelang; const char* name; std::vector countries; } t_language; extern std::vector languages; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_LOCALIZATION_PROTOS #define INCLUDED_LOCALIZATION_PROTOS #define JUST_NEED_TYPES # include # include "connection.h" # include "common/format.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int i18n_load(void); extern int i18n_reload(void); extern std::string i18n_filename(const char * filename, t_tag gamelang); extern t_language language_find_by_country(const char * code, bool &found); extern t_language language_find_by_tag(t_gamelang gamelang, bool &found); extern t_language language_get_by_country(const char * code); extern t_gamelang gamelang_get_by_country(const char * code); extern t_language language_get_by_tag(t_gamelang gamelang); extern t_gamelang conn_get_gamelang_localized(t_connection * c); extern int handle_language_command(t_connection * c, char const *text); extern char * i18n_convert(t_connection * c, char * buf); extern int tag_check_gamelang_real(t_tag gamelang); extern std::string _localize(t_connection * c, const char * func, const char *fmt, const fmt::ArgList &args); FMT_VARIADIC(std::string, _localize, t_connection *, const char *, const char *) #define localize(c, ...) _localize(c, __FUNCTION__, __VA_ARGS__) } } /*****/ #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/icons.cpp000066400000000000000000000726751462640461400177230ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/token.h" #include "common/list.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/xstring.h" #include "common/util.h" #include "common/tag.h" #include "account.h" #include "connection.h" #include "icons.h" #include "account_wrap.h" #include "message.h" #include "helpfile.h" #include "channel.h" #include "command.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_list * icon_head = NULL; /* use prefs_get_custom_icons() for this option */ static int enable_custom_icons = 0; /* command groups for master users */ static int master_commandgroups = 0; #define MASTER_COMMANDGROUPS_DEFAULT 8; /* default group flags if master_commandgroups is undefined */ static int skip_comments(char *buff); static t_icon_var_info * _read_option(char *str, unsigned lineno); static char * _find_attr_key(char * clienttag); /* Set usericon (command) */ extern int handle_icon_command(t_connection * c, char const *text) { t_account * account; t_connection * user_c; char const * channel_name; char const * subcommand, *username, *iconname; char const *usericon, *iconalias, *iconcode; t_clienttag clienttag; std::string output_icons = ""; int count = 1; // 1 icon in stash ("default" icon) bool is_selected = false; std::string msgtemp; if (!(conn_get_channel(c))) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel.")); return -1; } else { channel_name = channel_get_name(conn_get_channel(c)); } // get current clienttag clienttag = conn_get_clienttag(c); if (!clienttag || clienttag == CLIENTTAG_BNCHATBOT_UINT) { message_send_text(c, message_type_error, c, localize(c, "This command can only be used from the game.")); return -1; } // split command args std::vector args = split_command(text, 3); if (!((master_commandgroups & account_get_command_groups(conn_get_account(c))))) { /* A) Simple command syntax for users */ iconname = args[1].c_str(); // icon code account = conn_get_account(c); // get current user icon if (usericon = account_get_user_icon(account, clienttag)) usericon = strreverse(xstrdup(usericon)); bool is_found = false; // get user stash if (char const * iconstash = account_get_user_iconstash(account, clienttag)) { std::string s(iconstash); std::istringstream iss(s); do { std::string _icon; iss >> _icon; if (_icon.empty()) continue; // output icon_alias instead of icon_code if (iconalias = customicons_stash_find(clienttag, _icon.c_str(), true)) _icon = std::string(iconalias); if (!(iconcode = customicons_stash_find(clienttag, _icon.c_str()))) iconcode = iconalias = _icon.c_str(); // icon was added earlier to user and then removed from server stash // set selected icon in brackets if (usericon && strcasecmp(usericon, iconcode) == 0) { _icon = "[" + _icon + "]"; is_selected = true; } if (!output_icons.empty()) output_icons += ", "; output_icons += _icon; // if iconname parameter passed then find it in user stash if (iconname[0] != '\0') { if (strcasecmp(iconname, iconalias) == 0 || strcasecmp(iconname, iconcode) == 0) is_found = true; } count++; } while (iss); } if (iconname[0] != '\0') { // unset value if (strcasecmp(iconname, "default") == 0) { msgtemp = localize(c, "Set default icon."); usericon = NULL; } // set usericon (reversed) else { // if icon not found in server stash if (!(iconcode = customicons_stash_find(clienttag, iconname))) { // set icon code from args std::transform(args[1].begin(), args[1].end(), args[1].begin(), ::toupper); // to upper iconcode = args[1].c_str(); } if (!is_found || strlen(iconcode) != 4) { message_send_text(c, message_type_error, c, localize(c, "Bad icon.")); return -1; } msgtemp = localize(c, "Set new icon is succeed."); usericon = strreverse((char*)iconcode); } account_set_user_icon(account, clienttag, usericon); message_send_text(c, message_type_info, c, msgtemp); // if user online then force him to rejoin channel if (c) { conn_update_w3_playerinfo(c); channel_rejoin(c); } return 0; } // display icon list in user stash msgtemp = localize(c, "You have {} icons in stash:", count); message_send_text(c, message_type_info, c, msgtemp); output_icons = ((is_selected || usericon) ? "default" : "[default]") + std::string((count > 1) ? ", " : "") + output_icons; msgtemp = " " + output_icons; message_send_text(c, message_type_info, c, msgtemp); return 0; } /* B) Complex command syntax for operator and admins */ if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } subcommand = args[1].c_str(); // sub command username = args[2].c_str(); // username iconname = args[3].c_str(); // icon alias or code // display icons from the server stash // subcommand = list if (subcommand[0] == 'l' && username[0] == '\0') { message_send_text(c, message_type_info, c, localize(c, "Available icons in server stash:")); std::string output_icons = customicons_stash_get_list(clienttag, true); msgtemp = " " + output_icons; message_send_text(c, message_type_info, c, msgtemp); return 0; } // find user account if (!(account = accountlist_find_account(username))) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } user_c = account_get_conn(account); switch (safe_tolower(subcommand[0])) { // set case 's': if (iconname[0] == '\0') { describe_command(c, args[0].c_str()); return -1; } // unset value if (strcasecmp(iconname, "default") == 0) { msgtemp = localize(c, "Set default icon for {}", account_get_name(account)); usericon = NULL; } // set usericon (reversed) else { // find icon in server stash if (!(iconcode = customicons_stash_find(clienttag, iconname))) { message_send_text(c, message_type_error, c, localize(c, "That icon doesn't exist in server stash.")); return -1; } msgtemp = localize(c, "Set new icon is succeed for {}", account_get_name(account)); usericon = strreverse((char*)iconcode); } account_set_user_icon(account, clienttag, usericon); message_send_text(c, message_type_info, c, msgtemp); // if user online then force him to rejoin channel if (user_c) { conn_update_w3_playerinfo(user_c); channel_rejoin(user_c); } return 0; break; // list case 'l': // get current user icon if (usericon = account_get_user_icon(account, clienttag)) usericon = strreverse(xstrdup(usericon)); // get user stash if (char const * iconstash = account_get_user_iconstash(account, clienttag)) { std::string s(iconstash); std::istringstream iss(s); do { std::string _icon; iss >> _icon; if (_icon.empty()) continue; if (!(iconcode = customicons_stash_find(clienttag, _icon.c_str()))) iconcode = _icon.c_str(); // icon was added earlier to user and then removed from server stash // output icon_alias instead of icon_code if (iconalias = customicons_stash_find(clienttag, _icon.c_str(), true)) _icon = std::string(iconalias); // output key instead of code // set selected icon in brackets if (usericon && strcasecmp(usericon, iconcode) == 0) { _icon = "[" + _icon + "]"; is_selected = true; } if (!output_icons.empty()) output_icons += ", "; output_icons += _icon; count++; } while (iss); } // display icon list in user stash msgtemp = localize(c, "{} has {} icons in stash : ", account_get_name(account), count); message_send_text(c, message_type_info, c, msgtemp); output_icons = ((is_selected || usericon) ? "default" : "[default]") + std::string((count > 1) ? ", " : "") + output_icons; msgtemp = " " + output_icons; message_send_text(c, message_type_info, c, msgtemp); return 0; break; // add case 'a': if (iconname[0] == '\0') { describe_command(c, args[0].c_str()); return -1; } // find icon in server stash if (!(iconcode = customicons_stash_find(clienttag, iconname))) { message_send_text(c, message_type_error, c, localize(c, "That icon doesn't exist in server stash.")); return -1; } // find icon in user stash if (char const * iconstash = account_get_user_iconstash(account, clienttag)) { std::string s(iconstash); std::istringstream iss(s); do { std::string _icon; iss >> _icon; if (_icon.empty()) continue; if (!output_icons.empty()) output_icons += " "; output_icons += _icon; if (strcasecmp(_icon.c_str(), iconcode) == 0) { message_send_text(c, message_type_error, c, localize(c, "User already has that icon in stash.")); return -1; } } while (iss); } // append new icon if (!output_icons.empty()) output_icons += " "; output_icons += std::string(iconcode); // save stash account_set_user_iconstash(account, clienttag, output_icons.c_str()); msgtemp = localize(c, "Add new icon to {}'s stash.", account_get_name(account)); message_send_text(c, message_type_info, c, msgtemp); return 0; break; // del case 'd': if (iconname[0] == '\0') { describe_command(c, args[0].c_str()); return -1; } bool is_found = false; if (char const * iconstash = account_get_user_iconstash(account, clienttag)) { // get current user icon if (usericon = account_get_user_icon(account, clienttag)) usericon = strreverse(xstrdup(usericon)); std::string s(iconstash); std::istringstream iss(s); do { std::string _icon; iss >> _icon; if (_icon.empty()) continue; iconalias = customicons_stash_find(clienttag, _icon.c_str(), true); if (!(iconcode = customicons_stash_find(clienttag, _icon.c_str()))) iconcode = iconalias = _icon.c_str(); // icon was added earlier to user and then removed from server stash // exclude deleted icon (allow to delete icon by code or alias) if (strcasecmp(iconname, iconalias) == 0 || strcasecmp(iconname, iconcode) == 0) { // also unset current user icon if it equals with given if (usericon && strcasecmp(usericon, iconcode) == 0) account_set_user_icon(account, clienttag, NULL); is_found = true; continue; } if (!output_icons.empty()) output_icons += " "; output_icons += _icon; } while (iss); } if (!is_found) { message_send_text(c, message_type_error, c, localize(c, "That icon doesn't exist in user stash.")); return -1; } // save stash account_set_user_iconstash(account, clienttag, output_icons.c_str()); msgtemp = localize(c, "Delete icon from {}'s stash.", account_get_name(account)); message_send_text(c, message_type_info, c, msgtemp); return 0; break; } return -1; } /* Search usericon in available list by icon code or alias, for given clienttag * return icon code if found, and NULL if not found * if (return_alias == true) then aias is returned */ extern char const * customicons_stash_find(t_clienttag clienttag, char const * code, bool return_alias) { t_elem * curr; t_elem * curr_var; t_icon_var_info * var; t_iconset_info * iconset; if (!code || !clienttag) return NULL; char clienttag_str[5]; tag_uint_to_str(clienttag_str, clienttag); if (icon_head) { LIST_TRAVERSE(icon_head, curr) { if (!(iconset = (t_iconset_info*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); continue; } // find a needed tag if (std::strcmp(iconset->clienttag, clienttag_str) != 0) continue; LIST_TRAVERSE(iconset->iconstash, curr_var) { if (!(var = (t_icon_var_info*)elem_get_data(curr_var))) { eventlog(eventlog_level_error, __FUNCTION__, "vars list contains NULL item"); continue; } if (strcasecmp(var->key, code) == 0 || strcasecmp(var->value, code) == 0) { char const * val = (return_alias) ? xstrdup(var->key) : xstrdup(var->value); return val; } } } } return NULL; } /* Return comma delimeted icons from stash */ extern std::string customicons_stash_get_list(t_clienttag clienttag, bool return_alias) { t_elem * curr; t_elem * curr_var; t_icon_var_info * var; t_iconset_info * iconset; std::string output = ""; if (!clienttag) return output; char clienttag_str[5]; tag_uint_to_str(clienttag_str, clienttag); if (icon_head) { LIST_TRAVERSE(icon_head, curr) { if (!(iconset = (t_iconset_info*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); continue; } // find a needed tag if (std::strcmp(iconset->clienttag, clienttag_str) != 0) continue; LIST_TRAVERSE(iconset->iconstash, curr_var) { if (!(var = (t_icon_var_info*)elem_get_data(curr_var))) { eventlog(eventlog_level_error, __FUNCTION__, "vars list contains NULL item"); continue; } std::string val = (return_alias) ? var->key: var->value; if (!output.empty()) output += ", "; output += val; } break; } } return output; } extern int prefs_get_custom_icons() { return enable_custom_icons; } extern bool customicons_allowed_by_client(t_clienttag clienttag) { switch (clienttag) { // Starcraft variations case CLIENTTAG_STARCRAFT_UINT: case CLIENTTAG_BROODWARS_UINT: case CLIENTTAG_SHAREWARE_UINT: case CLIENTTAG_STARJAPAN_UINT: // Diablo case CLIENTTAG_DIABLORTL_UINT: case CLIENTTAG_DIABLOSHR_UINT: // Warcraft 2 case CLIENTTAG_WARCIIBNE_UINT: // Warcraft 3 case CLIENTTAG_WARCRAFT3_UINT: case CLIENTTAG_WAR3XP_UINT: return true; default: break; } return false; } /* Format stats text, with attributes from a storage, and output text to a user */ extern const char * customicons_get_stats_text(t_account * account, t_clienttag clienttag) { const char *value; const char *text; char clienttag_str[5], tmp[64]; t_icon_info * icon; t_iconset_info * iconset; t_icon_var_info * var; t_elem * curr; t_elem * curr_var; tag_uint_to_str(clienttag_str, clienttag); text = NULL; if (icon_head) { LIST_TRAVERSE(icon_head, curr) { if (!(iconset = (t_iconset_info*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); continue; } // find a needed tag if (std::strcmp(iconset->clienttag, clienttag_str) != 0) continue; if (!iconset->stats) return NULL; text = xstrdup(iconset->stats); LIST_TRAVERSE(iconset->vars, curr_var) { if (!(var = (t_icon_var_info*)elem_get_data(curr_var))) { eventlog(eventlog_level_error, __FUNCTION__, "vars list contains NULL item"); continue; } // replace NULL attributes to "0" if (!(value = account_get_strattr(account, var->value))) value = "0"; // replace in a text std::snprintf(tmp, sizeof(tmp), "{{%s}}", var->key); text = str_replace((char*)text, tmp, (char*)value); // also replace {var}->rank if (icon = customicons_get_icon_by_rating(atoi(value), clienttag_str)) { std::snprintf(tmp, sizeof(tmp), "{{%s->rank}}", var->key); text = str_replace((char*)text, tmp, icon->rank); } } } } return text; } /* find icon code by rating for the clienttag */ extern t_icon_info * customicons_get_icon_by_account(t_account * account, t_clienttag clienttag) { char * attr_key; int rating; char clienttag_str[5]; t_icon_info * icon; tag_uint_to_str(clienttag_str, clienttag); // get attribute field name from a storage if (!(attr_key = _find_attr_key((char*)clienttag_str))) { eventlog(eventlog_level_trace, __FUNCTION__, "no custom iconset defined for client tag {}", clienttag_str); return NULL; } rating = account_get_numattr(account, attr_key); icon = customicons_get_icon_by_rating(rating, clienttag_str); return icon; } extern int customicons_unload(void) { t_elem * curr; t_iconset_info * iconset; if (icon_head) { LIST_TRAVERSE(icon_head, curr) { if (!(iconset = (t_iconset_info*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); continue; } if (list_remove_elem(icon_head, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); xfree(iconset->clienttag); if (iconset->attr_key) xfree(iconset->attr_key); if (iconset->stats) xfree((void*)iconset->stats); if (iconset->icon_info) xfree(iconset->icon_info);//need to free ->rank and ->icon_code too somehow if (iconset->iconstash) xfree(iconset->iconstash);//need to free ->key and ->value too somehow if (iconset->vars) xfree(iconset->vars);//need to free ->key and ->value too somehow xfree(iconset); } if (list_destroy(icon_head) < 0) return -1; icon_head = NULL; } return 0; } /*****/ extern int customicons_load(char const * filename) { std::FILE * fp; unsigned int line, pos, counter = 0; bool end_of_iconset = false; char *buff, *value; char *rating, *rank, *icon; t_icon_var_info * option; icon_head = list_create(); unsigned int fpos = 0; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } /* 1) parse root config options */ for (line = 1; (buff = file_get_line(fp)); line++) { if (skip_comments(buff) > 0) { continue; } // read root options if (option = _read_option(buff, line)) { if (std::strcmp(option->key, "custom_icons") == 0) if (std::strcmp(option->value, "true") == 0) enable_custom_icons = 1; else enable_custom_icons = 0; if (std::strcmp(option->key, "master_commandgroups") == 0) { size_t option_valuelen = std::strlen(option->value); // convert string groups from config to integer for (std::size_t i = 0; i < option_valuelen; i++) { if (option->value[i] == '1') master_commandgroups |= 1; else if (option->value[i] == '2') master_commandgroups |= 2; else if (option->value[i] == '3') master_commandgroups |= 4; else if (option->value[i] == '4') master_commandgroups |= 8; else if (option->value[i] == '5') master_commandgroups |= 16; else if (option->value[i] == '6') master_commandgroups |= 32; else if (option->value[i] == '7') master_commandgroups |= 64; else if (option->value[i] == '8') master_commandgroups |= 128; } } // set default value if not defined if (master_commandgroups == 0) master_commandgroups = MASTER_COMMANDGROUPS_DEFAULT; xfree(option->key); xfree(option->value); xfree(option); } /* 2) parse clienttags */ if (std::strcmp(buff, "[W3XP]") == 0 || std::strcmp(buff, "[WAR3]") == 0 || std::strcmp(buff, "[STAR]") == 0 || std::strcmp(buff, "[SEXP]") == 0 || std::strcmp(buff, "[JSTR]") == 0 || std::strcmp(buff, "[SSHR]") == 0 || std::strcmp(buff, "[W2BN]") == 0 || std::strcmp(buff, "[DRTL]") == 0 || std::strcmp(buff, "[DSHR]") == 0) { if (skip_comments(buff) > 0) { continue; } value = std::strtok(buff, " []"); // extract clienttag // new iconset for a clienttag t_iconset_info * icon_set = (t_iconset_info*)xmalloc(sizeof(t_iconset_info)); icon_set->clienttag = xstrdup(value); icon_set->attr_key = NULL; icon_set->icon_info = list_create(); icon_set->iconstash = list_create(); icon_set->vars = list_create(); icon_set->stats = NULL; /* 3) parse inner options under a clienttag */ for (; (buff = file_get_line(fp)); line++) { if (std::strcmp(buff, "[W3XP]") == 0 || std::strcmp(buff, "[WAR3]") == 0 || std::strcmp(buff, "[STAR]") == 0 || std::strcmp(buff, "[SEXP]") == 0 || std::strcmp(buff, "[JSTR]") == 0 || std::strcmp(buff, "[SSHR]") == 0 || std::strcmp(buff, "[W2BN]") == 0 || std::strcmp(buff, "[DRTL]") == 0 || std::strcmp(buff, "[DSHR]") == 0) { // return position in stream fseek(fp, fpos, SEEK_SET); break; } if (skip_comments(buff) > 0) { continue; } // fill variables list if (option = _read_option(buff, line)) { // set attr_key with a first variable if (!icon_set->attr_key) icon_set->attr_key = xstrdup(option->value); // add to variables list_append_data(icon_set->vars, option); continue; } /* 3) parse icons section */ if (std::strcmp(buff, "[icons]") == 0) { counter = 0; for (; (buff = file_get_line(fp)); line++) { if (skip_comments(buff) > 0) { continue; } // end if icons if (std::strcmp(buff, "[/icons]") == 0) { break; } pos = 0; if (!(rating = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing value in line {} in file \"{}\"", line, filename); continue; } if (!(rank = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing rank in line {} in file \"{}\"", line, filename); continue; } if (!(icon = next_token(buff, &pos))) { eventlog(eventlog_level_error, __FUNCTION__, "missing icon in line {} in file \"{}\"", line, filename); continue; } counter++; t_icon_info * icon_info = (t_icon_info*)xmalloc(sizeof(t_icon_info)); icon_info->rating = atoi(rating); icon_info->rank = xstrdup(rank); icon_info->icon_code = xstrdup(strreverse(icon)); // save reversed icon code list_prepend_data(icon_set->icon_info, icon_info); } } /* 3) parse stats section */ if (std::strcmp(buff, "[stats]") == 0) { std::string tmp; for (; (buff = file_get_line(fp)); line++) { // end of stats if (std::strcmp(buff, "[/stats]") == 0) { // put whole text of stats after read icon_set->stats = xstrdup(tmp.c_str()); break; } tmp = tmp + buff + "\n"; } } /* 4) parse usericons section */ if (std::strcmp(buff, "[iconstash]") == 0) { char *key, *value; counter = 0; for (; (buff = file_get_line(fp)); line++) { if (skip_comments(buff) > 0) { continue; } // end if usericons if (std::strcmp(buff, "[/iconstash]") == 0) { break; } pos = 0; if (!(key = next_token(buff, &pos))) continue; if (!(value = next_token(buff, &pos))) value = key; counter++; t_icon_var_info * icon_item = (t_icon_var_info*)xmalloc(sizeof(t_icon_var_info)); icon_item->key = xstrdup(key); icon_item->value = xstrdup(value); list_append_data(icon_set->iconstash, icon_item); } } // remember file position fpos = ftell(fp); } if (!icon_set->attr_key) { eventlog(eventlog_level_error, __FUNCTION__, "attr_key is null for iconset {}", icon_set->clienttag); continue; } list_append_data(icon_head, icon_set); eventlog(eventlog_level_trace, __FUNCTION__, "loaded {} custom icons for {}", counter, icon_set->clienttag); } } std::fclose(fp); return 0; } static int skip_comments(char *buff) { char *temp; int pos; for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { return 1; } if ((temp = std::strrchr(buff, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } return 0; } /* Read an option like "key = value" from a str line, and split in into a pair: key, value */ static t_icon_var_info * _read_option(char *str, unsigned lineno) { char *cp, prev, *directive; t_icon_var_info * icon_var = (t_icon_var_info*)xmalloc(sizeof(t_icon_var_info)); directive = str; str = str_skip_word(str + 1); if (*str) *(str++) = '\0'; str = str_skip_space(str); if (*str != '=') { xfree((void*)icon_var); return NULL; } str = str_skip_space(str + 1); if (!*str) { eventlog(eventlog_level_error, __FUNCTION__, "missing value at line {}", lineno); xfree((void*)icon_var); return NULL; } if (*str == '"') { for (cp = ++str, prev = '\0'; *cp; cp++) { switch (*cp) { case '"': if (prev != '\\') break; prev = '"'; continue; case '\\': if (prev == '\\') prev = '\0'; else prev = '\\'; continue; default: prev = *cp; continue; } break; } if (*cp != '"') { eventlog(eventlog_level_error, __FUNCTION__, "missing end quota at line {}", lineno); xfree((void*)icon_var); return NULL; } *cp = '\0'; cp = str_skip_space(cp + 1); if (*cp) { eventlog(eventlog_level_error, __FUNCTION__, "extra characters in value after ending quote at line {}", lineno); xfree((void*)icon_var); return NULL; } } else { cp = str_skip_word(str); if (*cp) { *cp = '\0'; cp = str_skip_space(cp + 1); if (*cp) { eventlog(eventlog_level_error, __FUNCTION__, "extra characters after the value at line {}", lineno); xfree((void*)icon_var); return NULL; } } } //if (!find && std::strcmp(find, directive) == 0) // return str_skip_space(str); //else // return NULL; icon_var->key = xstrdup(directive); icon_var->value = xstrdup(str_skip_space(str)); return icon_var; } /* Get custom icon by rating for clienttag */ extern t_icon_info * customicons_get_icon_by_rating(int rating, char * clienttag) { t_elem * curr; t_elem * curr_icon; t_iconset_info * iconset; t_icon_info * icon = NULL; if (!clienttag) return NULL; if (icon_head) { LIST_TRAVERSE(icon_head, curr) { if (!(iconset = (t_iconset_info*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); continue; } // find a needed tag if (std::strcmp(iconset->clienttag, clienttag) != 0) continue; // iterate all icons for the tag LIST_TRAVERSE(iconset->icon_info, curr_icon) { if (!(icon = (t_icon_info*)elem_get_data(curr_icon))) { eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); continue; } if (rating >= icon->rating) return icon; } // if nothing found then return last icon return icon; } } return NULL; } /* Get attr_key for clienttag */ static char * _find_attr_key(char * clienttag) { t_elem * curr; t_iconset_info * iconset; if (icon_head) { LIST_TRAVERSE(icon_head, curr) { if (!(iconset = (t_iconset_info*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "icon list contains NULL item"); continue; } // find a needed tag if (std::strcmp(iconset->clienttag, clienttag) == 0) return iconset->attr_key; } } return NULL; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/icons.h000066400000000000000000000044641462640461400173570ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ICONS_TYPES #define INCLUDED_ICONS_TYPES namespace pvpgn { namespace bnetd { typedef struct { unsigned int rating; // rating or something else char * rank; // any string value char * icon_code; // icon code } t_icon_info; typedef struct { char * clienttag; char * attr_key; t_list * icon_info; // list of t_icon_info t_list * iconstash; // list of t_icon_var_info t_list * vars; // list of t_icon_var_info const char * stats; } t_iconset_info; typedef struct { char * key; char * value; } t_icon_var_info; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ICONS_PROTOS #define INCLUDED_ICONS_PROTOS #define JUST_NEED_TYPES # include "account.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int handle_icon_command(t_connection * c, char const *text); extern char const * customicons_stash_find(t_clienttag clienttag, char const * code, bool return_alias = false); extern std::string customicons_stash_get_list(t_clienttag clienttag, bool return_alias = false); extern int prefs_get_custom_icons(); extern bool customicons_allowed_by_client(t_clienttag clienttag); extern t_icon_info * customicons_get_icon_by_account(t_account * account, t_clienttag clienttag); extern const char * customicons_get_stats_text(t_account * account, t_clienttag clienttag); extern t_icon_info * customicons_get_icon_by_rating(int rating, char * clienttag); extern int customicons_load(char const * filename); extern int customicons_unload(void); } } /*****/ #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/ipban.cpp000066400000000000000000000676341462640461400177000ustar00rootroot00000000000000/* * Copyright (C) 2000 Gediminas (gugini@fortas.ktu.lt) * Copyright (C) 2002 Bartomiej Butyn (bartek@milc.com.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define IPBAN_INTERNAL_ACCESS #include "ipban.h" #include #include #include #include #include #include #include #include #include "compat/strsep.h" #include "compat/strcasecmp.h" #include "common/list.h" #include "common/util.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/field_sizes.h" #include "common/xstring.h" #include "message.h" #include "server.h" #include "prefs.h" #include "connection.h" #include "helpfile.h" #include "command.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static int identify_ipban_function(const char * funcstr); static int ipban_func_del(t_connection * c, char const * cp); static int ipban_func_list(t_connection * c); static int ipban_func_check(t_connection * c, char const * cp); static int ipban_unload_entry(t_ipban_entry * e); static int ipban_identical_entry(t_ipban_entry * e1, t_ipban_entry * e2); static t_ipban_entry * ipban_str_to_ipban_entry(char const * cp); static char * ipban_entry_to_str(t_ipban_entry const * entry); static unsigned long ipban_str_to_ulong(char const * ipaddr); static int ipban_could_be_exact_ip_str(char const * str); static int ipban_could_be_ip_str(char const * ipstr); static void ipban_usage(t_connection * c); static t_list * ipbanlist_head = NULL; static std::time_t lastchecktime = 0; extern int ipbanlist_create(void) { ipbanlist_head = list_create(); return 0; } extern int ipbanlist_destroy(void) { t_elem * curr; t_ipban_entry * entry; if (ipbanlist_head) { LIST_TRAVERSE(ipbanlist_head, curr) { entry = (t_ipban_entry*)elem_get_data(curr); if (!entry) /* should not happen */ { eventlog(eventlog_level_error, __FUNCTION__, "ipbanlist contains NULL item"); continue; } if (list_remove_elem(ipbanlist_head, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); ipban_unload_entry(entry); } if (list_destroy(ipbanlist_head) < 0) return -1; ipbanlist_head = NULL; } return 0; } extern int ipbanlist_load(char const * filename) { std::FILE * fp; char * buff; char * ip; char * timestr; unsigned int currline; unsigned int endtime; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open banlist file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } for (currline = 1; (buff = file_get_line(fp)); currline++) { ip = buff; /* eat whitespace in front */ while (*ip == '\t' || *ip == ' ') ip++; if (*ip == '\0' || *ip == '#') { continue; } /* eat whitespace in back */ while (ip[std::strlen(ip) - 1] == ' ' || ip[std::strlen(ip) - 1] == '\t') ip[std::strlen(ip) - 1] = '\0'; if (std::strchr(ip, ' ') || std::strchr(ip, '\t')) { timestr = ip; while (*timestr != ' ' && *timestr != '\t') timestr++; *timestr = '\0'; timestr++; while (*timestr == ' ' || *timestr == '\t') timestr++; } else timestr = NULL; if (!timestr) endtime = 0; else if (clockstr_to_seconds(timestr, &endtime) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not convert to seconds. Banning pernamently."); endtime = 0; } if (ipbanlist_add(NULL, ip, endtime) != 0) { eventlog(eventlog_level_warn, __FUNCTION__, "error in {} at line {}", filename, currline); continue; } } file_get_line(NULL); // clear file_get_line buffer if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close banlist file \"{}\" after reading (std::fclose: {})", filename, std::strerror(errno)); return 0; } extern int ipbanlist_save(char const * filename) { t_elem const * curr; t_ipban_entry * entry; std::FILE * fp; char * ipstr; char line[1024]; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "w"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open banlist file \"{}\" for writing (std::fopen: {})", filename, std::strerror(errno)); return -1; } /* if (ftruncate(fp,0)<0) { eventlog(eventlog_level_error,__FUNCTION__,"could not truncate banlist file \"{}\" (ftruncate: {})",filename,std::strerror(errno)); return -1; }*/ LIST_TRAVERSE_CONST(ipbanlist_head, curr) { entry = (t_ipban_entry*)elem_get_data(curr); if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "ipbanlist contains NULL element"); continue; } if (!(ipstr = ipban_entry_to_str(entry))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL ipstr"); continue; } if (entry->endtime == 0) std::snprintf(line, sizeof(line), "%s\n", ipstr); else std::sprintf(line, "%s %" PRId64 "\n", ipstr, static_cast(entry->endtime)); if (!(std::fwrite(line, std::strlen(line), 1, fp))) eventlog(eventlog_level_error, __FUNCTION__, "could not write to banlist file (write: {})", std::strerror(errno)); xfree(ipstr); } if (std::fclose(fp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close banlist file \"{}\" after writing (std::fclose: {})", filename, std::strerror(errno)); return -1; } return 0; } extern int ipbanlist_check(char const * ipaddr) { t_elem const * curr; t_ipban_entry * entry; char * whole; char const * ip1; char const * ip2; char const * ip3; char const * ip4; int counter; if (!ipaddr) { eventlog(eventlog_level_warn, __FUNCTION__, "got NULL ipaddr"); return -1; } whole = xstrdup(ipaddr); eventlog(eventlog_level_debug, __FUNCTION__, "lastcheck: {}, now: {}, now-lc: {}.", (unsigned)lastchecktime, (unsigned)now, (unsigned)(now - lastchecktime)); if (now - lastchecktime >= (signed)prefs_get_ipban_check_int()) /* unsigned; no need to check prefs < 0 */ { ipbanlist_unload_expired(); lastchecktime = now; } ip1 = std::strtok(whole, "."); ip2 = std::strtok(NULL, "."); ip3 = std::strtok(NULL, "."); ip4 = std::strtok(NULL, "."); if (!ip1 || !ip2 || !ip3 || !ip4) { eventlog(eventlog_level_warn, __FUNCTION__, "got bad IP address \"{}\"", ipaddr); xfree(whole); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "checking {}.{}.{}.{}", ip1, ip2, ip3, ip4); counter = 0; LIST_TRAVERSE_CONST(ipbanlist_head, curr) { entry = (t_ipban_entry*)elem_get_data(curr); if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "ipbanlist contains NULL item"); xfree(whole); return -1; } counter++; switch (entry->type) { case ipban_type_exact: if (std::strcmp(entry->info1, ipaddr) == 0) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} matched exact {}", ipaddr, entry->info1); xfree(whole); return counter; } eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match exact {}", ipaddr, entry->info1); continue; case ipban_type_wildcard: if (std::strcmp(entry->info1, "*") != 0 && std::strcmp(ip1, entry->info1) != 0) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match part 1 of wildcard {}.{}.{}.{}", ipaddr, entry->info1, entry->info2, entry->info3, entry->info4); continue; } if (std::strcmp(entry->info2, "*") != 0 && std::strcmp(ip2, entry->info2) != 0) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match part 2 of wildcard {}.{}.{}.{}", ipaddr, entry->info1, entry->info2, entry->info3, entry->info4); continue; } if (std::strcmp(entry->info3, "*") != 0 && std::strcmp(ip3, entry->info3) != 0) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match part 3 of wildcard {}.{}.{}.{}", ipaddr, entry->info1, entry->info2, entry->info3, entry->info4); continue; } if (std::strcmp(entry->info4, "*") != 0 && std::strcmp(ip4, entry->info4) != 0) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match part 4 of wildcard {}.{}.{}.{}", ipaddr, entry->info1, entry->info2, entry->info3, entry->info4); continue; } eventlog(eventlog_level_debug, __FUNCTION__, "address {} matched wildcard {}.{}.{}.{}", ipaddr, entry->info1, entry->info2, entry->info3, entry->info4); xfree(whole); return counter; case ipban_type_range: if ((ipban_str_to_ulong(ipaddr) >= ipban_str_to_ulong(entry->info1)) && (ipban_str_to_ulong(ipaddr) <= ipban_str_to_ulong(entry->info2))) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} matched range {}-{}", ipaddr, entry->info1, entry->info2); xfree(whole); return counter; } eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match range {}-{}", ipaddr, entry->info1, entry->info2); continue; case ipban_type_netmask: { unsigned long lip1; unsigned long lip2; unsigned long netmask; if (!(lip1 = ipban_str_to_ulong(ipaddr))) { xfree(whole); return -1; } if (!(lip2 = ipban_str_to_ulong(entry->info1))) { xfree(whole); return -1; } if (!(netmask = ipban_str_to_ulong(entry->info2))) { xfree(whole); return -1; } lip1 = lip1 & netmask; lip2 = lip2 & netmask; if (lip1 == lip2) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} matched netmask {}/{}", ipaddr, entry->info1, entry->info2); xfree(whole); return counter; } eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match netmask {}/{}", ipaddr, entry->info1, entry->info2); continue; } case ipban_type_prefix: { unsigned long lip1; unsigned long lip2; int prefix; if (!(lip1 = ipban_str_to_ulong(ipaddr))) { xfree(whole); return -1; } if (!(lip2 = ipban_str_to_ulong(entry->info1))) { xfree(whole); return -1; } prefix = std::atoi(entry->info2); lip1 = lip1 >> (32 - prefix); lip2 = lip2 >> (32 - prefix); if (lip1 == lip2) { eventlog(eventlog_level_debug, __FUNCTION__, "address {} matched prefix {}/{}", ipaddr, entry->info1, entry->info2); xfree(whole); return counter; } eventlog(eventlog_level_debug, __FUNCTION__, "address {} does not match prefix {}/{}", ipaddr, entry->info1, entry->info2); continue; } default: /* unknown type */ eventlog(eventlog_level_warn, __FUNCTION__, "found bad ban type {}", (int)entry->type); } } xfree(whole); return 0; } extern int ipbanlist_add(t_connection * c, char const * cp, std::time_t endtime) { t_ipban_entry * entry; std::string msgtemp; if (!(entry = ipban_str_to_ipban_entry(cp))) { if (c) message_send_text(c, message_type_error, c, localize(c, "Bad IP.")); eventlog(eventlog_level_error, __FUNCTION__, "could not convert to t_ipban_entry: \"{}\"", cp); return -1; } entry->endtime = endtime; list_append_data(ipbanlist_head, entry); if (c) { if (endtime == 0) { msgtemp = localize(c, "{} banned permamently by {}.", cp, conn_get_username(c)); eventlog(eventlog_level_info, __FUNCTION__, "{}", msgtemp.c_str()); message_send_admins(c, message_type_info, msgtemp.c_str()); msgtemp = localize(c, "{} banned permamently.", cp); message_send_text(c, message_type_info, c, msgtemp); } else { msgtemp = localize(c, "{} banned for {} by {}.", cp, seconds_to_timestr(entry->endtime - now), conn_get_username(c)); eventlog(eventlog_level_info, __FUNCTION__, "{}", msgtemp.c_str()); message_send_admins(c, message_type_info, msgtemp.c_str()); msgtemp = localize(c, "{} banned for {}.", cp, seconds_to_timestr(entry->endtime - now)); message_send_text(c, message_type_info, c, msgtemp); } } return 0; } extern int ipbanlist_unload_expired(void) { t_elem * curr; t_ipban_entry * entry; char removed; removed = 0; LIST_TRAVERSE(ipbanlist_head, curr) { entry = (t_ipban_entry*)elem_get_data(curr); if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "ipbanlist_contains NULL element"); return -1; } if ((entry->endtime - now <= 0) && (entry->endtime != 0)) { eventlog(eventlog_level_debug, __FUNCTION__, "removing item: {}", entry->info1); removed = 1; if (list_remove_elem(ipbanlist_head, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item"); else ipban_unload_entry(entry); } } if (removed == 1) ipbanlist_save(prefs_get_ipbanfile()); return 0; } extern std::time_t ipbanlist_str_to_time_t(t_connection * c, char const * timestr) { unsigned int bmin; char minstr[MAX_TIME_STR]; unsigned int i; std::string msgtemp; for (i = 0; std::isdigit((int)timestr[i]) && i < sizeof(minstr)-1; i++) minstr[i] = timestr[i]; minstr[i] = '\0'; if (timestr[i] != '\0') { if (c) { msgtemp = "There was an error in std::time."; //if (std::strlen(minstr) < 1) // message_send_text(c, message_type_info, c, localize(c, msgtemp)); //else { msgtemp += " "; msgtemp += localize(c, "Banning only for: {} minutes.", minstr); message_send_text(c, message_type_info, c, msgtemp); } } } if (clockstr_to_seconds(minstr, &bmin) < 0) /* it thinks these are seconds but we treat them as minutes */ { eventlog(eventlog_level_error, __FUNCTION__, "could not convert to minutes: \"{}\"", timestr); return -1; } if (bmin == 0) return 0; else { return now + bmin * 60; } } extern int handle_ipban_command(t_connection * c, char const * text) { char const *subcommand, *ipstr, *time; int result = -1; std::vector args = split_command(text, 3); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } subcommand = args[1].c_str(); // subcommand ipstr = args[2].c_str(); // ip address time = args[3].c_str(); // username switch (identify_ipban_function(subcommand)) { case IPBAN_FUNC_ADD: result = ipbanlist_add(c, ipstr, ipbanlist_str_to_time_t(c, time)); result = ipbanlist_save(prefs_get_ipbanfile()); break; case IPBAN_FUNC_DEL: result = ipban_func_del(c, ipstr); result = ipbanlist_save(prefs_get_ipbanfile()); break; case IPBAN_FUNC_LIST: result = ipban_func_list(c); break; case IPBAN_FUNC_CHECK: result = ipban_func_check(c, ipstr); break; default: describe_command(c, args[0].c_str()); } return result; } static int identify_ipban_function(const char * funcstr) { if (strcasecmp(funcstr, "add") == 0 || strcasecmp(funcstr, "a") == 0) return IPBAN_FUNC_ADD; if (strcasecmp(funcstr, "del") == 0 || strcasecmp(funcstr, "d") == 0) return IPBAN_FUNC_DEL; if (strcasecmp(funcstr, "list") == 0 || strcasecmp(funcstr, "l") == 0 || std::strcmp(funcstr, "") == 0) return IPBAN_FUNC_LIST; if (strcasecmp(funcstr, "check") == 0 || strcasecmp(funcstr, "c") == 0) return IPBAN_FUNC_CHECK; return IPBAN_FUNC_UNKNOWN; } static int ipban_func_del(t_connection * c, char const * cp) { t_ipban_entry * to_delete; unsigned int to_delete_nmbr; t_ipban_entry * entry; t_elem * curr; unsigned int counter; char tstr[MAX_MESSAGE_LEN]; counter = 0; if (std::strchr(cp, '.') || std::strchr(cp, '/') || std::strchr(cp, '*')) { if (!(to_delete = ipban_str_to_ipban_entry(cp))) { message_send_text(c, message_type_error, c, localize(c, "Illegal IP entry.")); return -1; } LIST_TRAVERSE(ipbanlist_head, curr) { entry = (t_ipban_entry*)elem_get_data(curr); if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "ipbanlist contains NULL item"); ipban_unload_entry(to_delete); return -1; } if (ipban_identical_entry(to_delete, entry)) { counter++; if (list_remove_elem(ipbanlist_head, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item"); else ipban_unload_entry(entry); } } ipban_unload_entry(to_delete); if (counter == 0) { message_send_text(c, message_type_error, c, localize(c, "No matching entry.")); return -1; } else { if (counter == 1) std::sprintf(tstr, "Entry deleted."); else std::sprintf(tstr, "Deleted %u entries.", counter); message_send_text(c, message_type_info, c, tstr); return 0; } } to_delete_nmbr = std::atoi(cp); if (to_delete_nmbr <= 0) { message_send_text(c, message_type_error, c, localize(c, "Wrong entry number.")); return -1; } LIST_TRAVERSE(ipbanlist_head, curr) { if (to_delete_nmbr == ++counter) { entry = (t_ipban_entry*)elem_get_data(curr); if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "ipbanlist contains NULL item"); return -1; } if (list_remove_elem(ipbanlist_head, &curr)<0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item"); else { ipban_unload_entry(entry); message_send_text(c, message_type_info, c, localize(c, "Entry deleted.")); } } } if (to_delete_nmbr > counter) { std::sprintf(tstr, "There are only %u entries.", counter); message_send_text(c, message_type_error, c, tstr); return -1; } return 0; } static int ipban_func_list(t_connection * c) { t_elem const * curr; t_ipban_entry * entry; char tstr[MAX_MESSAGE_LEN]; unsigned int counter; char timestr[50]; char * ipstr; counter = 0; message_send_text(c, message_type_info, c, localize(c, "Banned IPs:")); LIST_TRAVERSE_CONST(ipbanlist_head, curr) { entry = (t_ipban_entry*)elem_get_data(curr); if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "ipbanlist contains NULL item"); return -1; } counter++; if (entry->endtime == 0) std::sprintf(timestr, "%s", localize(c, "(perm)").c_str()); else std::sprintf(timestr, "(%.48s)", seconds_to_timestr(entry->endtime - now)); if (!(ipstr = ipban_entry_to_str(entry))) { eventlog(eventlog_level_error, __FUNCTION__, "could not convert entry to string"); continue; } std::sprintf(tstr, "%u: %s %s", counter, ipstr, timestr); message_send_text(c, message_type_info, c, tstr); xfree(ipstr); } if (counter == 0) message_send_text(c, message_type_info, c, localize(c, "none")); return 0; } static int ipban_func_check(t_connection * c, char const * cp) { int res; std::string msgtemp; res = ipbanlist_check(cp); switch (res) { case 0: message_send_text(c, message_type_info, c, localize(c, "IP not banned.")); break; case -1: message_send_text(c, message_type_error, c, localize(c, "Error occured.")); break; default: msgtemp = localize(c, "IP banned by rule #{}.", res); message_send_text(c, message_type_info, c, msgtemp); } return 0; } static int ipban_identical_entry(t_ipban_entry * e1, t_ipban_entry * e2) { if (e1->type != e2->type) return 0; switch (e2->type) { case ipban_type_exact: if (std::strcmp(e1->info1, e2->info1) == 0) return 1; break; case ipban_type_range: if (std::strcmp(e1->info1, e2->info1) == 0 && std::strcmp(e1->info2, e2->info2) == 0) return 1; break; case ipban_type_wildcard: if (std::strcmp(e1->info1, e2->info1) == 0 && std::strcmp(e1->info2, e2->info2) == 0 && std::strcmp(e1->info3, e2->info3) == 0 && std::strcmp(e1->info4, e2->info4) == 0) return 1; break; case ipban_type_netmask: if (std::strcmp(e1->info1, e2->info1) == 0 && std::strcmp(e1->info2, e2->info2) == 0) return 1; break; case ipban_type_prefix: if (std::strcmp(e1->info1, e2->info1) == 0 && std::strcmp(e1->info2, e2->info2) == 0) return 1; break; default: /* unknown type */ eventlog(eventlog_level_warn, __FUNCTION__, "found bad ban type {}", (int)e2->type); } return 0; } static int ipban_unload_entry(t_ipban_entry * e) { switch (e->type) { case ipban_type_exact: case ipban_type_wildcard: if (e->info1) xfree(e->info1); break; case ipban_type_range: case ipban_type_netmask: case ipban_type_prefix: if (e->info1) xfree(e->info1); if (e->info2) xfree(e->info2); break; default: /* unknown type */ eventlog(eventlog_level_warn, __FUNCTION__, "found bad ban type {}", (int)e->type); return -1; } xfree(e); return 0; } static t_ipban_entry * ipban_str_to_ipban_entry(char const * ipstr) { char * matched; char * whole; char * cp; t_ipban_entry * entry; entry = (t_ipban_entry*)xmalloc(sizeof(t_ipban_entry)); if (!ipstr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL IP"); xfree(entry); return NULL; } if (ipstr[0] == '\0') { eventlog(eventlog_level_warn, __FUNCTION__, "got empty IP string"); xfree(entry); return NULL; } cp = xstrdup(ipstr); if (ipban_could_be_ip_str(cp) == 0) { eventlog(eventlog_level_debug, __FUNCTION__, "string: \"{}\" can not be valid IP", cp); xfree(entry); return NULL; } if ((matched = std::strchr(cp, '-'))) /* range */ { entry->type = ipban_type_range; eventlog(eventlog_level_debug, __FUNCTION__, "entry: {} matched as ipban_type_range", cp); matched[0] = '\0'; entry->info1 = xstrdup(cp); /* start of range */ entry->info2 = xstrdup(&matched[1]); /* end of range */ entry->info3 = NULL; /* clear unused elements so debugging is nicer */ entry->info4 = NULL; } else if (std::strchr(cp, '*')) /* wildcard */ { entry->type = ipban_type_wildcard; eventlog(eventlog_level_debug, __FUNCTION__, "entry: {} matched as ipban_type_wildcard", cp); /* only xfree() info1! */ whole = xstrdup(cp); entry->info1 = std::strtok(whole, "."); entry->info2 = std::strtok(NULL, "."); entry->info3 = std::strtok(NULL, "."); entry->info4 = std::strtok(NULL, "."); if (!entry->info4) /* not enough dots */ { eventlog(eventlog_level_error, __FUNCTION__, "wildcard entry \"{}\" does not contain all four octets", cp); xfree(entry->info1); xfree(entry); xfree(cp); return NULL; } } else if ((matched = std::strchr(cp, '/'))) /* netmask or prefix */ { if (std::strchr(&matched[1], '.')) { entry->type = ipban_type_netmask; eventlog(eventlog_level_debug, __FUNCTION__, "entry: {} matched as ipban_type_netmask", cp); } else { entry->type = ipban_type_prefix; eventlog(eventlog_level_debug, __FUNCTION__, "entry: {} matched as ipban_type_prefix", cp); } matched[0] = '\0'; entry->info1 = xstrdup(cp); entry->info2 = xstrdup(&matched[1]); entry->info3 = NULL; /* clear unused elements so debugging is nicer */ entry->info4 = NULL; } else /* exact */ { entry->type = ipban_type_exact; eventlog(eventlog_level_debug, __FUNCTION__, "entry: {} matched as ipban_type_exact", cp); entry->info1 = xstrdup(cp); entry->info2 = NULL; /* clear unused elements so debugging is nicer */ entry->info3 = NULL; entry->info4 = NULL; } xfree(cp); return entry; } static char * ipban_entry_to_str(t_ipban_entry const * entry) { char tstr[MAX_MESSAGE_LEN]; char * str; switch (entry->type) { case ipban_type_exact: std::sprintf(tstr, "%s", entry->info1); break; case ipban_type_wildcard: std::sprintf(tstr, "%s.%s.%s.%s", entry->info1, entry->info2, entry->info3, entry->info4); break; case ipban_type_range: std::sprintf(tstr, "%s-%s", entry->info1, entry->info2); break; case ipban_type_netmask: case ipban_type_prefix: std::sprintf(tstr, "%s/%s", entry->info1, entry->info2); break; default: /* unknown type */ eventlog(eventlog_level_warn, __FUNCTION__, "found bad ban type {}", (int)entry->type); return NULL; } str = xstrdup(tstr); return str; } static unsigned long ipban_str_to_ulong(char const * ipaddr) { unsigned long lip; char * ip1; char * ip2; char * ip3; char * ip4; char * tipaddr; tipaddr = xstrdup(ipaddr); ip1 = std::strtok(tipaddr, "."); ip2 = std::strtok(NULL, "."); ip3 = std::strtok(NULL, "."); ip4 = std::strtok(NULL, "."); lip = (std::atoi(ip1) << 24) + (std::atoi(ip2) << 16) + (std::atoi(ip3) << 8) + (std::atoi(ip4)); xfree(tipaddr); return lip; } static int ipban_could_be_exact_ip_str(char const * str) { char * ipstr; char * s; char * ttok; int i; ipstr = xstrdup(str); s = ipstr; for (i = 0; i < 4; i++) { ttok = (char *)strsep(&ipstr, "."); if (!ttok || std::strlen(ttok)<1 || std::strlen(ttok)>3) { xfree(s); return 0; } } xfree(s); return 1; } static int ipban_could_be_ip_str(char const * str) { char * matched; char * ipstr; unsigned int i; size_t strlen = std::strlen(str); if (strlen < 7) { eventlog(eventlog_level_error, __FUNCTION__, "string too short"); return 0; } for (i = 0; i < strlen; i++) if (!std::isdigit((int)str[i]) && str[i] != '.' && str[i] != '*' && str[i] != '/' && str[i] != '-') { eventlog(eventlog_level_debug, __FUNCTION__, "illegal character on position {}", i); return 0; } ipstr = xstrdup(str); if ((matched = std::strchr(ipstr, '-'))) { matched[0] = '\0'; if ((ipban_could_be_exact_ip_str(ipstr) == 0) || (ipban_could_be_exact_ip_str(&matched[1]) == 0)) { xfree(ipstr); return 0; } } else if ((matched = std::strchr(ipstr, '*'))) { if (ipban_could_be_exact_ip_str(ipstr) == 0) /* FIXME: 123.123.1*.123 allowed */ { xfree(ipstr); return 0; } } else if ((matched = std::strchr(ipstr, '/'))) { matched[0] = '\0'; if (std::strchr(&matched[1], '.')) { if ((ipban_could_be_exact_ip_str(ipstr) == 0) || (ipban_could_be_exact_ip_str(&matched[1]) == 0)) { xfree(ipstr); return 0; } } else { if (ipban_could_be_exact_ip_str(ipstr) == 0) { xfree(ipstr); return 0; } for (i = 1; i32) /* can not be less than 0 because IP/-24 is matched as range */ { xfree(ipstr); return 0; } } } else { if (ipban_could_be_exact_ip_str(ipstr) == 0) { xfree(ipstr); return 0; } } xfree(ipstr); return 1; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/ipban.h000066400000000000000000000051461462640461400173330ustar00rootroot00000000000000/* * Copyright (C) 2000 Gediminas (gugini@fortas.ktu.lt) * Copyright (C) 2002 Bartomiej Butyn (bartek@milc.com.pl) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_IPBAN_TYPES #define INCLUDED_IPBAN_TYPES #ifdef IPBAN_INTERNAL_ACCESS #include #define MAX_FUNC_LEN 10 #define MAX_IP_STR 32 #define MAX_TIME_STR 9 #define IPBAN_FUNC_ADD 1 #define IPBAN_FUNC_DEL 2 #define IPBAN_FUNC_LIST 3 #define IPBAN_FUNC_CHECK 4 #define IPBAN_FUNC_UNKNOWN 5 namespace pvpgn { namespace bnetd { typedef enum { ipban_type_exact, /* 192.168.0.10 */ ipban_type_wildcard, /* 192.168.*.* */ ipban_type_range, /* 192.168.0.10-192.168.0.25 */ ipban_type_netmask, /* 192.168.0.0/255.255.0.0 */ ipban_type_prefix /* 192.168.0.0/16 */ } t_ipban_type; typedef struct ipban_entry_struct { char * info1; /* third octet */ char * info2; /* third octet */ char * info3; /* third octet */ char * info4; /* fourth octet */ int type; std::time_t endtime; } t_ipban_entry; } } #endif #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_IPBAN_PROTOS #define INCLUDED_IPBAN_PROTOS #include #define JUST_NEED_TYPES #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int ipbanlist_create(void); extern int ipbanlist_destroy(void); extern int ipbanlist_load(char const * filename); extern int ipbanlist_save(char const * filename); extern int ipbanlist_check(char const * addr); extern int ipbanlist_add(t_connection * c, char const * cp, std::time_t endtime); extern int ipbanlist_unload_expired(void); extern std::time_t ipbanlist_str_to_time_t(t_connection * c, char const * timestr); extern int handle_ipban_command(t_connection * c, char const * text); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/irc.cpp000066400000000000000000001433071462640461400173540ustar00rootroot00000000000000/* * Copyright (C) 2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2005 Bryan Biedenkapp (gatekeep@gmail.com) * Copyright (C) 2006,2007,2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "irc.h" #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/irc_protocol.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/field_sizes.h" #include "common/bnethash.h" #include "common/xalloc.h" #include "common/addr.h" #include "common/tag.h" #include "common/list.h" #include "common/util.h" #include "message.h" #include "channel.h" #include "game.h" #include "connection.h" #include "server.h" #include "account.h" #include "account_wrap.h" #include "prefs.h" #include "tick.h" #include "handle_irc.h" #include "handle_wol.h" #include "command_groups.h" #include "topic.h" #include "clan.h" #include "command.h" #include "anongame_wol.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef struct { char const * nick; char const * user; char const * host; } t_irc_message_from; static char ** irc_split_elems(char * list, int separator, int ignoreblank); static int irc_unget_elems(char ** elems); static char * irc_message_preformat(t_irc_message_from const * from, char const * command, char const * dest, char const * text); extern int irc_send_cmd(t_connection * conn, char const * command, char const * params) { t_packet * p; char data[MAX_IRC_MESSAGE_LEN + 1]; char const * ircname = server_get_hostname(); char const * nick; if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!command) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL command"); return -1; } if (!(p = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return -1; } nick = conn_get_loggeduser(conn); if (!nick) nick = "UserName"; if (params) std::snprintf(data, sizeof(data), ":%s %s %s %s", ircname, command, nick, params); else std::snprintf(data, sizeof(data), ":%s %s %s", ircname, command, nick); DEBUG2("[{}] sent \"{}\"", conn_get_socket(conn), data); std::strcat(data, "\r\n"); packet_set_size(p, 0); packet_append_data(p, data, std::strlen(data)); conn_push_outqueue(conn, p); packet_del_ref(p); return 0; } extern int irc_send(t_connection * conn, int code, char const * params) { char temp[4]; /* '000\0' */ if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if ((code > 999) || (code < 0)) { /* more than 3 digits or negative */ eventlog(eventlog_level_error, __FUNCTION__, "invalid message code ({})", code); return -1; } std::sprintf(temp, "%03u", code); return irc_send_cmd(conn, temp, params); } extern int irc_send_ping(t_connection * conn) { t_packet * p; char data[MAX_IRC_MESSAGE_LEN]; if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if ((conn_get_class(conn) == conn_class_wserv) || (conn_get_class(conn) == conn_class_wgameres) || (conn_get_class(conn) == conn_class_wladder)) return 0; if (!(p = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return -1; } conn_set_ircping(conn, get_ticks()); if (conn_get_state(conn) == conn_state_bot_username) std::sprintf(data, "PING :%u", conn_get_ircping(conn)); /* Undernet doesn't reveal the servername yet ... neither do we */ else if ((6 + std::strlen(server_get_hostname()) + 2 + 1) <= MAX_IRC_MESSAGE_LEN) std::sprintf(data, "PING :%s", server_get_hostname()); else eventlog(eventlog_level_error, __FUNCTION__, "maximum message length exceeded"); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] sent \"{}\"", conn_get_socket(conn), data); std::strcat(data, "\r\n"); packet_set_size(p, 0); packet_append_data(p, data, std::strlen(data)); conn_push_outqueue(conn, p); packet_del_ref(p); return 0; } extern int irc_send_pong(t_connection * conn, char const * params) { t_packet * p; char data[MAX_IRC_MESSAGE_LEN]; if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if ((1 + std::strlen(server_get_hostname()) + 1 + 4 + 1 + std::strlen(server_get_hostname()) + ((params) ? (2 + std::strlen(params)) : (0)) + 2 + 1) > MAX_IRC_MESSAGE_LEN) { eventlog(eventlog_level_error, __FUNCTION__, "max message length exceeded"); return -1; } if (!(p = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return -1; } if (params) std::sprintf(data, ":%s PONG %s :%s", server_get_hostname(), server_get_hostname(), params); else std::sprintf(data, ":%s PONG %s", server_get_hostname(), server_get_hostname()); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] sent \"{}\"", conn_get_socket(conn), data); std::strcat(data, "\r\n"); packet_set_size(p, 0); packet_append_data(p, data, std::strlen(data)); conn_push_outqueue(conn, p); packet_del_ref(p); return 0; } extern int irc_authenticate(t_connection * conn, char const * passhash) { /* FIXME: Move this function to handle_irc.* file! */ char temp[MAX_IRC_MESSAGE_LEN]; t_hash h1; t_hash h2; t_account * a; char const * temphash; char const * username; if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return 0; } if (!passhash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL passhash"); return 0; } username = conn_get_loggeduser(conn); if (!username) { /* redundant sanity check */ eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn->protocol.loggeduser"); return 0; } a = accountlist_find_account(username); if (!a) { message_send_text(conn, message_type_notice, NULL, "Authentication failed."); return 0; } if (connlist_find_connection_by_account(a) && prefs_get_kick_old_login() == 0) { std::snprintf(temp, sizeof(temp), "%s :Account is already in use!", conn_get_loggeduser(conn)); irc_send(conn, ERR_NICKNAMEINUSE, temp); } else if (account_get_auth_lock(a) == 1) { message_send_text(conn, message_type_notice, NULL, "Authentication rejected (account is locked) "); } else { hash_set_str(&h1, passhash); temphash = account_get_pass(a); hash_set_str(&h2, temphash); if (hash_eq(h1, h2)) { conn_login(conn, a, username); conn_set_state(conn, conn_state_loggedin); conn_set_clienttag(conn, CLIENTTAG_IIRC_UINT); /* IIRC hope here is ok */ message_send_text(conn, message_type_notice, NULL, "Authentication successful. You are now logged in."); return 1; } else { message_send_text(conn, message_type_notice, NULL, "Authentication failed."); /* wrong password */ conn_increment_passfail_count(conn); } } return 0; } /* Channel name conversion rules: */ /* Not allowed in IRC (RFC2812): NUL, BELL, CR, LF, ' ', ':' and ','*/ /* ' ' -> '_' */ /* '_' -> '%_' */ /* '%' -> '%%' */ /* '\b' -> '%b' */ /* '\n' -> '%n' */ /* '\r' -> '%r' */ /* ':' -> '%=' */ /* ',' -> '%-' */ /* In IRC a channel can be specified by '#'+channelname or '!'+channelid */ extern char const * irc_convert_channel(t_channel const * channel, t_connection * c) { char const * bname; static char out[MAX_CHANNELNAME_LEN]; unsigned int outpos; int i; if (!channel) return "*"; std::memset(out, 0, sizeof(out)); out[0] = '#'; outpos = 1; if ((conn_get_wol(c) == 1) && (channel_get_clienttag(channel) != 0 && (conn_get_clienttag(c) == channel_get_clienttag(channel)))) bname = channel_get_shortname(channel); /* We converting unreadable "lob 18 0" names to human redable ones */ else bname = channel_get_name(channel); for (i = 0; bname[i] != '\0'; i++) { if (bname[i] == ' ') { out[outpos++] = '_'; } else if (bname[i] == '_') { out[outpos++] = '%'; out[outpos++] = '_'; } else if (bname[i] == '%') { out[outpos++] = '%'; out[outpos++] = '%'; } else if (bname[i] == '\b') { out[outpos++] = '%'; out[outpos++] = 'b'; } else if (bname[i] == '\n') { out[outpos++] = '%'; out[outpos++] = 'n'; } else if (bname[i] == '\r') { out[outpos++] = '%'; out[outpos++] = 'r'; } else if (bname[i] == ':') { out[outpos++] = '%'; out[outpos++] = '='; } else if (bname[i] == ',') { out[outpos++] = '%'; out[outpos++] = '-'; } else { out[outpos++] = bname[i]; } if ((outpos + 2) >= (sizeof(out))) { std::sprintf(out, "!%u", channel_get_channelid(channel)); return out; } } return out; } extern char const * irc_convert_ircname(char const * pircname) { static char out[MAX_CHANNELNAME_LEN]; unsigned int outpos; int special; int i; char const * ircname = pircname + 1; if (!ircname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL ircname"); return NULL; } outpos = 0; std::memset(out, 0, sizeof(out)); special = 0; if (pircname[0] == '!') { t_channel * channel; channel = channellist_find_channel_bychannelid(std::atoi(ircname)); if (channel) return channel_get_name(channel); else return NULL; } else if (pircname[0] != '#') { return NULL; } for (i = 0; ircname[i] != '\0'; i++) { if (ircname[i] == '_') { out[outpos++] = ' '; } else if (ircname[i] == '%') { if (special) { out[outpos++] = '%'; special = 0; } else { special = 1; } } else if (special) { if (ircname[i] == '_') { out[outpos++] = '_'; } else if (ircname[i] == 'b') { out[outpos++] = '\b'; } else if (ircname[i] == 'n') { out[outpos++] = '\n'; } else if (ircname[i] == 'r') { out[outpos++] = '\r'; } else if (ircname[i] == '=') { out[outpos++] = ':'; } else if (ircname[i] == '-') { out[outpos++] = ','; } else { /* maybe it's just a typo :) */ out[outpos++] = '%'; out[outpos++] = ircname[i]; } } else { out[outpos++] = ircname[i]; } if ((outpos + 2) >= (sizeof(out))) { return NULL; } } return out; } /* splits an string list into its elements */ /* (list will be modified) */ static char ** irc_split_elems(char * list, int separator, int ignoreblank) { int i; int count; char ** out; if (!list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); return NULL; } for (count = 0, i = 0; list[i] != '\0'; i++) { if (list[i] == separator) { count++; if (ignoreblank) { /* ignore more than one separators "in a row" */ while ((list[i + 1] != '\0') && (list[i] == separator)) i++; } } } count++; /* count separators -> we have one more element ... */ /* we also need a terminating element */ out = (char**)xmalloc((count + 1)*sizeof(char *)); out[0] = list; if (count > 1) { for (i = 1; i < count; i++) { out[i] = std::strchr(out[i - 1], separator); if (!out[i]) { eventlog(eventlog_level_error, __FUNCTION__, "BUG: wrong number of separators"); xfree(out); return NULL; } if (ignoreblank) while ((*out[i] + 1) == separator) out[i]++; *out[i]++ = '\0'; } if ((ignoreblank) && (out[count - 1]) && (*out[count - 1] == '\0')) { out[count - 1] = NULL; /* last element is blank */ } } else if ((ignoreblank) && (*out[0] == '\0')) { out[0] = NULL; /* now we have 2 terminators ... never mind */ } out[count] = NULL; /* terminating element */ return out; } static int irc_unget_elems(char ** elems) { if (!elems) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elems"); return -1; } xfree(elems); return 0; } extern char ** irc_get_listelems(char * list) { return irc_split_elems(list, ',', 0); } extern int irc_unget_listelems(char ** elems) { return irc_unget_elems(elems); } extern char ** irc_get_paramelems(char * list) { return irc_split_elems(list, ' ', 1); } extern int irc_unget_paramelems(char ** elems) { return irc_unget_elems(elems); } extern char ** irc_get_ladderelems(char * list) { return irc_split_elems(list, ':', 1); } extern int irc_unget_ladderelems(char ** elems) { return irc_unget_elems(elems); } static char * irc_message_preformat(t_irc_message_from const * from, char const * command, char const * dest, char const * text) { char * myfrom; char const * mydest = ""; char const * mytext = ""; int len; char * msg; if (!command) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL command"); return NULL; } if (from) { if ((!from->nick) || (!from->user) || (!from->host)) { eventlog(eventlog_level_error, __FUNCTION__, "got malformed from"); return NULL; } myfrom = (char*)xmalloc(std::strlen(from->nick) + 1 + std::strlen(from->user) + 1 + std::strlen(from->host) + 1); /* nick + "!" + user + "@" + host + "\0" */ std::sprintf(myfrom, "%s!%s@%s", from->nick, from->user, from->host); } else myfrom = xstrdup(server_get_hostname()); if (dest) mydest = dest; if (text) mytext = text; len = 1 + std::strlen(myfrom) + 1 + std::strlen(command) + 1 + std::strlen(mydest) + 1 + 1 + std::strlen(mytext) + 1; msg = (char*)xmalloc(len); std::sprintf(msg, ":%s\n%s\n%s\n%s", myfrom, command, mydest, mytext); xfree(myfrom); return msg; } extern int irc_message_postformat(t_packet * packet, t_connection const * dest) { /* the four elements */ char * e1; char * e1_2; char * e2; char * e3; char * e4; char const * tname = NULL; char const * toname = "AUTH"; /* fallback name */ const char * temp; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (!dest) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dest"); return -1; } e1 = (char*)packet_get_raw_data(packet, 0); e2 = std::strchr(e1, '\n'); if (!e2) { eventlog(eventlog_level_warn, __FUNCTION__, "malformed message (e2 missing)"); return -1; } *e2++ = '\0'; e3 = std::strchr(e2, '\n'); if (!e3) { eventlog(eventlog_level_warn, __FUNCTION__, "malformed message (e3 missing)"); return -1; } *e3++ = '\0'; e4 = std::strchr(e3, '\n'); if (!e4) { eventlog(eventlog_level_warn, __FUNCTION__, "malformed message (e4 missing)"); return -1; } *e4++ = '\0'; if (prefs_get_hide_addr() && !(account_get_command_groups(conn_get_account(dest)) & command_get_group("/admin-addr"))) { e1_2 = std::strchr(e1, '@'); if (e1_2) { *e1_2++ = '\0'; } } else e1_2 = NULL; if (e3[0] == '\0') { /* fill in recipient */ if ((tname = conn_get_loggeduser(dest))) toname = tname; } else toname = e3; if (std::strcmp(toname, "\r") == 0) { toname = ""; /* HACK: the target field is really empty */ temp = ""; } else temp = " "; if (std::strlen(e1) + 1 + std::strlen(e2) + 1 + std::strlen(toname) + std::strlen(temp) + std::strlen(e4) + 2 + 1 <= MAX_IRC_MESSAGE_LEN) { char msg[MAX_IRC_MESSAGE_LEN + 1]; if (e1_2) std::sprintf(msg, "%s@hidden %s %s%s%s", e1, e2, toname, temp, e4); else std::sprintf(msg, "%s %s %s%s%s", e1, e2, toname, temp, e4); DEBUG2("[{}] sent \"{}\"", conn_get_socket(dest), msg); std::strcat(msg, "\r\n"); packet_set_size(packet, 0); packet_append_data(packet, msg, std::strlen(msg)); if (tname) conn_unget_chatname(dest, tname); return 0; } else { /* FIXME: split up message? */ eventlog(eventlog_level_warn, __FUNCTION__, "maximum IRC message length exceeded"); if (tname) conn_unget_chatname(dest, tname); return -1; } } extern int irc_message_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags) { char * msg; char const * ctag; t_irc_message_from from; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } msg = NULL; if (me) ctag = clienttag_uint_to_str(conn_get_clienttag(me)); else ctag = clienttag_uint_to_str(CLIENTTAG_IIRC_UINT); switch (type) { /* case message_type_adduser: this is sent manually in handle_irc */ case message_type_adduser: /* when we do it somewhere else, then we can also make sure to not get our logs spammed */ break; case message_type_join: { t_channel * channel; from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); channel = conn_get_channel(me); if (tag_check_wolv2(conn_get_clienttag(dst))) { char temp[MAX_IRC_MESSAGE_LEN]; t_clan * clan; unsigned int clanid = 0; std::memset(temp, 0, sizeof(temp)); /** * For WOLv2 the channel JOIN output must be like the following: * user!WWOL@hostname JOIN :clanID,longIP channelName */ clan = account_get_clan(conn_get_account(me)); if (clan) clanid = clan_get_clanid(clan); std::sprintf(temp, ":%u,%u", clanid, conn_get_addr(me)); msg = irc_message_preformat(&from, "JOIN", temp, irc_convert_channel(channel, dst)); } else { msg = irc_message_preformat(&from, "JOIN", "\r", irc_convert_channel(channel, dst)); } conn_unget_chatname(me, from.nick); } break; case message_type_part: from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "PART", "\r", irc_convert_channel(conn_get_channel(me), dst)); conn_unget_chatname(me, from.nick); break; case message_type_talk: case message_type_whisper: { char const * dest; char temp[MAX_IRC_MESSAGE_LEN]; if (me) { from.nick = conn_get_chatname(me); from.host = addr_num_to_ip_str(conn_get_addr(me)); } else { from.nick = server_get_hostname(); from.host = server_get_hostname(); } from.user = ctag; if (type == message_type_talk) dest = irc_convert_channel(conn_get_channel(me), dst); /* FIXME: support more channels and choose right one! */ else dest = ""; /* will be replaced with username in postformat */ std::sprintf(temp, ":%s", text); if ((conn_get_wol(me) != 1) && (conn_get_wol(dst) == 1) && (conn_wol_get_pageme(dst)) && (type == message_type_whisper) && (conn_get_channel(me) != (conn_get_channel(dst)))) msg = irc_message_preformat(&from, "PAGE", NULL, temp); else msg = irc_message_preformat(&from, "PRIVMSG", dest, temp); if (me) conn_unget_chatname(me, from.nick); } break; case message_type_emote: { char const * dest; char temp[MAX_IRC_MESSAGE_LEN]; /* "\001ACTION " + text + "\001" + \0 */ /* PELISH: WOLv1, DUNE2000 and RENEGADE shows emotes automaticaly to self */ if ((me == dst) && ((tag_check_wolv1(conn_get_clienttag(dst))) || (conn_get_clienttag(dst) == CLIENTTAG_DUNE2000_UINT) || (conn_get_clienttag(dst) == CLIENTTAG_RENEGADE_UINT) || (conn_get_clienttag(dst) == CLIENTTAG_RENGDFDS_UINT))) break; if ((8 + std::strlen(text) + 1 + 1) <= MAX_IRC_MESSAGE_LEN) { std::sprintf(temp, ":\001ACTION %s\001", text); } else { std::sprintf(temp, ":\001ACTION (maximum message length exceeded)\001"); } from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); /* FIXME: also supports whisper emotes? */ dest = irc_convert_channel(conn_get_channel(me), dst); /* FIXME: support more channels and choose right one! */ msg = irc_message_preformat(&from, "PRIVMSG", dest, temp); conn_unget_chatname(me, from.nick); } break; case message_type_broadcast: case message_type_info: case message_type_error: { char temp[MAX_IRC_MESSAGE_LEN]; std::sprintf(temp, ":%s", text); if (conn_get_wol(dst) == 1) { if ((type == message_type_info) || (type == message_type_error)) msg = irc_message_preformat(NULL, "PAGE", NULL, temp); else msg = irc_message_preformat(NULL, "NOTICE", NULL, temp); } else msg = irc_message_preformat(NULL, "NOTICE", NULL, temp); } break; case message_type_channel: /* ignore it */ break; case message_type_userflags: /* ignore it but maybe will be used for set MODE +o if user is operator * but at this time is this command sended only once when user join * first channel */ break; case message_type_mode: from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "MODE", "\r", text); conn_unget_chatname(me, from.nick); break; case message_type_nick: { from.nick = conn_get_loggeduser(me); from.host = addr_num_to_ip_str(conn_get_addr(me)); from.user = ctag; msg = irc_message_preformat(&from, "NICK", "\r", text); } break; case message_type_notice: { char temp[MAX_IRC_MESSAGE_LEN]; std::sprintf(temp, ":%s", text); if (me && conn_get_chatname(me)) { from.nick = conn_get_chatname(me); from.host = addr_num_to_ip_str(conn_get_addr(me)); from.user = ctag; msg = irc_message_preformat(&from, "NOTICE", "", temp); } else { msg = irc_message_preformat(NULL, "NOTICE", "", temp); } } break; case message_type_namreply: { t_channel * channel; channel = conn_get_channel(me); irc_send_rpl_namreply(dst, channel); } break; case message_type_topic: { t_channel * channel; channel = conn_get_channel(me); irc_send_topic(dst, channel); } break; case message_type_kick: { char temp[MAX_IRC_MESSAGE_LEN]; from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); if (text) std::sprintf(temp, "%s :%s", conn_get_chatname(me), text); else std::sprintf(temp, "%s :", conn_get_chatname(me)); msg = irc_message_preformat(&from, "KICK", irc_convert_channel(conn_get_channel(me), dst), temp); conn_unget_chatname(me, from.nick); } break; case message_type_quit: if (conn_get_clienttag(dst) == CLIENTTAG_IIRC_UINT) { char temp[MAX_IRC_MESSAGE_LEN]; if (text) sprintf(temp, ":%s", text); else sprintf(temp, ":"); from.nick = conn_get_chatname(me); from.host = addr_num_to_ip_str(conn_get_addr(me)); from.user = ctag; msg = irc_message_preformat(&from, "QUIT", "\r", temp); } else { from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "PART", "\r", irc_convert_channel(conn_get_channel(me), dst)); conn_unget_chatname(me, from.nick); } break; /** * Westwood Online Extensions */ case message_type_host: from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "HOST", "\r", text); conn_unget_chatname(me, from.nick); break; case message_type_invmsg: from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "INVMSG", NULL, text); conn_unget_chatname(me, from.nick); break; case message_type_page: { char temp[MAX_IRC_MESSAGE_LEN]; from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); std::sprintf(temp, ":%s", text); msg = irc_message_preformat(&from, "PAGE", NULL, temp); conn_unget_chatname(me, from.nick); } break; case message_wol_joingame: from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "JOINGAME", "\r", text); conn_unget_chatname(me, from.nick); break; case message_type_gameopt_talk: case message_type_gameopt_whisper: { char const * dest; char temp[MAX_IRC_MESSAGE_LEN]; if (me) { from.nick = conn_get_chatname(me); from.host = addr_num_to_ip_str(conn_get_addr(me)); } else { from.nick = server_get_hostname(); from.host = server_get_hostname(); } from.user = ctag; if (type == message_type_gameopt_talk) dest = irc_convert_channel(conn_get_channel(me), dst); /* FIXME: support more channels and choose right one! */ else dest = ""; /* will be replaced with username in postformat */ std::sprintf(temp, ":%s", text); msg = irc_message_preformat(&from, "GAMEOPT", dest, temp); if (me) conn_unget_chatname(me, from.nick); } break; case message_wol_start_game: from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "STARTG", NULL, text); conn_unget_chatname(me, from.nick); break; case message_wol_advertr: msg = irc_message_preformat(NULL, "ADVERTR", "\r", text); break; case message_wol_chanchk: msg = irc_message_preformat(NULL, "CHANCHK", "\r", text); break; case message_wol_userip: from.nick = conn_get_chatname(me); from.user = ctag; from.host = addr_num_to_ip_str(conn_get_addr(me)); msg = irc_message_preformat(&from, "USERIP", "\r", text); conn_unget_chatname(me, from.nick); break; default: eventlog(eventlog_level_warn, __FUNCTION__, "{} not yet implemented", type); return -1; } if (msg) { packet_append_string(packet, msg); xfree(msg); return 0; } return -1; } int irc_send_rpl_namreply_internal(t_connection * c, t_channel const * channel){ char temp[MAX_IRC_MESSAGE_LEN]; char const * ircname; int first = 1; t_connection * m; if (!channel) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel"); return -1; } std::memset(temp, 0, sizeof(temp)); ircname = irc_convert_channel(channel, c); if (!ircname) { eventlog(eventlog_level_error, __FUNCTION__, "channel has NULL ircname"); return -1; } /* '@' = secret; '*' = private; '=' = public */ if ((1 + 1 + std::strlen(ircname) + 2 + 1) <= MAX_IRC_MESSAGE_LEN) { std::sprintf(temp, "%c %s :", ((channel_get_permanent(channel)) ? ('=') : ('*')), ircname); } else { eventlog(eventlog_level_warn, __FUNCTION__, "maximum message length exceeded"); return -1; } if (!(channel_get_flags(channel) & channel_flags_thevoid)) for (m = channel_get_first(channel); m; m = channel_get_next()) { char const * name = conn_get_chatname(m); char flg[5] = ""; unsigned int flags; if (!name) continue; flags = conn_get_flags(m); if (flags & MF_BLIZZARD) std::strcat(flg, "@"); else if ((flags & MF_BNET) || (flags & MF_GAVEL)) std::strcat(flg, "%"); else if (flags & MF_VOICE) std::strcat(flg, "+"); if ((std::strlen(temp) + ((!first) ? (1) : (0)) + std::strlen(flg) + std::strlen(name) + 1) <= sizeof(temp)) { if (!first) std::strcat(temp, " "); if (conn_get_wol(c) == 1) { char _temp[MAX_IRC_MESSAGE_LEN]; t_game * game = conn_get_game(m); if ((first) && ((std::strcmp(ircname, "#Lob_38_0") == 0) || (std::strcmp(ircname, "#Lob_39_0") == 0) || (std::strcmp(ircname, "#Lob_40_0") == 0))) { std::sprintf(_temp, "@matchbot,0,0 "); std::strcat(temp, _temp); first = 0; } if ((game) && (game_get_channel(game) == channel)) { if (game_get_owner(game) == m) { /* PELISH: Only game owners will have OP flag (this prevent official OP to be normal player) */ std::strcat(temp, "@"); } } else { if ((flags & MF_BNET) || (flags & MF_GAVEL)) std::sprintf(flg, "@"); /* PELISH: WOL does not understand to '%' char so we using '@' for TempOP too */ std::strcat(temp, flg); } if (tag_check_wolv2(conn_get_clienttag(c))) { /* BATTLECLAN Support */ std::memset(_temp, 0, sizeof(_temp)); t_clan * clan = account_get_clan(conn_get_account(m)); unsigned int clanid = 0; if (clan) clanid = clan_get_clanid(clan); std::sprintf(_temp, "%s,%u,%u", name, clanid, conn_get_addr(m)); std::strcat(temp, _temp); } else { std::strcat(temp, name); } } else { std::strcat(temp, flg); std::strcat(temp, name); } first = 0; } conn_unget_chatname(m, name); } irc_send(c, RPL_NAMREPLY, temp); return 0; } extern int irc_send_rpl_namreply(t_connection * c, t_channel const * channel) { char temp[MAX_IRC_MESSAGE_LEN]; char const * ircname; if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (channel) { ircname = irc_convert_channel(channel, c); if (!ircname) { eventlog(eventlog_level_error, __FUNCTION__, "channel has NULL ircname"); return -1; } irc_send_rpl_namreply_internal(c, channel); std::sprintf(temp, "%.32s :End of NAMES list", ircname); } else { t_elem const * curr; LIST_TRAVERSE_CONST(channellist(), curr) { channel = (t_channel*)elem_get_data(curr); irc_send_rpl_namreply_internal(c, channel); } std::sprintf(temp, "* :End of NAMES list"); } irc_send(c, RPL_ENDOFNAMES, temp); return 0; } static int irc_who_connection(t_connection * dest, t_connection * c) { t_account * a; char const * tempuser; char const * tempowner; char const * tempname; char const * tempip; char const * tempflags = "@"; /* FIXME: that's dumb */ char temp[MAX_IRC_MESSAGE_LEN]; char const * tempchannel; if (!dest) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL destination"); return -1; } if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } a = conn_get_account(c); if (!(tempuser = clienttag_uint_to_str(conn_get_clienttag(c)))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clienttag (tempuser)"); return -1; } if (!(tempowner = account_get_ll_owner(a))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL ll_owner (tempowner)"); return -1; } if (!(tempname = conn_get_username(c))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL username (tempname)"); return -1; } if (!(tempip = addr_num_to_ip_str(conn_get_addr(c)))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr (tempip)"); return -1; } if (!(tempchannel = irc_convert_channel(conn_get_channel(c), c))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel (tempchannel)"); return -1; } if ((std::strlen(tempchannel) + 1 + std::strlen(tempuser) + 1 + std::strlen(tempip) + 1 + std::strlen(server_get_hostname()) + 1 + std::strlen(tempname) + 1 + 1 + std::strlen(tempflags) + 4 + std::strlen(tempowner) + 1) > MAX_IRC_MESSAGE_LEN) { eventlog(eventlog_level_info, __FUNCTION__, "WHO reply too long - skip"); return -1; } else std::sprintf(temp, "%s %s %s %s %s %c%s :0 %s", tempchannel, tempuser, tempip, server_get_hostname(), tempname, 'H', tempflags, tempowner); irc_send(dest, RPL_WHOREPLY, temp); return 0; } extern int irc_who(t_connection * c, char const * name) { /* FIXME: support wildcards! */ if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!name) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL name"); return -1; } if ((name[0] == '#') || (name[0] == '&') || (name[0] == '!')) { /* it's a channel */ t_connection * info; t_channel * channel; char const * ircname; ircname = irc_convert_ircname(name); channel = channellist_find_channel_by_name(ircname, NULL, NULL); if (!channel) { char temp[MAX_IRC_MESSAGE_LEN]; if ((std::strlen(":No such channel") + 1 + std::strlen(name) + 1) <= MAX_IRC_MESSAGE_LEN) { std::sprintf(temp, ":No such channel %s", name); irc_send(c, ERR_NOSUCHCHANNEL, temp); } else { irc_send(c, ERR_NOSUCHCHANNEL, ":No such channel"); } return 0; } for (info = channel_get_first(channel); info; info = channel_get_next()) { irc_who_connection(c, info); } } else { /* it's just one user */ t_connection * info; if ((info = connlist_find_connection_by_accountname(name))) return irc_who_connection(c, info); } return 0; } extern int irc_send_motd(t_connection * conn) { char const * tempname; char const * filename; std::FILE *fp; char * line, *formatted_line; char send_line[MAX_IRC_MESSAGE_LEN]; char motd_failed = 0; bool first = true; if (!conn) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } tempname = conn_get_loggeduser(conn); if ((filename = prefs_get_motdfile())) { if ((fp = std::fopen(filename, "r"))) { while ((line = file_get_line(fp))) { if ((formatted_line = message_format_line(conn, line))) { formatted_line[0] = ' '; std::sprintf(send_line, ":-%s", formatted_line); if (first) { irc_send(conn, RPL_MOTDSTART, send_line); first = false; } else irc_send(conn, RPL_MOTD, send_line); xfree(formatted_line); } } file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); } else motd_failed = 1; } else motd_failed = 1; if (motd_failed) { irc_send(conn, RPL_MOTDSTART, ":- Failed to load motd, sending default motd "); irc_send(conn, RPL_MOTD, ":- ====================================================== "); irc_send(conn, RPL_MOTD, ":- http://www.pvpgn.pro "); irc_send(conn, RPL_MOTD, ":- ====================================================== "); } irc_send(conn, RPL_ENDOFMOTD, ":End of /MOTD command"); return 0; } int irc_welcome(t_connection * conn){ if (conn_get_wol(conn)) handle_wol_welcome(conn); else handle_irc_welcome(conn); return 0; } extern int _handle_nick_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; /* FIXME: more strict param checking */ if ((conn_get_loggeduser(conn)) && (conn_get_state(conn) != conn_state_bot_password && conn_get_state(conn) != conn_state_bot_username)) { irc_send(conn, ERR_RESTRICTED, ":You can't change your nick after login"); } else { if ((params) && (params[0])) { if (conn_get_loggeduser(conn)){ std::snprintf(temp, sizeof(temp), ":%s", params[0]); message_send_text(conn, message_type_nick, conn, temp); } conn_set_loggeduser(conn, params[0]); } else if (text) { if (conn_get_loggeduser(conn)){ std::snprintf(temp, sizeof(temp), ":%s", text); message_send_text(conn, message_type_nick, conn, temp); } conn_set_loggeduser(conn, text); } else irc_send(conn, ERR_NEEDMOREPARAMS, "NICK :Not enough parameters"); if ((conn_get_user(conn)) && (conn_get_loggeduser(conn))) irc_welcome(conn); /* only send the welcome if we have USER and NICK */ } return 0; } extern int _handle_ping_command(t_connection * conn, int numparams, char ** params, char * text) { /* Dizzy: just ignore this because RFC says we should not reply client PINGs * NOTE: RFC2812 doesn't seem to be very expressive about this ... */ if (numparams) irc_send_pong(conn, params[0]); else irc_send_pong(conn, text); return 0; } extern int _handle_pong_command(t_connection * conn, int numparams, char ** params, char * text) { /* NOTE: RFC2812 doesn't seem to be very expressive about this ... */ if (conn_get_ircping(conn) == 0) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] PONG without PING", conn_get_socket(conn)); } else { unsigned int val = 0; char * sname; if (numparams >= 1) { val = std::strtoul(params[0], NULL, 10); sname = params[0]; } else if (text) { val = std::strtoul(text, NULL, 10); sname = text; } else { val = 0; sname = 0; } if (conn_get_ircping(conn) != val) { if ((!(sname)) || (std::strcmp(sname, server_get_hostname()) != 0)) { /* Actually the servername should not be always accepted but we aren't that pedantic :) */ eventlog(eventlog_level_warn, __FUNCTION__, "[{}] got bad PONG ({}!={} && {}!={})", conn_get_socket(conn), val, conn_get_ircping(conn), sname, server_get_hostname()); return -1; } } conn_set_latency(conn, get_ticks() - conn_get_ircping(conn)); eventlog(eventlog_level_debug, __FUNCTION__, "[{}] latency is now {} ({}-{})", conn_get_socket(conn), get_ticks() - conn_get_ircping(conn), get_ticks(), conn_get_ircping(conn)); conn_set_ircping(conn, 0); } return 0; } extern int _handle_join_command(t_connection * conn, int numparams, char ** params, char * text) { if (numparams >= 1) { char ** e; /* According to RFC2812 - if channelname is "0" we need to PART all channels */ if (std::strcmp(params[0], "0") == 0) { conn_part_channel(conn); return 0; } e = irc_get_listelems(params[0]); if ((e) && (e[0])) { char temp[MAX_IRC_MESSAGE_LEN]; char const * ircname = irc_convert_ircname(e[0]); t_channel * old_channel = conn_get_channel(conn); t_channel * channel; t_account * acc = conn_get_account(conn); if ((ircname) && (channel = channellist_find_channel_by_name(ircname, NULL, NULL))) { if (channel_check_banning(channel, conn)) { std::snprintf(temp, sizeof(temp), "%s :You are banned from that channel.", e[0]); irc_send(conn, ERR_BANNEDFROMCHAN, temp); if (e) irc_unget_listelems(e); return 0; } if ((account_get_auth_admin(acc, NULL) != 1) && (account_get_auth_admin(acc, ircname) != 1) && (account_get_auth_operator(acc, NULL) != 1) && (account_get_auth_operator(acc, ircname) != 1) && (channel_get_max(channel) != -1) && (channel_get_curr(channel) >= channel_get_max(channel))) { std::snprintf(temp, sizeof(temp), "%s :The channel is currently full.", e[0]); irc_send(conn, ERR_CHANNELISFULL, temp); if (e) irc_unget_listelems(e); return 0; } } if ((!(ircname)) || (conn_set_channel(conn, ircname) < 0)) { std::snprintf(temp, sizeof(temp), "%s :JOIN failed", e[0]); irc_send(conn, ERR_NOSUCHCHANNEL, temp); /* Anything is still bad */ } } if (e) irc_unget_listelems(e); } else irc_send(conn, ERR_NEEDMOREPARAMS, "JOIN :Not enough parameters"); return 0; } extern int irc_send_topic(t_connection * c, t_channel const * channel) { class_topic Topic; std::string topicstr = Topic.get(channel_get_name(channel)); char temp[MAX_IRC_MESSAGE_LEN]; if (topicstr.empty() == false) { std::snprintf(temp, sizeof(temp), "%s :%s", irc_convert_channel(channel, c), topicstr.c_str()); irc_send(c, RPL_TOPIC, temp); } else { std::snprintf(temp, sizeof(temp), "%s :", irc_convert_channel(channel, c)); irc_send(c, RPL_TOPIC, temp); /* PELISH: This is according to RFC2812 but brakes WOLv1/WOLv2 */ //snprintf(temp, sizeof(temp), "%s :No topic is set", irc_convert_channel(channel,c)); //irc_send(c, RPL_NOTOPIC, temp); } return 0; } extern int _handle_topic_command(t_connection * conn, int numparams, char ** params, char * text) { char ** e = NULL; char temp[MAX_IRC_MESSAGE_LEN]; class_topic Topic; if (params && params[0]) { if (conn_get_wol(conn) == 1) { t_channel * channel = conn_get_channel(conn); if (channel) Topic.set(std::string(channel_get_name(channel)), std::string(text), false); else { std::snprintf(temp, sizeof(temp), "%s :You're not on that channel", params[0]); irc_send(conn, ERR_NOTONCHANNEL, temp); } } e = irc_get_listelems(params[0]); } if ((e) && (e[0])) { t_channel *channel = conn_get_channel(conn); if (channel) { char const * ircname = irc_convert_ircname(e[0]); if ((ircname) && (strcasecmp(channel_get_name(channel), ircname) == 0)) { irc_send_topic(conn, channel); } else { std::snprintf(temp, sizeof(temp), "%s :You're not on that channel", e[0]); irc_send(conn, ERR_NOTONCHANNEL, temp); } } else { std::snprintf(temp, sizeof(temp), "%s :You're not on that channel", e[0]); irc_send(conn, ERR_NOTONCHANNEL, temp); } } else irc_send(conn, ERR_NEEDMOREPARAMS, "TOPIC :Not enough parameters"); if (e) { irc_unget_listelems(e); } return 0; } extern int _handle_kick_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; char ** e; /** * Heres the imput expected * KICK [channel] [kicked_user],[kicked_user2] * * Heres the output expected * :user!WWOL@hostname KICK [channel] [kicked_user] :[text] * * WOL in [text] sends Admin name */ if ((numparams != 2) || !(params[1])) { irc_send(conn, ERR_NEEDMOREPARAMS, "KICK :Not enough parameters"); return 0; } if (e = irc_get_listelems(params[1])) { /* Make standart PvPGN KICK from RFC2812 KICK */ if (text) std::snprintf(temp, sizeof(temp), "/kick %s %s", e[0], text); else std::snprintf(temp, sizeof(temp), "/kick %s", e[0]); handle_command(conn, temp); irc_unget_listelems(e); } return 0; } static int irc_send_banlist(t_connection * conn, t_channel * channel) { t_elem const * curr; char const * banned; char const * ircname = server_get_hostname(); char temp[MAX_IRC_MESSAGE_LEN]; if (!conn) { ERROR0("got NULL conn"); return -1; } if (!channel) { ERROR0("got NULL channel"); return -1; } LIST_TRAVERSE_CONST(channel_get_banlist(channel), curr) { banned = (char*)elem_get_data(curr); //FIXME: right now we lie about who have gives ban and also about bantime std::snprintf(temp, sizeof(temp), "%s %s!*@* %s 1208297879", irc_convert_channel(channel, conn), banned, ircname); irc_send(conn, RPL_BANLIST, temp); } return 0; } extern int _handle_mode_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN]; t_account * acc = conn_get_account(conn); std::memset(temp, 0, sizeof(temp)); if (numparams < 1) { irc_send(conn, ERR_NEEDMOREPARAMS, "MODE :Not enough parameters"); return 0; } if (params[0][0] == '#') { /* Channel mode */ t_channel * channel; char const * ircname = irc_convert_ircname(params[0]); /* FIXME: Supports more than one channel in MODE command */ if (!(channel = conn_get_channel(conn))) { std::snprintf(temp, sizeof(temp), "%s :No such channel", params[0]); irc_send(conn, ERR_NOSUCHCHANNEL, temp); return 0; } if (numparams == 1) { /* FIXME: Send real CHANELMODE flags! */ std::snprintf(temp, sizeof(temp), "%s +tns", params[0]); irc_send(conn, RPL_CHANNELMODEIS, temp); return 0; } if (numparams == 2) { if (std::strcmp(params[1], "b") == 0) { irc_send_banlist(conn, channel); std::snprintf(temp, sizeof(temp), "%s :End of channel ban list", params[0]); irc_send(conn, RPL_ENDOFBANLIST, temp); return 0; } else { std::snprintf(temp, sizeof(temp), ":%s is unknown mode char to me for %s", params[1], params[0]); irc_send(conn, ERR_UNKNOWNMODE, temp); return 0; } } /* PELISH: Also tmpOP have setting modes alowed because all new channels have only tmpOP */ if ((channel_conn_is_tmpOP(channel, conn) != 1) && (account_get_auth_admin(acc, NULL) != 1) && (account_get_auth_admin(acc, ircname) != 1) && (account_get_auth_operator(acc, NULL) != 1) && (account_get_auth_operator(acc, ircname) != 1)) { std::snprintf(temp, sizeof(temp), "%s :You're not channel operator", params[0]); irc_send(conn, ERR_CHANOPRIVSNEEDED, temp); return 0; } if (std::strcmp(params[1], "+b") == 0) { channel_ban_user(channel, params[2]); std::snprintf(temp, sizeof(temp), "%s %s %s!*@*", params[0], params[1], params[2]); channel_message_send(channel, message_type_mode, conn, temp); } else if (std::strcmp(params[1], "-b") == 0) { channel_unban_user(channel, params[2]); std::snprintf(temp, sizeof(temp), "%s %s %s!*@*", params[0], params[1], params[2]); channel_message_send(channel, message_type_mode, conn, temp); } else if (std::strcmp(params[1], "+o") == 0) { std::snprintf(temp, sizeof(temp), "/op %s", params[2]); handle_command(conn, temp); std::snprintf(temp, sizeof(temp), "%s %s %s", params[0], params[1], params[2]); channel_message_send(channel, message_type_mode, conn, temp); } else if (std::strcmp(params[1], "-o") == 0) { std::snprintf(temp, sizeof(temp), "/deop %s", params[2]); handle_command(conn, temp); std::snprintf(temp, sizeof(temp), "%s %s %s", params[0], params[1], params[2]); channel_message_send(channel, message_type_mode, conn, temp); } else if (std::strcmp(params[1], "+v") == 0) { std::snprintf(temp, sizeof(temp), "/voice %s", params[2]); handle_command(conn, temp); std::snprintf(temp, sizeof(temp), "%s %s %s", params[0], params[1], params[2]); channel_message_send(channel, message_type_mode, conn, temp); } else if (std::strcmp(params[1], "-v") == 0) { std::snprintf(temp, sizeof(temp), "/devoice %s", params[2]); handle_command(conn, temp); std::snprintf(temp, sizeof(temp), "%s %s %s", params[0], params[1], params[2]); channel_message_send(channel, message_type_mode, conn, temp); } else if (std::strcmp(params[1], "+i") == 0) { /* FIXME: channel will be only for invited */ } else if (std::strcmp(params[1], "+l") == 0) { channel_set_max(channel, std::atoi(params[2])); std::snprintf(temp, sizeof(temp), "%s %s %s", params[0], params[1], params[2]); channel_message_send(channel, message_type_mode, conn, temp); } else if (std::strcmp(params[1], "-l") == 0) { channel_set_max(channel, -1); std::snprintf(temp, sizeof(temp), "%s %s", params[0], params[1]); channel_message_send(channel, message_type_mode, conn, temp); } else { std::snprintf(temp, sizeof(temp), ":%s is unknown mode char to me for %s", params[1], params[0]); irc_send(conn, ERR_UNKNOWNMODE, temp); } } else { /* User modes */ /* FIXME: Support user modes (away, invisible...) */ irc_send(conn, ERR_UMODEUNKNOWNFLAG, ":Unknown MODE flag"); } return 0; } extern int _handle_time_command(t_connection * conn, int numparams, char ** params, char * text) { char temp[MAX_IRC_MESSAGE_LEN] = {}; // PELISH: According to RFC2812 std::time_t now; char const * ircname = server_get_hostname(); if ((numparams >= 1) && (params[0])) { if (std::strcmp(params[0], ircname) == 0) { now = std::time(NULL); std::snprintf(temp, sizeof(temp), "%s :%" PRId64, ircname, static_cast(now)); irc_send(conn, RPL_TIME, temp); } else { std::snprintf(temp, sizeof(temp), "%s :No such server", params[0]); irc_send(conn, ERR_NOSUCHSERVER, temp); } } else { /* RPL_TIME contains time and name of this server */ now = std::time(NULL); std::snprintf(temp, sizeof(temp), "%s :%" PRId64, ircname, static_cast(now)); irc_send(conn, RPL_TIME, temp); } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/irc.h000066400000000000000000000062411462640461400170140ustar00rootroot00000000000000/* * Copyright (C) 2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_IRC_PROTOS #define INCLUDED_IRC_PROTOS #define JUST_NEED_TYPES # include "common/packet.h" # include "connection.h" # include "channel.h" # include "message.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int irc_send_cmd(t_connection * conn, char const * command, char const * params); extern int irc_send(t_connection * conn, int code, char const * params); extern int irc_send_ping(t_connection * conn); extern int irc_send_pong(t_connection * conn, char const * params); extern int irc_authenticate(t_connection * conn, char const * passhash); extern char const * irc_convert_channel(t_channel const * channel, t_connection * c); extern char const * irc_convert_ircname(char const * pircname); extern char ** irc_get_listelems(char * list); extern int irc_unget_listelems(char ** elems); extern char ** irc_get_paramelems(char * list); extern char ** irc_get_ladderelems(char * list); extern int irc_unget_ladderelems(char ** elems); extern int irc_unget_paramelems(char ** elems); extern int irc_message_postformat(t_packet * packet, t_connection const * dest); extern int irc_message_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags); extern int irc_send_rpl_namreply(t_connection * c, t_channel const * channel); extern int irc_who(t_connection * c, char const * name); extern int irc_send_motd(t_connection * conn); extern int _handle_nick_command(t_connection * conn, int numparams, char ** params, char * text); extern int _handle_ping_command(t_connection * conn, int numparams, char ** params, char * text); extern int _handle_pong_command(t_connection * conn, int numparams, char ** params, char * text); extern int _handle_join_command(t_connection * conn, int numparams, char ** params, char * text); extern int irc_send_topic(t_connection * c, t_channel const * channel); extern int _handle_topic_command(t_connection * conn, int numparams, char ** params, char * text); extern int _handle_kick_command(t_connection * conn, int numparams, char ** params, char * text); extern int _handle_mode_command(t_connection * conn, int numparams, char ** params, char * text); extern int _handle_time_command(t_connection * conn, int numparams, char ** params, char * text); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/ladder.cpp000066400000000000000000001411341462640461400200260ustar00rootroot00000000000000/* * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999,2000 D.Moreaux (vapula@linuxbe.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define LADDER_INTERNAL_ACCESS #include "ladder.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include "compat/strerror.h" #include "common/tag.h" #include "common/eventlog.h" #include "common/list.h" #include "common/hashtable.h" #include "common/bnettime.h" #include "common/bnet_protocol.h" #include "account.h" #include "account_wrap.h" #include "prefs.h" #include "ladder_calc.h" #include "team.h" #include "common/setup_after.h" #define MaxRankKeptInLadder 1000 namespace pvpgn { namespace bnetd { /* for War3 XP computations */ static t_xpcalc_entry * xpcalc; static t_xplevel_entry * xplevels; int w3_xpcalc_maxleveldiff; const std::vector ladder_id_str = { "0", "1", "", "3", "", "solo", "team", "ffa" }; const std::vector bin_ladder_id_str = { "", "", "", "I", "", "SOLO", "TEAM", "FFA", "AT" }; const std::vector bin_ladder_sort_str = { "R", "W", "G", "" }; const std::vector bin_ladder_time_str = { "A", "C", "" }; Ladders ladders; /* * Prepare an account for first ladder play if necessary. */ extern int ladder_init_account(t_account * account, t_clienttag clienttag, t_ladder_id id) { unsigned int uid, rating; if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } if (account_get_ladder_rating(account, clienttag, id) == 0) { if (account_get_ladder_wins(account, clienttag, id) + account_get_ladder_losses(account, clienttag, id) > 0) /* no ladder games so far... */ { eventlog(eventlog_level_warn, __FUNCTION__, "account for \"{}\" ({}) has {} wins and {} losses but has zero rating", account_get_name(account), clienttag_uint_to_str(clienttag), account_get_ladder_wins(account, clienttag, id), account_get_ladder_losses(account, clienttag, id)); return -1; } account_adjust_ladder_rating(account, clienttag, id, prefs_get_ladder_init_rating()); uid = account_get_uid(account); rating = account_get_ladder_rating(account, clienttag, id); LadderList* ladderlist_cr = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_highestrated, ladder_time_current)); LadderList* ladderlist_cw = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_mostwins, ladder_time_current)); LadderList* ladderlist_cg = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_mostgames, ladder_time_current)); LadderReferencedObject reference(account); ladderlist_cr->updateEntry(uid, rating, 0, 0, reference); ladderlist_cg->updateEntry(uid, 0, rating, 0, reference); ladderlist_cw->updateEntry(uid, 0, rating, 0, reference); INFO2("initialized account for \"{}\" for \"{}\" ladder", account_get_name(account), clienttag_uint_to_str(clienttag)); } return 0; } extern int ladder_init_account_wol(t_account * account, t_clienttag clienttag, t_ladder_id id) { unsigned int uid; if (!account) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL account"); return -1; } if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } if ((account_get_ladder_points(account, clienttag, id) == 0) && (account_get_ladder_wins(account, clienttag, id) == 0) && (account_get_ladder_losses(account, clienttag, id) == 0)) { uid = account_get_uid(account); LadderList* ladderlist = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_default, ladder_time_default)); LadderReferencedObject reference(account); ladderlist->updateEntry(uid, 0, 0, 0, reference); INFO2("initialized WOL account for \"{}\" for \"{}\" ladder", account_get_name(account), clienttag_uint_to_str(clienttag)); } return 0; } /* * Update player ratings, rankings, etc due to game results. */ extern int ladder_update(t_clienttag clienttag, t_ladder_id id, unsigned int count, t_account * * players, t_game_result * results, t_ladder_info * info) { unsigned int curr; unsigned int ratio = 0; int uid; if (count<1 || count>8) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid player count {}", count); return -1; } if (!players) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL players"); return -1; } if (!results) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL results"); return -1; } if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } if (!info) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL info"); return -1; } if (ladder_calc_info(clienttag, id, count, players, results, info) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "unable to calculate info from game results"); return -1; } for (curr = 0; curr < count; curr++) { unsigned int wins, games; t_account * account; account = players[curr]; uid = account_get_uid(account); wins = account_get_ladder_wins(account, clienttag, id); games = wins + account_get_ladder_losses(account, clienttag, id) + account_get_ladder_disconnects(account, clienttag, id) + account_get_ladder_draws(account, clienttag, id); //reasonable close to 1000 ;-) ratio = (wins << 10) / games; account_adjust_ladder_rating(account, clienttag, id, info[curr].adj); unsigned int rating = account_get_ladder_rating(account, clienttag, id); LadderReferencedObject reference(account); LadderList* ladderlist = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_highestrated, ladder_time_current)); ladderlist->updateEntry(uid, rating, wins, ratio, reference); ladderlist = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_mostgames, ladder_time_current)); ladderlist->updateEntry(uid, games, rating, ratio, reference); ladderlist = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_mostwins, ladder_time_current)); ladderlist->updateEntry(uid, wins, rating, ratio, reference); } ladders.update(); return 0; } static int _cb_count_points(int * points_win, int * points_loss, int pl1_points, int pl2_points) { *points_win = static_cast(64 * (1 - 1 / (powf(10, static_cast(pl2_points - pl1_points) / 400) + 1)) + 0.5); *points_loss = std::min(64 - *points_win, pl1_points / 10); return 0; } extern int ladder_update_wol(t_clienttag clienttag, t_ladder_id id, t_account * * players, t_game_result * results) { unsigned int wins, losses, points; int curr; int uid; if (!players) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL players"); return -1; } if (!results) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL results"); return -1; } if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } if (((results[0]) && (results[1])) && ((results[0] == results[1]) && (results[0] == game_result_disconnect))) { DEBUG0("Both players got game_result_disconnect - points counting terminated"); return 0; } int pl1_points = account_get_ladder_points(players[0], clienttag, id); int pl2_points = account_get_ladder_points(players[1], clienttag, id); for (curr = 0; curr < 2; curr++) { t_account * account = players[curr]; int mypoints = account_get_ladder_points(players[curr], clienttag, id); int points_win = 0; int points_loss = 0; uid = account_get_uid(account); if (results[curr] == game_result_win) { _cb_count_points(&points_win, &points_loss, mypoints, (mypoints == pl1_points ? pl2_points : pl1_points)); account_set_ladder_points(players[curr], clienttag, id, mypoints + points_win); DEBUG3("Player {} WIN, had {} points and now have {} points", account_get_name(account), mypoints, mypoints + points_win); } else { _cb_count_points(&points_win, &points_loss, mypoints, (mypoints == pl1_points ? pl2_points : pl1_points)); account_set_ladder_points(players[curr], clienttag, id, mypoints - points_loss); DEBUG3("Player {} LOSS, had {} points and now have {} points", account_get_name(account), mypoints, mypoints - points_loss); } LadderReferencedObject reference(account); wins = account_get_ladder_wins(account, clienttag, id); losses = account_get_ladder_losses(account, clienttag, id); points = account_get_ladder_points(account, clienttag, id); LadderList* ladderlist = ladders.getLadderList(LadderKey(id, clienttag, ladder_sort_default, ladder_time_default)); ladderlist->updateEntry(uid, points, wins, 0, reference); } ladders.update(); return 0; } extern int ladder_check_map(char const * mapname, t_game_maptype maptype, t_clienttag clienttag) { if (!mapname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL mapname"); return -1; } if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "checking mapname \"{}\" maptype={}", mapname, (int)maptype); if (maptype == game_maptype_ladder) /* FIXME: what about Ironman? */ return 1; return 0; } /* ********************************************************************************************************************* * handling of bnxpcalc and bnxplevel stuff (old code) * *********************************************************************************************************************/ extern int ladder_createxptable(const char *xplevelfile, const char *xpcalcfile) { std::FILE *fd1, *fd2; char buffer[256]; char *p; t_xpcalc_entry * newxpcalc; int len, i, j; int level, startxp, neededxp, mingames; float lossfactor; int minlevel, leveldiff, higher_xpgained, higher_xplost, lower_xpgained, lower_xplost = 10; if (xplevelfile == NULL || xpcalcfile == NULL) { eventlog(eventlog_level_error, "ladder_createxptable", "got NULL filename(s)"); return -1; } /* first lets open files */ if ((fd1 = std::fopen(xplevelfile, "rt")) == NULL) { eventlog(eventlog_level_error, "ladder_createxptable", "could not open XP level file : \"{}\"", xplevelfile); return -1; } if ((fd2 = std::fopen(xpcalcfile, "rt")) == NULL) { eventlog(eventlog_level_error, "ladder_createxptable", "could not open XP calc file : \"{}\"", xpcalcfile); std::fclose(fd1); return -1; } /* then lets allocate mem for all the arrays */ xpcalc = (t_xpcalc_entry*)xmalloc(sizeof(t_xpcalc_entry)* W3_XPCALC_MAXLEVEL); //presume the maximal leveldiff is level number w3_xpcalc_maxleveldiff = -1; std::memset(xpcalc, 0, sizeof(t_xpcalc_entry)* W3_XPCALC_MAXLEVEL); xplevels = (t_xplevel_entry*)xmalloc(sizeof(t_xplevel_entry)* W3_XPCALC_MAXLEVEL); std::memset(xplevels, 0, sizeof(t_xplevel_entry)* W3_XPCALC_MAXLEVEL); /* finally, lets read from the files */ while (std::fgets(buffer, 256, fd1)) { len = std::strlen(buffer); if (len < 2) continue; if (buffer[len - 1] == '\n') buffer[len - 1] = '\0'; /* support comments */ for (p = buffer; *p && *p != '#'; p++); if (*p == '#') *p = '\0'; if (std::sscanf(buffer, "%d %d %d %f %d", &level, &startxp, &neededxp, &lossfactor, &mingames) != 5) continue; if (level < 1 || level > W3_XPCALC_MAXLEVEL) { /* invalid level */ eventlog(eventlog_level_error, "ladder_createxptable", "read INVALID player level : {}", level); continue; } level--; /* the index in a C array starts from 0 */ xplevels[level].startxp = startxp; xplevels[level].neededxp = neededxp; xplevels[level].lossfactor = (int)(lossfactor * 100); /* we store the loss factor as % */ xplevels[level].mingames = mingames; eventlog(eventlog_level_trace, "ladder_createxptable", "inserting level XP info (level: {}, startxp: {} neededxp: {} lossfactor: {} mingames: {})", level + 1, xplevels[level].startxp, xplevels[level].neededxp, xplevels[level].lossfactor, xplevels[level].mingames); } std::fclose(fd1); while (std::fgets(buffer, 256, fd2)) { len = std::strlen(buffer); if (len < 2) continue; if (buffer[len - 1] == '\n') buffer[len - 1] = '\0'; /* support comments */ for (p = buffer; *p && *p != '#'; p++); if (*p == '#') *p = '\0'; if (std::sscanf(buffer, " %d %d %d %d %d %d ", &minlevel, &leveldiff, &higher_xpgained, &higher_xplost, &lower_xpgained, &lower_xplost) != 6) continue; eventlog(eventlog_level_trace, "ladder_createxptable", "parsed xpcalc leveldiff : {}", leveldiff); if (leveldiff <0) { eventlog(eventlog_level_error, "ladder_createxptable", "got invalid level diff : {}", leveldiff); continue; } if (leveldiff>(w3_xpcalc_maxleveldiff + 1)) { eventlog(eventlog_level_error, __FUNCTION__, "expected entry for leveldiff={} but found {}", w3_xpcalc_maxleveldiff + 1, leveldiff); continue; } w3_xpcalc_maxleveldiff = leveldiff; xpcalc[leveldiff].higher_winxp = higher_xpgained; xpcalc[leveldiff].higher_lossxp = higher_xplost; xpcalc[leveldiff].lower_winxp = lower_xpgained; xpcalc[leveldiff].lower_lossxp = lower_xplost; } std::fclose(fd2); newxpcalc = (t_xpcalc_entry*)xrealloc(xpcalc, sizeof(t_xpcalc_entry)* (w3_xpcalc_maxleveldiff + 1)); xpcalc = newxpcalc; /* OK, now we need to test couse if the user forgot to put some values * lots of profiles could get screwed up */ if (w3_xpcalc_maxleveldiff < 0) { eventlog(eventlog_level_error, __FUNCTION__, "found no valid entries for WAR3 xp calculation"); ladder_destroyxptable(); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "set war3 xpcalc maxleveldiff to {}", w3_xpcalc_maxleveldiff); for (j = 0; j <= w3_xpcalc_maxleveldiff; j++) if (xpcalc[j].higher_winxp == 0 || xpcalc[j].higher_lossxp == 0 || xpcalc[j].lower_winxp == 0 || xpcalc[j].lower_lossxp == 0) { eventlog(eventlog_level_error, __FUNCTION__, "I found 0 for a win/loss XP, please check your config file"); ladder_destroyxptable(); return -1; } for (i = 0; i 0 && xplevels[i].neededxp == 0) || xplevels[i].lossfactor == 0 || (i > 0 && (xplevels[i].startxp <= xplevels[i - 1].startxp || xplevels[i].neededxp < xplevels[i - 1].neededxp))) { eventlog(eventlog_level_error, __FUNCTION__, "I found 0 for a level XP, please check your config file (level: {} neededxp: {} lossfactor: {})", i + 1, xplevels[i].neededxp, xplevels[i].lossfactor); ladder_destroyxptable(); return -1; } return 0; } extern void ladder_destroyxptable() { if (xpcalc != NULL) xfree(xpcalc); if (xplevels != NULL) xfree(xplevels); } extern int war3_get_maxleveldiff() { return w3_xpcalc_maxleveldiff; } extern int ladder_war3_xpdiff(unsigned int winnerlevel, unsigned int looserlevel, int *winxpdiff, int *loosxpdiff) { int diff, absdiff; diff = winnerlevel - looserlevel; absdiff = (diff < 0) ? (-diff) : diff; if (absdiff > w3_xpcalc_maxleveldiff) { eventlog(eventlog_level_error, "ladder_war3_xpdiff", "got invalid level difference : {}", absdiff); return -1; } if (winnerlevel > W3_XPCALC_MAXLEVEL || looserlevel > W3_XPCALC_MAXLEVEL || winnerlevel < 1 || looserlevel < 1) { eventlog(eventlog_level_error, "ladder_war3_xpdiff", "got invalid account levels (win: {} loss: {})", winnerlevel, looserlevel); return -1; } if (winxpdiff == NULL || loosxpdiff == NULL) { eventlog(eventlog_level_error, "ladder_war3_xpdiff", "got NULL winxpdiff, loosxpdiff"); return -1; } /* we return the xp difference for the winner and the looser * we compute that from the xp charts also applying the loss factor for * lower level profiles * FIXME: ?! loss factor doesnt keep the sum of xp won/lost constant * DON'T CARE, cause current win/loss values aren't symetrical any more */ if (diff >= 0) { *winxpdiff = xpcalc[absdiff].higher_winxp; *loosxpdiff = -(xpcalc[absdiff].lower_lossxp * xplevels[looserlevel - 1].lossfactor) / 100; } else { *winxpdiff = xpcalc[absdiff].lower_winxp; *loosxpdiff = -(xpcalc[absdiff].higher_lossxp * xplevels[looserlevel - 1].lossfactor) / 100; } return 0; } extern int ladder_war3_updatelevel(unsigned int oldlevel, int xp) { int i, mylevel; if (oldlevel < 1 || oldlevel > W3_XPCALC_MAXLEVEL) { eventlog(eventlog_level_error, "ladder_war3_updatelevel", "got invalid level: {}", oldlevel); return oldlevel; } if (xp <= 0) return 1; mylevel = oldlevel; for (i = mylevel; i < W3_XPCALC_MAXLEVEL; i++) if (xplevels[i].startxp > xp) { mylevel = i; break; } for (i = mylevel - 1; i > 0; i--) if (xplevels[i - 1].startxp < xp) { mylevel = i + 1; break; } return mylevel; } extern int ladder_war3_get_min_xp(unsigned int Level) { if (Level < 1 || Level > W3_XPCALC_MAXLEVEL) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid Level {}", Level); return -1; } return xplevels[Level - 1].startxp; } /* ********************************************************************************************************************* * start of new ladder codes * *********************************************************************************************************************/ LadderKey::LadderKey(t_ladder_id ladderId_, t_clienttag clienttag_, t_ladder_sort ladderSort_, t_ladder_time ladderTime_) :ladderId(ladderId_), clienttag(clienttag_), ladderSort(ladderSort_), ladderTime(ladderTime_) { } LadderKey::~LadderKey() throw () {} bool LadderKey::operator== (const LadderKey& right) const { return ((ladderId == right.ladderId) && (clienttag == right.clienttag) && (ladderSort == right.ladderSort) && (ladderTime == right.ladderTime)); } bool LadderKey::operator< (const LadderKey& right) const { if (ladderId != right.ladderId) return (ladderId < right.ladderId); else if (clienttag != right.clienttag) return (clienttag < right.clienttag); else if (ladderSort != right.ladderSort) return ladderSort < right.ladderSort; else return (ladderTime < right.ladderTime); } t_ladder_id LadderKey::getLadderId() const { return ladderId; } t_clienttag LadderKey::getClienttag() const { return clienttag; } t_ladder_sort LadderKey::getLadderSort() const { return ladderSort; } t_ladder_time LadderKey::getLadderTime() const { return ladderTime; } LadderKey LadderKey::getOpposingKey() const { if (ladderTime == ladder_time_active) return LadderKey(ladderId, clienttag, ladderSort, ladder_time_current); else if (ladderTime == ladder_time_current) return LadderKey(ladderId, clienttag, ladderSort, ladder_time_active); else return LadderKey(ladderId, clienttag, ladderSort, ladderTime);; } LadderReferencedObject::LadderReferencedObject(t_account *account_) :referenceType(referenceTypeAccount), account(account_), team(NULL) { } LadderReferencedObject::LadderReferencedObject(t_team *team_) : referenceType(referenceTypeTeam), account(NULL), team(team_) { } LadderReferencedObject::~LadderReferencedObject() throw() { } bool LadderReferencedObject::getData(const LadderKey& ladderKey_, unsigned int& uid_, unsigned int& primary_, unsigned int& secondary_, unsigned int& tertiary_) const { // returns false in case of failures - and also when no need to add this referencedObject to ladder t_clienttag clienttag = ladderKey_.getClienttag(); t_ladder_id ladderId = ladderKey_.getLadderId(); if (referenceType == referenceTypeAccount) { uid_ = account_get_uid(account); if (clienttag == CLIENTTAG_WARCRAFT3_UINT || clienttag == CLIENTTAG_WAR3XP_UINT) { if (!(primary_ = account_get_ladder_level(account, clienttag, ladderId))) return false; secondary_ = account_get_ladder_xp(account, clienttag, ladderId); tertiary_ = 0; return true; } else if (tag_check_wolv1(clienttag) || tag_check_wolv2(clienttag)) { if (!(primary_ = account_get_ladder_points(account, clienttag, ladderId))) return false; secondary_ = account_get_ladder_wins(account, clienttag, ladderId); tertiary_ = 0; return true; } else{ t_ladder_sort ladderSort = ladderKey_.getLadderSort(); unsigned int rating, wins, games; // current ladders if (ladderKey_.getLadderTime() == ladder_time_current) { rating = account_get_ladder_rating(account, clienttag, ladderId); if (!rating) return false; wins = account_get_ladder_wins(account, clienttag, ladderId); games = wins + account_get_ladder_losses(account, clienttag, ladderId) + account_get_ladder_disconnects(account, clienttag, ladderId) + account_get_ladder_draws(account, clienttag, ladderId); } else{ // active ladders rating = account_get_ladder_active_rating(account, clienttag, ladderId); if (!rating) return false; wins = account_get_ladder_active_wins(account, clienttag, ladderId); games = wins + account_get_ladder_active_losses(account, clienttag, ladderId) + account_get_ladder_active_disconnects(account, clienttag, ladderId) + account_get_ladder_active_draws(account, clienttag, ladderId); } if (games == 0) return false; unsigned int ratio = (wins << 10) / games; switch (ladderSort) { case ladder_sort_highestrated: primary_ = rating; secondary_ = wins; tertiary_ = ratio; return true; case ladder_sort_mostwins: primary_ = wins; secondary_ = rating; tertiary_ = ratio; return true; case ladder_sort_mostgames: primary_ = games; secondary_ = rating; tertiary_ = ratio; return true; default: return false; } } } else if (referenceType == referenceTypeTeam) { uid_ = team_get_teamid(team); if (!(primary_ = team_get_level(team))) return false; secondary_ = team_get_xp(team); tertiary_ = 0; return true; } return false; } unsigned int LadderReferencedObject::getRank(const LadderKey& ladderKey_) const { t_clienttag clienttag = ladderKey_.getClienttag(); t_ladder_id ladderId = ladderKey_.getLadderId(); t_ladder_time ladderTime = ladderKey_.getLadderTime(); t_ladder_sort ladderSort = ladderKey_.getLadderSort(); if (referenceType == referenceTypeAccount) { if (ladderSort == ladder_sort_default || ladderSort == ladder_sort_highestrated) { if (ladderTime == ladder_time_active) { return account_get_ladder_active_rank(account, clienttag, ladderId); } else{ return account_get_ladder_rank(account, clienttag, ladderId); } } else{ // the account rank is only determined by highestrated/default ladder sort return 0; } } else if (referenceType == referenceTypeTeam) { return team_get_rank(team); } return 0; } bool LadderReferencedObject::setRank(const LadderKey& ladderKey_, unsigned int rank_) const { t_clienttag clienttag = ladderKey_.getClienttag(); t_ladder_id ladderId = ladderKey_.getLadderId(); t_ladder_time ladderTime = ladderKey_.getLadderTime(); t_ladder_sort ladderSort = ladderKey_.getLadderSort(); if (referenceType == referenceTypeAccount) { if (ladderSort == ladder_sort_default || ladderSort == ladder_sort_highestrated) { if (ladderTime == ladder_time_active) { account_set_ladder_active_rank(account, clienttag, ladderId, rank_); } else{ account_set_ladder_rank(account, clienttag, ladderId, rank_); } } else{ // the account rank is only determined by highestrated/default ladder sort return false; } } else if (referenceType == referenceTypeTeam) { team_set_rank(team, rank_); } return true; } void LadderReferencedObject::activate(const LadderKey& ladderKey_) const { if (referenceType == referenceTypeAccount) { if (ladderKey_.getLadderSort() == ladder_sort_highestrated) { t_clienttag clienttag = ladderKey_.getClienttag(); t_ladder_id ladderId = ladderKey_.getLadderId(); char const * timestr; t_bnettime bt; account_set_ladder_active_wins(account, clienttag, ladderId, account_get_ladder_wins(account, clienttag, ladderId)); account_set_ladder_active_losses(account, clienttag, ladderId, account_get_ladder_losses(account, clienttag, ladderId)); account_set_ladder_active_draws(account, clienttag, ladderId, account_get_ladder_draws(account, clienttag, ladderId)); account_set_ladder_active_disconnects(account, clienttag, ladderId, account_get_ladder_disconnects(account, clienttag, ladderId)); account_set_ladder_active_rating(account, clienttag, ladderId, account_get_ladder_rating(account, clienttag, ladderId)); account_set_ladder_active_rank(account, clienttag, ladderId, account_get_ladder_rank(account, clienttag, ladderId)); if (!(timestr = account_get_ladder_last_time(account, clienttag, ladderId))) timestr = BNETD_LADDER_DEFAULT_TIME; bnettime_set_str(&bt, timestr); account_set_ladder_active_last_time(account, clienttag, ladderId, bt); } } } const t_referenceType LadderReferencedObject::getReferenceType() const { return referenceType; } t_account * LadderReferencedObject::getAccount() const { if (referenceType == referenceTypeAccount) return account; else return NULL; } t_team * LadderReferencedObject::getTeam() const { if (referenceType == referenceTypeTeam) return team; else return NULL; } LadderEntry::LadderEntry(unsigned int uid_, unsigned int primary_, unsigned int secondary_, unsigned int tertiary_, LadderReferencedObject referencedObject_) :uid(uid_), primary(primary_), secondary(secondary_), tertiary(tertiary_), rank(0), referencedObject(referencedObject_) { } LadderEntry::~LadderEntry() throw() { } unsigned int LadderEntry::getUid() const { return uid; } unsigned int LadderEntry::getPrimary() const { return primary; } unsigned int LadderEntry::getSecondary() const { return secondary; } unsigned int LadderEntry::getTertiary() const { return tertiary; } unsigned int LadderEntry::getRank() const { return rank; } const LadderReferencedObject& LadderEntry::getReferencedObject() const { return referencedObject; } bool LadderEntry::setRank(unsigned int rank_, const LadderKey& ladderKey_) { if (referencedObject.getRank(ladderKey_) != rank_) { rank = rank_; return referencedObject.setRank(ladderKey_, rank_); } else{ return false; } } void LadderEntry::update(unsigned int primary_, unsigned int secondary_, unsigned int tertiary_) { primary = primary_; secondary = secondary_; tertiary = tertiary_; } std::string LadderEntry::status() const { std::ostringstream result; result << uid << "," << primary << "," << secondary << "," << tertiary; return result.str(); } bool LadderEntry::operator== (const LadderEntry& right) const { return ((uid == right.uid) && (primary == right.primary) && (secondary == right.secondary) && (tertiary == right.tertiary)); } bool LadderEntry::operator< (const LadderEntry& right) const { if (primary != right.primary) return (primary>right.primary); else if (secondary != right.secondary) return (secondary > right.secondary); else if (tertiary != right.tertiary) return (tertiary > right.tertiary); else return (uid > right.uid); } LadderList::LadderList(LadderKey ladderKey_, t_referenceType referenceType_) :ladderKey(ladderKey_), dirty(true), saved(false), referenceType(referenceType_) { ladderFilename = clienttag_uint_to_str(ladderKey_.getClienttag()); ladderFilename += "_"; ladderFilename += bin_ladder_time_str.at(static_cast(ladderKey_.getLadderTime())); ladderFilename += bin_ladder_sort_str.at(static_cast(ladderKey_.getLadderSort())); ladderFilename += bin_ladder_id_str.at(static_cast(ladderKey_.getLadderId())); ladderFilename += "_LADDER"; } LadderList::~LadderList() throw () { } bool LadderList::load() { return loadBinary(); } bool LadderList::save() { sortAndUpdate(); return saveBinary(); } void LadderList::sortAndUpdate() { if (!(dirty)) return; unsigned int rank = 1; unsigned int changed = 0; LList::iterator endMarker = ladder.end(); sort(ladder.begin(), ladder.end()); for (LList::iterator lit(ladder.begin()); lit != ladder.end(); lit++, rank++) { if (rank <= MaxRankKeptInLadder) { if (lit->getRank() != rank) { if (lit->setRank(rank, ladderKey)) changed++; } } else { lit->setRank(0, ladderKey); if (endMarker == ladder.end()) endMarker = lit; } } if (endMarker != ladder.end()) ladder.erase(endMarker, ladder.end()); if ((changed)) eventlog(eventlog_level_trace, __FUNCTION__, "adjusted rank for {} accounts", changed); saved = false; dirty = false; } const unsigned int magick = 0xdeadc0de; inline void LadderList::readdata(std::ifstream &fp, unsigned int &data) { fp.read((char *)(&data), sizeof(unsigned int)); } inline void LadderList::readdata(std::ifstream &fp, unsigned int data[], unsigned int membercount) { fp.read((char *)(data), sizeof(*data)*membercount); } bool LadderList::loadBinary() { std::string filename = prefs_get_ladderdir(); filename += "/"; filename += ladderFilename; std::ifstream fp(filename.c_str(), std::ios::in | std::ios::binary); if (!(fp)) { eventlog(eventlog_level_info, __FUNCTION__, "could not open ladder file \"{}\" - maybe ladder still empty (std::ifstream: {})", filename.c_str(), std::strerror(errno)); return false; } unsigned int checksum = 0; readdata(fp, checksum); if (checksum != magick) { eventlog(eventlog_level_error, __FUNCTION__, "{} not starting with magick", ladderFilename.c_str()); return false; } struct stat sfile; if (stat(filename.c_str(), &sfile) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to retrieve size of {}", ladderFilename.c_str()); return false; } unsigned int filesize = sfile.st_size; if (filesize%sizeof(unsigned int) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "{} has unexpected size of {} bytes (not multiple of sizeof(unsigned int)", ladderFilename.c_str(), filesize); return false; } unsigned int noe = (filesize) / sizeof(unsigned int)-2; if (noe % 4 != 0) { eventlog(eventlog_level_error, __FUNCTION__, "{} has unexpected count of entries ({}) ", ladderFilename.c_str(), noe); return false; } checksum = 0; unsigned int values[4]; unsigned int uid, primary, secondary, tertiary; while (noe >= 4) { readdata(fp, values, 4); noe -= 4; //handle differently dependant on ladderKey->ladderId if (t_account* account = accountlist_find_account_by_uid(values[0])) { uid = values[0]; primary = values[2]; secondary = values[1]; tertiary = values[3]; LadderReferencedObject reference(account); addEntry(uid, primary, secondary, tertiary, reference); } else{ eventlog(eventlog_level_debug, __FUNCTION__, "no known entry for uid {}", values[0]); } for (int count = 0; count < 4; count++) checksum += values[count]; } unsigned int filechecksum = 0; readdata(fp, filechecksum); if (filechecksum != checksum) { eventlog(eventlog_level_error, __FUNCTION__, "{} has invalid checksum... fall back to old loading mode", ladderFilename.c_str()); ladder.clear(); return false; } eventlog(eventlog_level_info, __FUNCTION__, "successfully loaded {}", filename.c_str()); return true; } inline void LadderList::writedata(std::ofstream &fp, unsigned int &data) { fp.write((char *)(&data), sizeof(unsigned int)); } inline void LadderList::writedata(std::ofstream &fp, const unsigned int &data) { fp.write((char *)(&data), sizeof(unsigned int)); } inline void LadderList::writedata(std::ofstream &fp, unsigned int data[], unsigned int membercount) { fp.write((char *)(data), sizeof(*data)*membercount); } bool LadderList::saveBinary() { if (saved) return true; std::string filename = prefs_get_ladderdir(); filename += "/"; filename += ladderFilename; std::ofstream fp(filename.c_str(), std::ios::out | std::ios::binary); if (!(fp)) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for writing (std::ofstream: {})", filename.c_str(), std::strerror(errno)); return false; } writedata(fp, magick); //write the magick int as header unsigned int checksum = 0; unsigned int results[4]; for (LList::const_iterator lit(ladder.begin()); lit != ladder.end(); lit++) { results[0] = lit->getUid(); results[1] = lit->getSecondary(); results[2] = lit->getPrimary(); results[3] = 0; writedata(fp, results, 4); //calculate a checksum over saved data for (int count = 0; count < 4; count++) checksum += results[count]; } writedata(fp, checksum); // add checksum at the en saved = true; eventlog(eventlog_level_info, __FUNCTION__, "successfully saved {}", filename.c_str()); return true; } void LadderList::addEntry(unsigned int uid_, unsigned int primary_, unsigned int secondary_, unsigned int tertiary_, const LadderReferencedObject& referencedObject_) { if (referenceType != referencedObject_.getReferenceType()) { eventlog(eventlog_level_error, __FUNCTION__, "referenceType of LadderList and referencedObject do mismatch"); return; } LadderEntry entry(uid_, primary_, secondary_, tertiary_, referencedObject_); ladder.push_back(entry); dirty = true; } void LadderList::updateEntry(unsigned int uid_, unsigned int primary_, unsigned int secondary_, unsigned int tertiary_, const LadderReferencedObject& referencedObject_) { if (referenceType != referencedObject_.getReferenceType()) { eventlog(eventlog_level_error, __FUNCTION__, "referenceType of LadderList and referencedObject do mismatch"); return; } LList::iterator lit(ladder.begin()); for (; lit != ladder.end() && lit->getUid() != uid_; lit++); if (lit == ladder.end()) { LadderEntry entry(uid_, primary_, secondary_, tertiary_, referencedObject_); ladder.push_back(entry); } else{ lit->update(primary_, secondary_, tertiary_); } dirty = true; } bool LadderList::delEntry(unsigned int uid_) { LList::iterator lit(ladder.begin()); for (; lit != ladder.end() && lit->getUid() != uid_; lit++); if (lit == ladder.end()) return false; //account not on ladder else{ lit->setRank(0, ladderKey); ladder.erase(lit); dirty = true; return true; } } const LadderReferencedObject* LadderList::getReferencedObject(unsigned int rank_) const { if (rank_ > ladder.size()) return 0; else return &ladder[rank_ - 1].getReferencedObject(); } unsigned int LadderList::getRank(unsigned int uid_) const { unsigned int rank; LList::const_iterator lit(ladder.begin()); for (rank = 1; lit != ladder.end() && lit->getUid() != uid_; lit++); if (lit == ladder.end()) return 0; else return lit->getRank(); } const LadderKey& LadderList::getLadderKey() const { return ladderKey; } const t_referenceType LadderList::getReferenceType() const { return referenceType; } void LadderList::activateFrom(const LadderList * currentLadder_) { const LList* currentList = ¤tLadder_->ladder; for (LList::const_iterator lit(currentList->begin()); lit != currentList->end(); lit++) { const LadderReferencedObject& referencedObject = lit->getReferencedObject(); updateEntry(lit->getUid(), lit->getPrimary(), lit->getSecondary(), lit->getTertiary(), referencedObject); referencedObject.activate(ladderKey); } return; } void LadderList::writeStatusfile() const { std::string filename; filename = prefs_get_outputdir(); filename += "/"; filename += ladderFilename; std::ofstream fp(filename.c_str()); if (!(fp)) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for writing (std::ofstream: {})", filename.c_str(), std::strerror(errno)); return; } unsigned int rank = 1; for (LList::const_iterator lit(ladder.begin()); lit != ladder.end(); lit++, rank++) { fp << rank << "," << lit->status() << "\n"; } } Ladders::Ladders() { // WAR3 ladders LadderKey WAR3_solo(ladder_id_solo, CLIENTTAG_WARCRAFT3_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(WAR3_solo, LadderList(WAR3_solo, referenceTypeAccount))); LadderKey WAR3_team(ladder_id_team, CLIENTTAG_WARCRAFT3_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(WAR3_team, LadderList(WAR3_team, referenceTypeAccount))); LadderKey WAR3_ffa(ladder_id_ffa, CLIENTTAG_WARCRAFT3_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(WAR3_ffa, LadderList(WAR3_ffa, referenceTypeAccount))); LadderKey WAR3_ateam(ladder_id_ateam, CLIENTTAG_WARCRAFT3_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(WAR3_ateam, LadderList(WAR3_ateam, referenceTypeTeam))); //W3XP ladders LadderKey W3XP_solo(ladder_id_solo, CLIENTTAG_WAR3XP_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(W3XP_solo, LadderList(W3XP_solo, referenceTypeAccount))); LadderKey W3XP_team(ladder_id_team, CLIENTTAG_WAR3XP_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(W3XP_team, LadderList(W3XP_team, referenceTypeAccount))); LadderKey W3XP_ffa(ladder_id_ffa, CLIENTTAG_WAR3XP_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(W3XP_ffa, LadderList(W3XP_ffa, referenceTypeAccount))); LadderKey W3XP_ateam(ladder_id_ateam, CLIENTTAG_WAR3XP_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(W3XP_ateam, LadderList(W3XP_ateam, referenceTypeTeam))); //STAR ladders LadderKey STAR_ar(ladder_id_normal, CLIENTTAG_STARCRAFT_UINT, ladder_sort_highestrated, ladder_time_active); ladderMap.insert(std::make_pair(STAR_ar, LadderList(STAR_ar, referenceTypeAccount))); LadderKey STAR_aw(ladder_id_normal, CLIENTTAG_STARCRAFT_UINT, ladder_sort_mostwins, ladder_time_active); ladderMap.insert(std::make_pair(STAR_aw, LadderList(STAR_aw, referenceTypeAccount))); LadderKey STAR_ag(ladder_id_normal, CLIENTTAG_STARCRAFT_UINT, ladder_sort_mostgames, ladder_time_active); ladderMap.insert(std::make_pair(STAR_ag, LadderList(STAR_ag, referenceTypeAccount))); LadderKey STAR_cr(ladder_id_normal, CLIENTTAG_STARCRAFT_UINT, ladder_sort_highestrated, ladder_time_current); ladderMap.insert(std::make_pair(STAR_cr, LadderList(STAR_cr, referenceTypeAccount))); LadderKey STAR_cw(ladder_id_normal, CLIENTTAG_STARCRAFT_UINT, ladder_sort_mostwins, ladder_time_current); ladderMap.insert(std::make_pair(STAR_cw, LadderList(STAR_cw, referenceTypeAccount))); LadderKey STAR_cg(ladder_id_normal, CLIENTTAG_STARCRAFT_UINT, ladder_sort_mostgames, ladder_time_current); ladderMap.insert(std::make_pair(STAR_cg, LadderList(STAR_cg, referenceTypeAccount))); //SEXP ladders LadderKey SEXP_ar(ladder_id_normal, CLIENTTAG_BROODWARS_UINT, ladder_sort_highestrated, ladder_time_active); ladderMap.insert(std::make_pair(SEXP_ar, LadderList(SEXP_ar, referenceTypeAccount))); LadderKey SEXP_aw(ladder_id_normal, CLIENTTAG_BROODWARS_UINT, ladder_sort_mostwins, ladder_time_active); ladderMap.insert(std::make_pair(SEXP_aw, LadderList(SEXP_aw, referenceTypeAccount))); LadderKey SEXP_ag(ladder_id_normal, CLIENTTAG_BROODWARS_UINT, ladder_sort_mostgames, ladder_time_active); ladderMap.insert(std::make_pair(SEXP_ag, LadderList(SEXP_ag, referenceTypeAccount))); LadderKey SEXP_cr(ladder_id_normal, CLIENTTAG_BROODWARS_UINT, ladder_sort_highestrated, ladder_time_current); ladderMap.insert(std::make_pair(SEXP_cr, LadderList(SEXP_cr, referenceTypeAccount))); LadderKey SEXP_cw(ladder_id_normal, CLIENTTAG_BROODWARS_UINT, ladder_sort_mostwins, ladder_time_current); ladderMap.insert(std::make_pair(SEXP_cw, LadderList(SEXP_cw, referenceTypeAccount))); LadderKey SEXP_cg(ladder_id_normal, CLIENTTAG_BROODWARS_UINT, ladder_sort_mostgames, ladder_time_current); ladderMap.insert(std::make_pair(SEXP_cg, LadderList(SEXP_cg, referenceTypeAccount))); //W2BN ladders LadderKey W2BN_ar(ladder_id_normal, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_highestrated, ladder_time_active); ladderMap.insert(std::make_pair(W2BN_ar, LadderList(W2BN_ar, referenceTypeAccount))); LadderKey W2BN_aw(ladder_id_normal, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostwins, ladder_time_active); ladderMap.insert(std::make_pair(W2BN_aw, LadderList(W2BN_aw, referenceTypeAccount))); LadderKey W2BN_ag(ladder_id_normal, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostgames, ladder_time_active); ladderMap.insert(std::make_pair(W2BN_ag, LadderList(W2BN_ag, referenceTypeAccount))); LadderKey W2BN_cr(ladder_id_normal, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_highestrated, ladder_time_current); ladderMap.insert(std::make_pair(W2BN_cr, LadderList(W2BN_cr, referenceTypeAccount))); LadderKey W2BN_cw(ladder_id_normal, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostwins, ladder_time_current); ladderMap.insert(std::make_pair(W2BN_cw, LadderList(W2BN_cw, referenceTypeAccount))); LadderKey W2BN_cg(ladder_id_normal, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostgames, ladder_time_current); ladderMap.insert(std::make_pair(W2BN_cg, LadderList(W2BN_cg, referenceTypeAccount))); LadderKey W2BN_ari(ladder_id_ironman, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_highestrated, ladder_time_active); ladderMap.insert(std::make_pair(W2BN_ari, LadderList(W2BN_ari, referenceTypeAccount))); LadderKey W2BN_awi(ladder_id_ironman, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostwins, ladder_time_active); ladderMap.insert(std::make_pair(W2BN_awi, LadderList(W2BN_awi, referenceTypeAccount))); LadderKey W2BN_agi(ladder_id_ironman, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostgames, ladder_time_active); ladderMap.insert(std::make_pair(W2BN_agi, LadderList(W2BN_agi, referenceTypeAccount))); LadderKey W2BN_cri(ladder_id_ironman, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_highestrated, ladder_time_current); ladderMap.insert(std::make_pair(W2BN_cri, LadderList(W2BN_cri, referenceTypeAccount))); LadderKey W2BN_cwi(ladder_id_ironman, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostwins, ladder_time_current); ladderMap.insert(std::make_pair(W2BN_cwi, LadderList(W2BN_cwi, referenceTypeAccount))); LadderKey W2BN_cgi(ladder_id_ironman, CLIENTTAG_WARCIIBNE_UINT, ladder_sort_mostgames, ladder_time_current); ladderMap.insert(std::make_pair(W2BN_cgi, LadderList(W2BN_cgi, referenceTypeAccount))); //TSUN ladders LadderKey TSUN_solo(ladder_id_solo, CLIENTTAG_TIBERNSUN_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(TSUN_solo, LadderList(TSUN_solo, referenceTypeAccount))); //TSXP ladders LadderKey TSXP_solo(ladder_id_solo, CLIENTTAG_TIBSUNXP_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(TSXP_solo, LadderList(TSXP_solo, referenceTypeAccount))); //RAL2 ladders LadderKey RAL2_solo(ladder_id_solo, CLIENTTAG_REDALERT2_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(RAL2_solo, LadderList(RAL2_solo, referenceTypeAccount))); //YURI ladders LadderKey YURI_solo(ladder_id_solo, CLIENTTAG_YURISREV_UINT, ladder_sort_default, ladder_time_default); ladderMap.insert(std::make_pair(YURI_solo, LadderList(YURI_solo, referenceTypeAccount))); } Ladders::~Ladders() throw () { } LadderList* Ladders::getLadderList(const LadderKey& ladderKey_) { KeyLadderMap::iterator kit(ladderMap.begin()); for (; kit != ladderMap.end() && (!(ladderKey_ == (*kit).first)); kit++); if (kit == ladderMap.end()) { eventlog(eventlog_level_error, __FUNCTION__, "found no matching ladderlist"); return NULL; } else{ return &(*kit).second; } } void Ladders::load() { std::list laddersToRebuild; for (KeyLadderMap::iterator kit(ladderMap.begin()); kit != ladderMap.end(); kit++) { if (!kit->second.load()) { //loading failed, add to rebuild list laddersToRebuild.push_back(&kit->second); } } if (!laddersToRebuild.empty()) rebuild(laddersToRebuild); update(); save(); } void Ladders::rebuild(std::list& laddersToRebuild) { t_entry * curr; t_account * account; unsigned int uid, primary, secondary, tertiary; eventlog(eventlog_level_debug, __FUNCTION__, "start rebuilding ladders"); if (accountlist_load_all(ST_FORCE)) { eventlog(eventlog_level_error, __FUNCTION__, "error loading all accounts"); return; } HASHTABLE_TRAVERSE(accountlist(), curr) { if ((account = ((t_account *)entry_get_data(curr)))) { LadderReferencedObject referencedObject(account); for (std::list::iterator lit(laddersToRebuild.begin()); lit != laddersToRebuild.end(); lit++) { // only do handle referenceTypeAccount ladders here if ((*lit)->getReferenceType() != referenceTypeAccount) { continue; } if (referencedObject.getData((*lit)->getLadderKey(), uid, primary, secondary, tertiary)) { (*lit)->addEntry(uid, primary, secondary, tertiary, referencedObject); } } } } // now we would need to traverse teamlist, too. // how about comletly moving this code into team? eventlog(eventlog_level_debug, __FUNCTION__, "done rebuilding ladders"); } void Ladders::update() { for (KeyLadderMap::iterator kit(ladderMap.begin()); kit != ladderMap.end(); kit++) { kit->second.sortAndUpdate(); } } void Ladders::activate() { for (KeyLadderMap::iterator kit(ladderMap.begin()); kit != ladderMap.end(); kit++) { if (kit->second.getLadderKey().getLadderTime() == ladder_time_active) { LadderList* activeLadder = &kit->second; LadderList* currentLadder = getLadderList(activeLadder->getLadderKey().getOpposingKey()); if (currentLadder == NULL){ eventlog(eventlog_level_error, __FUNCTION__, "found active ladder, but no matching current ladder"); continue; } activeLadder->activateFrom(currentLadder); } } update(); } void Ladders::save() { for (KeyLadderMap::iterator kit(ladderMap.begin()); kit != ladderMap.end(); kit++) { kit->second.save(); } } void Ladders::status() const { for (KeyLadderMap::const_iterator kit(ladderMap.begin()); kit != ladderMap.end(); kit++) { kit->second.writeStatusfile(); } } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/ladder.h000066400000000000000000000170731462640461400174770ustar00rootroot00000000000000/* * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_LADDER_TYPES #define INCLUDED_LADDER_TYPES #include "account.h" #include "common/tag.h" #include #include #include #include #include #define W3_XPCALC_MAXLEVEL 65 namespace pvpgn { namespace bnetd { typedef enum { ladder_sort_highestrated, ladder_sort_mostwins, ladder_sort_mostgames, ladder_sort_default } t_ladder_sort; extern const std::vector bin_ladder_sort_str; typedef enum { ladder_time_active, ladder_time_current, ladder_time_default } t_ladder_time; extern const std::vector bin_ladder_time_str; typedef enum { ladder_id_none = 0, ladder_id_normal = 1, ladder_id_ironman = 3, ladder_id_solo = 5, ladder_id_team = 6, ladder_id_ffa = 7, ladder_id_ateam = 8 } t_ladder_id; const extern std::vector ladder_id_str; const extern std::vector bin_ladder_id_str; typedef enum { ladder_option_none = 0, ladder_option_disconnectisloss = 1 } t_ladder_option; typedef enum { referenceTypeAccount, referenceTypeTeam, referenceTypeClan } t_referenceType; class LadderKey { public: LadderKey(t_ladder_id ladderId_, t_clienttag clienttag_, t_ladder_sort ladderSort_, t_ladder_time ladderTime_); ~LadderKey() throw (); bool operator== (const LadderKey& right) const; bool operator< (const LadderKey& right) const; t_ladder_id getLadderId() const; t_clienttag getClienttag() const; t_ladder_sort getLadderSort() const; t_ladder_time getLadderTime() const; LadderKey getOpposingKey() const; private: t_ladder_id ladderId; t_clienttag clienttag; t_ladder_sort ladderSort; t_ladder_time ladderTime; }; class LadderReferencedObject { public: explicit LadderReferencedObject(t_account *account_); explicit LadderReferencedObject(t_team *team_); ~LadderReferencedObject() throw (); bool getData(const LadderKey& ladderKey_, unsigned int& uid, unsigned int& primary_, unsigned int& secondary_, unsigned int& tertiary_) const; unsigned int getRank(const LadderKey& ladderKey_) const; bool setRank(const LadderKey& ladderKey_, unsigned int rank_) const; t_account* getAccount() const; t_team* getTeam() const; void activate(const LadderKey& ladderKey_) const; const t_referenceType getReferenceType() const; private: t_referenceType referenceType; t_account * account; t_team * team; }; class LadderEntry { public: LadderEntry(unsigned int uid_, unsigned int primary_, unsigned int secondary_, unsigned int tertiary_, LadderReferencedObject referencedObject_); ~LadderEntry() throw (); unsigned int getUid() const; unsigned int getPrimary() const; unsigned int getSecondary() const; unsigned int getTertiary() const; unsigned int getRank() const; const LadderReferencedObject& getReferencedObject() const; bool setRank(unsigned int rank_, const LadderKey& ladderKey_); void update(unsigned int primary_, unsigned int secondary_, unsigned int tertiary_); std::string status() const; bool operator== (const LadderEntry& right) const; bool operator< (const LadderEntry& right) const; private: unsigned int uid; unsigned int primary; unsigned int secondary; unsigned int tertiary; unsigned int rank; LadderReferencedObject referencedObject; }; class LadderList { public: explicit LadderList(LadderKey ladderkey_, t_referenceType referenceType_); ~LadderList() throw (); bool load(); bool save(); void addEntry(unsigned int uid_, unsigned int primary_, unsigned int secondary_, unsigned int tertiary_, const LadderReferencedObject& referencedObject_); void updateEntry(unsigned int uid_, unsigned int primary_, unsigned int secondary_, unsigned int tertiary_, const LadderReferencedObject& referencedObject_); bool delEntry(unsigned int uid_); const LadderKey& getLadderKey() const; const t_referenceType getReferenceType() const; void sortAndUpdate(); const LadderReferencedObject* getReferencedObject(unsigned int rank) const; unsigned int getRank(unsigned int uid_) const; void activateFrom(const LadderList * currentLadder_); void writeStatusfile() const; private: typedef std::vector LList; LadderKey ladderKey; LList ladder; bool dirty; bool saved; std::string ladderFilename; t_referenceType referenceType; bool loadBinary(); bool saveBinary(); void readdata(std::ifstream &fp, unsigned int &data); void readdata(std::ifstream &fp, unsigned int data[], unsigned int membercount); void writedata(std::ofstream &fp, unsigned int &data); void writedata(std::ofstream &fp, const unsigned int &data); void writedata(std::ofstream &fp, unsigned int data[], unsigned int membercount); }; class Ladders { public: Ladders(); ~Ladders() throw (); LadderList* getLadderList(const LadderKey& ladderKey_); void load(); void update(); void activate(); void save(); void status() const; private: void rebuild(std::list& laddersToRebuild); typedef std::map KeyLadderMap; KeyLadderMap ladderMap; }; extern Ladders ladders; #ifdef LADDER_INTERNAL_ACCESS typedef struct { int startxp, neededxp, lossfactor, mingames; } t_xplevel_entry; typedef struct { int higher_winxp, higher_lossxp, lower_winxp, lower_lossxp; } t_xpcalc_entry; #endif } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_LADDER_PROTOS #define INCLUDED_LADDER_PROTOS #define JUST_NEED_TYPES #include "account.h" #include "game.h" #include "ladder_calc.h" #include "common/tag.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int ladder_update(t_clienttag clienttag, t_ladder_id id, unsigned int count, t_account * * players, t_game_result * results, t_ladder_info * info); extern int ladder_update_wol(t_clienttag clienttag, t_ladder_id id, t_account * * players, t_game_result * results); extern int ladder_init_account(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int ladder_init_account_wol(t_account * account, t_clienttag clienttag, t_ladder_id id); extern int ladder_check_map(char const * mapname, t_game_maptype maptype, t_clienttag clienttag); extern int ladder_createxptable(const char *xplevelfile, const char *xpcalcfile); extern void ladder_destroyxptable(void); extern int ladder_war3_xpdiff(unsigned int winnerlevel, unsigned int looserlevel, int *, int *); extern int ladder_war3_updatelevel(unsigned int oldlevel, int xp); extern int ladder_war3_get_min_xp(unsigned int level); extern int war3_get_maxleveldiff(void); extern void ladder_reload_conf(void); /* reloads relevant parameters from bnetd.conf (xml/std mode for ladder) */ } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/ladder_calc.cpp000066400000000000000000000415711462640461400210140ustar00rootroot00000000000000/* * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999,2000 D.Moreaux (vapula@linuxbe.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "ladder_calc.h" #include #include "common/eventlog.h" #include "account.h" #include "account_wrap.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static double probability(unsigned int a, unsigned int b); static int coefficient(t_account * account, t_clienttag clienttag, t_ladder_id id); static double two_player(unsigned int *rating); static double three_player(unsigned int *rating); static double four_player(unsigned int *rating); static double five_player(unsigned int *rating); static double five_f1(int a, int b, int c, int d, int e); static double five_f2(int a, int b, int c); static double six_player(unsigned int *rating); static double six_f1(int a, int b, int c, int d, int e, int f); static double six_f2(int a, int b, int c, int d, int e, int f); static double six_f3(int a, int b, int c, int d); static double seven_player(unsigned int *rating); static double seven_f1(int a, int b, int c, int d, int e, int f, int g); static double seven_f2(int a, int b, int c, int d, int e, int f, int g); static double eight_player(unsigned int *rating); static double eight_f1(int a, int b, int c, int d, int e, int f, int g); static double eight_f2(int a, int b, int c, int d, int e, int f, int g); static double eight_f3(int a, int b, int c, int d, int e); /* * Compute probability of winning using the Elo system * * The formula is: * * D = rating(player a) - rating(player b) * * 1 * Pwin(D) = ------------------ * -(D / 400) * 1 + 10 */ static double probability(unsigned int a, unsigned int b) { double i, j; i = (((double)a) - ((double)b)) / 400.0; j = std::pow(10.0, -i); return (1.0 / (1.0 + j)); } /* * This is the coefficient k which is meant to enhance the * effect of the Elo std::system where more experienced players * will gain fewer points when playing against newbies, and * newbies will gain massive points if they win against an * experienced player. It also helps stabilize a player's * rating after they have played 30 games or so. * * K=50 for new players * K=30 for players who have played 30 or more ladder games * K=20 for players who have attained a rating of 2400 or higher */ static int coefficient(t_account * account, t_clienttag clienttag, t_ladder_id id) { int const total_ladder_games = account_get_ladder_wins(account, clienttag, id) + account_get_ladder_losses(account, clienttag, id) + account_get_ladder_disconnects(account, clienttag, id); if (total_ladder_games < 30) return 50; if (account_get_ladder_rating(account, clienttag, id) < 2400) return 30; return 20; } /* * The Elo std::system only handles 2 players, these functions extend * the calculation to different numbers of players as if they were * in a tournament. It turns out the math for this is really ugly, * so we have hardcoded the equations for every number of players. */ static double two_player(unsigned int *rating) { unsigned int a, b; double ab; a = rating[0]; b = rating[1]; ab = probability(a, b); return ab; } static double three_player(unsigned int *rating) { unsigned int a, b, c; double ab, ac, bc, cb; a = rating[0]; b = rating[1]; c = rating[2]; ab = probability(a, b); ac = probability(a, c); bc = probability(b, c); cb = 1.0 - bc; return (2 * (ab*ac) + (bc*ab) + (cb*ac)) / 3; } static double four_player(unsigned int *rating) { unsigned int a, b, c, d; double ab, ac, ad, bc, bd, cb, cd, db, dc; a = rating[0]; b = rating[1]; c = rating[2]; d = rating[3]; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); bc = probability(b, c); bd = probability(b, d); cd = probability(c, d); cb = 1.0 - bc; db = 1.0 - bd; dc = 1.0 - cd; return (ab*ac*(cd + bd) + ac*ad*(db + cb) + ab*ad*(dc + bc)) / 3; } /* [Denis MOREAUX , 10 Apr 2000] * * C D E A D E The winner may be in the * A B C E B C A E 2 players or the 3 players * A C B A group. In either case, a * A A second player must be choosen * to be either the one playing * against A in the 2-players subtree or being the winner * of the 2-player subtree if A is in the 3-players subtree. */ static double five_player(unsigned int *rating) { unsigned int a, b, c, d, e; a = rating[0]; b = rating[1]; c = rating[2]; d = rating[3]; e = rating[4]; return (five_f1(a, b, c, d, e) + five_f1(a, c, d, e, b) + five_f1(a, d, e, b, c) + five_f1(a, e, b, c, d)) / 30; } /* [Denis MOREAUX , 10 Apr 2000 * * Two cases to treat : AB-CDE and BC-ADE. * in both cases, A win against B. * In the first case, A win over the winner of a 3-players game * (3 possible winners). * In the second case, B win over one of the three other and A is in * the 3-players game. */ static double five_f1(int a, int b, int c, int d, int e) { double ab, ac, ad, ae, bc, bd, be; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); ae = probability(a, e); bc = probability(b, c); bd = probability(b, d); be = probability(b, e); return ab*(ac*five_f2(c, d, e) + ad*five_f2(d, e, c) + ae*five_f2(e, c, d) + bc*five_f2(a, d, e) + bd*five_f2(a, c, e) + be*five_f2(a, c, d)); } static double five_f2(int a, int b, int c) { double ab, ac, bc, cb; ab = probability(a, b); ac = probability(a, c); bc = probability(b, c); cb = 1.0 - bc; return (2 * (ab*ac) + bc*ab + cb*ac); } static double six_player(unsigned int *rating) { unsigned int a, b, c, d, e, f; a = rating[0]; b = rating[1]; c = rating[2]; d = rating[3]; e = rating[4]; f = rating[5]; /* A B C D * A C E F * A E * A */ return (six_f1(a, b, c, d, e, f) + /* A is in group of 4 */ six_f1(a, b, c, e, d, f) + six_f1(a, b, e, d, c, f) + six_f1(a, e, c, d, b, f) + six_f1(a, b, c, f, d, e) + six_f1(a, b, f, d, c, e) + six_f1(a, f, c, d, b, e) + six_f1(a, e, f, b, c, d) + six_f1(a, e, f, c, b, d) + six_f1(a, e, f, d, b, c) + six_f2(a, b, c, d, e, f) + /* A is in group of 2 */ six_f2(a, c, b, d, e, f) + six_f2(a, d, b, c, e, f) + six_f2(a, e, b, c, d, f) + six_f2(a, f, b, c, d, e)) / 45; } /* ABCD = group of 4, EF = group of 2, A must win */ /* D.Moreaux, 10 Apr 2000: changed double to int for the parameters */ static double six_f1(int a, int b, int c, int d, int e, int f) { double ab, ac, ad, bc, bd, cb, cd, db, dc, ef, fe, ae, af; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); ae = probability(a, e); af = probability(a, f); bc = probability(b, c); bd = probability(b, d); cd = probability(c, d); ef = probability(e, f); cb = 1.0 - bc; db = 1.0 - bd; dc = 1.0 - cd; fe = 1.0 - ef; return (ab*ac*(cd + bd) + ac*ad*(db + cb) + ab*ad*(dc + bc))*(ef*ae + fe*af); } /* AB is group of 2, CDEF is group of 4, A must win */ static double six_f2(int a, int b, int c, int d, int e, int f) { double ab, ac, ad, ae, af; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); ae = probability(a, e); af = probability(a, f); return (six_f3(c, d, e, f)*ab*ac + six_f3(d, c, e, f)*ab*ad + six_f3(e, c, d, f)*ab*ae + six_f3(f, c, d, e)*ab*af); } /* ABCD is group of 4, A win */ static double six_f3(int a, int b, int c, int d) { double ab, ac, ad, bc, bd, cb, cd, db, dc; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); bc = probability(b, c); bd = probability(b, d); cd = probability(c, d); cb = 1.0 - bc; db = 1.0 - bd; dc = 1.0 - cd; return (ab*ac*(cd + bd) + ac*ad*(db + cb) + ab*ad*(dc + bc)); } static double seven_player(unsigned int *rating) { unsigned int a, b, c, d, e, f, g; a = rating[0]; b = rating[1]; c = rating[2]; d = rating[3]; e = rating[4]; f = rating[5]; g = rating[6]; return (seven_f1(a, b, c, d, e, f, g) + seven_f1(a, c, b, d, e, f, g) + seven_f1(a, d, c, b, e, f, g) + seven_f1(a, e, c, d, b, f, g) + seven_f1(a, f, c, d, e, b, g) + seven_f1(a, g, c, d, e, f, b)) / 45; } static double seven_f1(int a, int b, int c, int d, int e, int f, int g) { return seven_f2(a, b, c, d, e, f, g) + seven_f2(a, b, d, c, e, f, g) + seven_f2(a, b, e, d, c, f, g) + seven_f2(a, b, f, d, e, c, g) + seven_f2(a, b, g, d, e, f, c); } static double seven_f2(int a, int b, int c, int d, int e, int f, int g) { double ab, ac, ad, ae, af, ag, bc, bd, be, bf, bg, cd, ce, cf, cg; double de, df, dg, ed, ef, eg, fd, fe, fg, gd, ge, gf; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); ae = probability(a, e); af = probability(a, f); ag = probability(a, g); bc = probability(b, c); bd = probability(b, d); be = probability(b, e); bf = probability(b, f); bg = probability(b, g); cd = probability(c, d); ce = probability(c, e); cf = probability(c, f); cg = probability(c, g); de = probability(d, e); df = probability(d, f); dg = probability(d, g); ef = probability(e, f); eg = probability(e, g); fg = probability(f, g); ed = 1.0 - de; fd = 1.0 - df; gd = 1.0 - dg; fe = 1.0 - ef; ge = 1.0 - eg; gf = 1.0 - fg; return ab*( (ac + bc)* (ad*(de*df*(fg + eg) + df*dg*(ge + fe) + de*dg*(gf + ef)) + /* 4:d win */ ae*(ed*ef*(fg + dg) + ef*eg*(gd + fd) + ed*eg*(gf + df)) + /* 4:e win */ af*(fe*fd*(dg + eg) + fd*fg*(ge + de) + fe*fg*(gd + ed)) + /* 4:f win */ ag*(ge*gf*(fd + ed) + gf*gd*(de + fe) + ge*gd*(df + ef))) + /* 4:g win */ bc* ((bd + cd)*(ae*af*(fg + eg) + af*ag*(ge + fe) + ae*ag*(gf + ef)) + /* 3:d */ (be + ce)*(ad*af*(fg + dg) + af*ag*(ad + ad) + ad*ag*(gf + df)) + /* 3:e */ (bf + cf)*(ae*ad*(dg + eg) + ad*ag*(ge + de) + ae*ag*(gd + ed)) + /* 3:f */ (bg + cg)*(ae*af*(fd + ed) + af*ad*(de + fe) + ae*ad*(df + ef)))); /* 3:g */ } static double eight_player(unsigned int *rating) { unsigned int a, b, c, d, e, f, g, h; double ab, ac, ad, ae, af, ag, ah; a = rating[0]; b = rating[1]; c = rating[2]; d = rating[3]; e = rating[4]; f = rating[5]; g = rating[6]; h = rating[7]; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); ae = probability(a, e); af = probability(a, f); ag = probability(a, g); ah = probability(a, h); /* First against A may be one from seven */ return (eight_f1(a, c, d, e, f, g, h)*ab + eight_f1(a, b, d, e, f, g, h)*ac + eight_f1(a, b, c, e, f, g, h)*ad + eight_f1(a, b, c, d, f, g, h)*ae + eight_f1(a, b, c, d, e, g, h)*af + eight_f1(a, b, c, d, e, f, h)*ag + eight_f1(a, b, c, d, e, f, g)*ah) / 315; } static double eight_f1(int a, int b, int c, int d, int e, int f, int g) { /* The winner of the second group, who'll then play against A, may be one from six possible players */ return eight_f2(a, b, c, d, e, f, g) + eight_f2(a, c, b, d, e, f, g) + eight_f2(a, d, b, c, e, f, g) + eight_f2(a, e, b, c, d, f, g) + eight_f2(a, f, b, c, d, e, g) + eight_f2(a, g, b, c, d, e, f); } static double eight_f2(int a, int b, int c, int d, int e, int f, int g) { double ab, bc, bd, be, bf, bg; ab = probability(a, b); bc = probability(b, c); bd = probability(b, d); be = probability(b, e); bf = probability(b, f); bg = probability(b, g); /* There are 5 player who may play against the 3rd. The third (b) will win over them and lose against a */ return ab*(eight_f3(a, d, e, f, g)*bc + eight_f3(a, c, e, f, g)*bd + eight_f3(a, c, d, f, g)*be + eight_f3(a, c, d, e, g)*bf + eight_f3(a, c, d, e, f)*bg); } /* D.Moreaux, 10 Apr 2000: changed double to int for the parameters */ static double eight_f3(int a, int b, int c, int d, int e) { double ab, ac, ad, ae, bc, bd, be, cb, cd, ce, db, dc, de, eb, ec, ed; ab = probability(a, b); ac = probability(a, c); ad = probability(a, d); ae = probability(a, e); bc = probability(b, c); bd = probability(b, d); be = probability(b, e); cd = probability(c, d); ce = probability(c, e); de = probability(d, e); cb = 1.0 - bc; db = 1.0 - bd; dc = 1.0 - cd; eb = 1.0 - be; ec = 1.0 - ce; ed = 1.0 - de; /* expansion then factorisation (this function is called 210 times) * gain 4 func_call * 24 * * 30 probability */ return (bc*de + be*dc)*((ab - ad)*bd + ad) + (bd*ce + be*cd)*((ab - ac)*bc + ac) + (cb*de + db*ce)*((ac - ad)*cd + ad) + (cd*eb + cb*ed)*((ac - ae)*ce + ae) + (dc*eb + db*ec)*((ad - ae)*de + ae) + (bd*ec + bc*ed)*((ae - ab)*eb + ab); } /* Determine changes in ratings due to game results. */ extern int ladder_calc_info(t_clienttag clienttag, t_ladder_id id, unsigned int count, t_account * * players, t_game_result * results, t_ladder_info * info) { unsigned int curr; unsigned int *rating; unsigned int *sorted; if (!players) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL players"); return -1; } if (!results) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL results"); return -1; } if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got bad clienttag"); return -1; } if (!info) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL info"); return -1; } rating = (unsigned int*)xmalloc(sizeof(unsigned int)*count); sorted = (unsigned int*)xmalloc(sizeof(unsigned int)*count); for (curr = 0; curr < count; curr++) rating[curr] = account_get_ladder_rating(players[curr], clienttag, id); for (curr = 0; curr < count; curr++) { double k; double prob; double delta; t_game_result myresult; unsigned int opponent_count; unsigned int team_members; k = coefficient(players[curr], clienttag, id); opponent_count = 0; myresult = results[curr]; { unsigned int i, j; /* Put the current user into slot 0, his opponents into other slots order is not important for the other players */ for (i = 0, j = 1; i < count; i++) if (i == curr) sorted[0] = rating[i]; else { if (results[i] != myresult) { sorted[j++] = rating[i]; opponent_count++; } } } team_members = count - opponent_count; switch (opponent_count) { case 1: prob = two_player(sorted); break; case 2: prob = three_player(sorted); break; case 3: prob = four_player(sorted); break; case 4: prob = five_player(sorted); break; case 5: prob = six_player(sorted); break; case 6: prob = seven_player(sorted); break; case 7: prob = eight_player(sorted); break; default: eventlog(eventlog_level_error, __FUNCTION__, "sorry, unsupported number of ladder opponents ({})", opponent_count); xfree((void *)rating); xfree((void *)sorted); return -1; } if (results[curr] == game_result_win) delta = std::fabs(k * (1.0 - prob) / team_members); /* better the chance of winning -> fewer points added */ else delta = -std::fabs(k * prob); /* better the chance of winning -> more points subtracted */ eventlog(eventlog_level_debug, __FUNCTION__, "computed probability={}, k={}, deltar={:+g}", prob, k, delta); info[curr].prob = prob; info[curr].k = (unsigned int)k; info[curr].adj = (int)delta; info[curr].oldrating = rating[curr]; info[curr].oldrank = account_get_ladder_rank(players[curr], clienttag, id); } xfree((void *)rating); xfree((void *)sorted); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/ladder_calc.h000066400000000000000000000030571462640461400204560ustar00rootroot00000000000000/* * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_LADDER_CALC_TYPES #define INCLUDED_LADDER_CALC_TYPES namespace pvpgn { namespace bnetd { typedef struct { double prob; unsigned int k; int adj; unsigned int oldrating; unsigned int oldrank; } t_ladder_info; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_LADDER_CALC_PROTOS #define INCLUDED_LADDER_CALC_PROTOS #define JUST_NEED_TYPES #include "ladder.h" #include "account.h" #include "game.h" #include "common/tag.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int ladder_calc_info(t_clienttag clienttag, t_ladder_id id, unsigned int count, t_account * * players, t_game_result * results, t_ladder_info * info); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/luafunctions.cpp000066400000000000000000000451201462640461400213030ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WITH_LUA #include "common/setup_before.h" #define GAME_INTERNAL_ACCESS #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/tag.h" #include "common/util.h" #include "common/eventlog.h" #include "common/list.h" #include "common/hashtable.h" #include "common/xstring.h" #include "connection.h" #include "message.h" #include "channel.h" #include "game.h" #include "account.h" #include "account_wrap.h" #include "timer.h" #include "ipban.h" #include "command_groups.h" #include "friends.h" #include "clan.h" #include "attrlayer.h" #include "icons.h" #include "helpfile.h" #include "i18n.h" #include "luawrapper.h" #include "luaobjects.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef enum { attr_type_str, attr_type_num, attr_type_bool, attr_type_raw } t_attr_type; /* Send message text to user */ extern int __message_send_text(lua_State* L) { const char *text; const char *username_src, *username_dst; int message_type; t_connection *c_src = NULL, *c_dst = NULL; try { lua::stack st(L); // get args st.at(1, username_dst); st.at(2, message_type); st.at(3, username_src); st.at(4, text); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } // get user connections if (t_account * account = accountlist_find_account(username_dst)) c_dst = account_get_conn(account); if (username_src) if (t_account * account = accountlist_find_account(username_src)) c_src = account_get_conn(account); // send message if (c_dst) { // src connection can be NULL if message_type == message_type_whisper (message will be sent from the server name) message_send_text(c_dst, (t_message_type)message_type, c_src, text); } return 0; } /* Log text into logfile */ extern int __eventlog(lua_State* L) { int loglevel; const char *text, *function; try { lua::stack st(L); // get args st.at(1, loglevel); st.at(2, function); st.at(3, text); eventlog(t_eventlog_level(loglevel), function, "{}", text); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 0; } /* Get one account table object */ extern int __account_get_by_name(lua_State* L) { const char *username; std::map o_account; try { lua::stack st(L); // get args st.at(1, username); o_account = get_account_object(username); st.push(o_account); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get one account table object */ extern int __account_get_by_id(lua_State* L) { unsigned int userid; std::map o_account; try { lua::stack st(L); // get args st.at(1, userid); o_account = get_account_object(userid); st.push(o_account); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get account attribute value */ extern int __account_get_attr(lua_State* L) { const char *username, *attrkey; int attrtype; try { lua::stack st(L); // get args st.at(1, username); st.at(2, attrkey); st.at(3, attrtype); if (t_account *account = accountlist_find_account(username)) { switch ((t_attr_type)attrtype) { case attr_type_str: if (const char * val = account_get_strattr(account, attrkey)) st.push(val); break; case attr_type_num: st.push(account_get_numattr(account, attrkey)); break; case attr_type_bool: st.push(account_get_boolattr(account, attrkey)); break; case attr_type_raw: if (const char * val = account_get_rawattr(account, attrkey)) st.push(val); break; } } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Set account attribute value * (Return 0 if attribute is not set, and 1 if set) */ extern int __account_set_attr(lua_State* L) { const char *username, *attrkey; int attrtype; std::map o_account; int result = 0; try { lua::stack st(L); // get args st.at(1, username); st.at(2, attrkey); st.at(3, attrtype); if (t_account *account = accountlist_find_account(username)) { switch ((t_attr_type)attrtype) { case attr_type_str: const char * strvalue; st.at(4, strvalue); if (account_set_strattr(account, attrkey, strvalue) >= 0) result = 1; break; case attr_type_num: int numvalue; st.at(4, numvalue); if (account_set_numattr(account, attrkey, numvalue) >= 0) result = 1; break; case attr_type_bool: bool boolvalue; st.at(4, boolvalue); if (account_set_boolattr(account, attrkey, boolvalue ? 1 : 0) >= 0) result = 1; break; case attr_type_raw: const char * rawvalue; int length; st.at(4, rawvalue); st.at(5, length); if (account_set_rawattr(account, attrkey, rawvalue, length) >= 0) result = 1; break; } } st.push(result); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get friend list of account */ extern int __account_get_friends(lua_State* L) { const char *username; std::vector > friends; try { lua::stack st(L); // get args st.at(1, username); if (t_account * account = accountlist_find_account(username)) if (t_list *friendlist = account_get_friends(account)) { t_elem const * curr; t_friend * f; LIST_TRAVERSE_CONST(friendlist, curr) { if (!(f = (t_friend*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } friends.push_back(get_friend_object(f)); } } st.push(friends); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get team list of account */ extern int __account_get_teams(lua_State* L) { const char *username; std::vector > teams; try { lua::stack st(L); // get args st.at(1, username); if (t_account * account = accountlist_find_account(username)) if (t_list *teamlist = account_get_teams(account)) { t_elem const * curr; t_team * t; LIST_TRAVERSE_CONST(teamlist, curr) { if (!(t = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } teams.push_back(get_team_object(t)); } } st.push(teams); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get clan member list */ extern int __clan_get_members(lua_State* L) { unsigned int clanid; std::vector > members; try { lua::stack st(L); // get args st.at(1, clanid); if (t_clan * clan = clanlist_find_clan_by_clanid(clanid)) if (t_list * clanmembers = clan_get_members(clan)) { t_elem const * curr; LIST_TRAVERSE_CONST(clanmembers, curr) { t_clanmember * m; if (!(m = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); continue; } members.push_back(get_clanmember_object(m)); } } st.push(members); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get one game table object */ extern int __game_get_by_id(lua_State* L) { unsigned int gameid; std::map o_game; try { lua::stack st(L); // get args st.at(1, gameid); o_game = get_game_object(gameid); st.push(o_game); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } extern int __game_get_by_name(lua_State* L) { const char* gamename; const char* clienttag_str; int gametype; std::map o_game; try { lua::stack st(L); // get args st.at(1, gamename); st.at(2, clienttag_str); st.at(3, gametype); t_clienttag clienttag = tag_str_to_uint(clienttag_str); o_game = get_game_object(gamename, clienttag, (t_game_type)gametype); st.push(o_game); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get one channel table object */ extern int __channel_get_by_id(lua_State* L) { unsigned int channelid; std::map o_channel; try { lua::stack st(L); // get args st.at(1, channelid); o_channel = get_channel_object(channelid); st.push(o_channel); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get usernames online. If allaccounts = true then return all accounts that were used by the server since start */ extern int __server_get_users(lua_State* L) { bool allaccounts = false; std::vector > users; t_connection * conn; t_account * account; try { lua::stack st(L); // get args st.at(1, allaccounts); if (allaccounts) { t_entry * curr; HASHTABLE_TRAVERSE(accountlist(), curr) { if (account = (t_account*)entry_get_data(curr)) users.push_back(get_account_object(account)); } } else { t_elem const * curr; LIST_TRAVERSE_CONST(connlist(), curr) { if (conn = (t_connection*)elem_get_data(curr)) if (account = conn_get_account(conn)) users.push_back(get_account_object(account)); } } st.push(users); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get game list table (id => name) */ extern int __server_get_games(lua_State* L) { std::vector > games; try { lua::stack st(L); t_elist * curr; t_game * game; elist_for_each(curr, gamelist()) { if (game = elist_entry(curr, t_game, glist_link)) games.push_back(get_game_object(game)); } st.push(games); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get channel list (id => name) */ extern int __server_get_channels(lua_State* L) { std::vector > channels; t_channel * channel; try { lua::stack st(L); t_elem * curr; LIST_TRAVERSE(channellist(), curr) { if (channel = (t_channel*)elem_get_data(curr)) channels.push_back(get_channel_object(channel)); } st.push(channels); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get command groups for a command string */ extern int __command_get_group(lua_State* L) { char const * command; try { lua::stack st(L); // get args st.at(1, command); int group = command_get_group(command); st.push(group); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Get customicon rank by rating */ extern int __icon_get_rank(lua_State* L) { int rating; char const * clienttag; try { lua::stack st(L); // get args st.at(1, rating); st.at(2, clienttag); if (t_icon_info * icon = customicons_get_icon_by_rating(rating, (char*)clienttag)) st.push(icon->rank); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } /* Display help for a command */ extern int __describe_command(lua_State* L) { char const *username, *cmdname; try { lua::stack st(L); // get args st.at(1, username); st.at(2, cmdname); if (t_account * account = accountlist_find_account(username)) { if (t_connection * c = account_get_conn(account)) describe_command(c, cmdname); } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 0; } /* Show messagebox */ extern int __messagebox_show(lua_State* L) { char const *username, *text, *caption; int messagebox_type; try { lua::stack st(L); // get args st.at(1, username); st.at(2, text); st.at(3, caption); st.at(4, messagebox_type); if (t_account * account = accountlist_find_account(username)) { if (t_connection * c = account_get_conn(account)) messagebox_show(c, text, caption, messagebox_type); } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 0; } /* Read memory of game client */ extern int __client_readmemory(lua_State* L) { const char * username; unsigned int request_id, offset, length; try { lua::stack st(L); // get args st.at(1, username); st.at(2, request_id); st.at(3, offset); st.at(4, length); if (t_account * account = accountlist_find_account(username)) { if (t_connection * c = account_get_conn(account)) conn_client_readmemory(c, request_id, offset, length); } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 0; } /* Send SID_REQUIREDWORK packet to a client */ extern int __client_requiredwork(lua_State* L) { const char * username; const char * filename; try { lua::stack st(L); // get args st.at(1, username); st.at(2, filename); if (t_account * account = accountlist_find_account(username)) { if (t_connection * c = account_get_conn(account)) conn_client_requiredwork(c, filename); } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 0; } /* Destroy client connection */ extern int __client_kill(lua_State* L) { const char * username; try { lua::stack st(L); // get args st.at(1, username); if (t_account * account = accountlist_find_account(username)) { if (t_connection * c = account_get_conn(account)) conn_set_state(c, conn_state_destroy); } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 0; } /* Localize text */ extern int __localize(lua_State* L) { const char *username, *text; const char *arg1, *arg2, *arg3, *arg4, *arg5; try { lua::stack st(L); // get args st.at(1, username); st.at(2, text); st.at(3, arg1); st.at(4, arg2); st.at(5, arg3); st.at(6, arg4); st.at(7, arg5); if (t_account * account = accountlist_find_account(username)) { if (t_connection * c = account_get_conn(account)) { st.push(localize(c, text, arg1, arg2, arg3, arg4, arg5)); } } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return 1; } } } #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/luafunctions.h000066400000000000000000000040661462640461400207540ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifdef WITH_LUA #ifndef JUST_NEED_TYPES #ifndef INCLUDED_LUAFUNCTION_PROTOS #define INCLUDED_LUAFUNCTION_PROTOS #define JUST_NEED_TYPES #include "luawrapper.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int __message_send_text(lua_State* L); extern int __eventlog(lua_State* L); extern int __account_get_by_name(lua_State* L); extern int __account_get_by_id(lua_State* L); extern int __account_get_attr(lua_State* L); extern int __account_set_attr(lua_State* L); extern int __account_get_friends(lua_State* L); extern int __account_get_teams(lua_State* L); extern int __clan_get_members(lua_State* L); extern int __game_get_by_id(lua_State* L); extern int __game_get_by_name(lua_State* L); extern int __channel_get_by_id(lua_State* L); extern int __server_get_users(lua_State* L); extern int __server_get_games(lua_State* L); extern int __server_get_channels(lua_State* L); extern int __client_kill(lua_State* L); extern int __client_readmemory(lua_State* L); extern int __client_requiredwork(lua_State* L); extern int __command_get_group(lua_State* L); extern int __icon_get_rank(lua_State* L); extern int __describe_command(lua_State* L); extern int __messagebox_show(lua_State* L); extern int __localize(lua_State* L); } } #endif #endif #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/luainterface.cpp000066400000000000000000000533661462640461400212460ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define GAME_INTERNAL_ACCESS #ifdef WITH_LUA #include "common/setup_before.h" #include "team.h" #include #include #include #include #include #include "compat/strcasecmp.h" #include "compat/pdir.h" #include "common/tag.h" #include "common/util.h" #include "common/eventlog.h" #include "connection.h" #include "message.h" #include "channel.h" #include "game.h" #include "account.h" #include "account_wrap.h" #include "timer.h" #include "ipban.h" #include "command_groups.h" #include "friends.h" #include "clan.h" #include "prefs.h" #include "luawrapper.h" #include "luainterface.h" #include "luafunctions.h" #include "luaobjects.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { lua::vm vm; char _msgtemp[MAX_MESSAGE_LEN]; char _msgtemp2[MAX_MESSAGE_LEN]; void _register_functions(); /* Unload all the lua scripts */ extern void lua_unload() { // nothing to do, "vm.initialize()" already destroys lua vm before initialize } /* Initialize lua, register functions and load scripts */ extern void lua_load(char const * scriptdir) { eventlog(eventlog_level_info, __FUNCTION__, "Loading Lua interface..."); try { // init lua virtual machine vm.initialize(); std::vector files = dir_getfiles(scriptdir, ".lua", true); // load all files from the script directory for (int i = 0; i < files.size(); ++i) { vm.load_file(files[i].c_str()); std::snprintf(_msgtemp, sizeof(_msgtemp), "%s", files[i].c_str()); eventlog(eventlog_level_info, __FUNCTION__, "{}", _msgtemp); } _register_functions(); std::snprintf(_msgtemp, sizeof(_msgtemp), "Lua sripts were successfully loaded (%zu files)", files.size()); eventlog(eventlog_level_info, __FUNCTION__, "{}", _msgtemp); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } // handle start event lua_handle_server(luaevent_server_start); } /* Register C++ functions to be able use them from lua scripts */ void _register_functions() { // register package 'api' with functions static const luaL_Reg api[] = { { "message_send_text", __message_send_text }, { "eventlog", __eventlog }, { "account_get_by_id", __account_get_by_id }, { "account_get_by_name", __account_get_by_name }, { "account_get_attr", __account_get_attr }, { "account_set_attr", __account_set_attr }, { "account_get_friends", __account_get_friends }, { "account_get_teams", __account_get_teams }, { "clan_get_members", __clan_get_members }, { "game_get_by_id", __game_get_by_id }, { "game_get_by_name", __game_get_by_name }, { "channel_get_by_id", __channel_get_by_id }, { "server_get_users", __server_get_users }, { "server_get_games", __server_get_games }, { "server_get_channels", __server_get_channels }, { "client_kill", __client_kill }, { "client_readmemory", __client_readmemory }, { "client_requiredwork", __client_requiredwork }, { "command_get_group", __command_get_group }, { "icon_get_rank", __icon_get_rank }, { "describe_command", __describe_command }, { "messagebox_show", __messagebox_show }, { "localize", __localize }, { 0, 0 } }; vm.reg("api", api); // register standalone functions //vm.reg("sum", _sum); // (test function) // global variables lua::table g(vm); g.update("PVPGN_SOFTWARE", PVPGN_SOFTWARE); g.update("PVPGN_VERSION", PVPGN_VERSION); // config variables from bnetd.conf lua::transaction bind(vm); bind.lookup("config"); { lua::table config = bind.table(); config.update("filedir", prefs_get_filedir()); config.update("i18ndir", prefs_get_i18ndir()); config.update("scriptdir", prefs_get_scriptdir()); config.update("reportdir", prefs_get_reportdir()); config.update("chanlogdir", prefs_get_chanlogdir()); config.update("userlogdir", prefs_get_userlogdir()); config.update("localizefile", prefs_get_localizefile()); config.update("motdfile", prefs_get_motdfile()); config.update("motdw3file", prefs_get_motdw3file()); config.update("issuefile", prefs_get_issuefile()); config.update("channelfile", prefs_get_channelfile()); config.update("newsfile", prefs_get_newsfile()); config.update("adfile", prefs_get_adfile()); config.update("topicfile", prefs_get_topicfile()); config.update("ipbanfile", prefs_get_ipbanfile()); config.update("helpfile", prefs_get_helpfile()); config.update("mpqfile", prefs_get_mpqfile()); config.update("logfile", prefs_get_logfile()); config.update("realmfile", prefs_get_realmfile()); config.update("maildir", prefs_get_maildir()); config.update("versioncheck_file", prefs_get_versioncheck_file()); config.update("mapsfile", prefs_get_mapsfile()); config.update("xplevelfile", prefs_get_xplevel_file()); config.update("xpcalcfile", prefs_get_xpcalc_file()); config.update("ladderdir", prefs_get_ladderdir()); config.update("command_groups_file", prefs_get_command_groups_file()); config.update("tournament_file", prefs_get_tournament_file()); config.update("statusdir", prefs_get_outputdir()); config.update("aliasfile", prefs_get_aliasfile()); config.update("anongame_infos_file", prefs_get_anongame_infos_file()); config.update("DBlayoutfile", prefs_get_DBlayoutfile()); config.update("supportfile", prefs_get_supportfile()); config.update("transfile", prefs_get_transfile()); config.update("customicons_file", prefs_get_customicons_file()); config.update("loglevels", prefs_get_loglevels()); config.update("d2cs_version", prefs_get_d2cs_version()); config.update("allow_d2cs_setname", prefs_allow_d2cs_setname()); config.update("iconfile", prefs_get_iconfile()); config.update("war3_iconfile", prefs_get_war3_iconfile()); config.update("star_iconfile", prefs_get_star_iconfile()); config.update("tosfile", prefs_get_tosfile()); config.update("allowed_clients", prefs_get_allowed_clients()); config.update("allow_bad_version", prefs_get_allow_bad_version()); config.update("allow_unknown_version", prefs_get_allow_unknown_version()); config.update("usersync", prefs_get_user_sync_timer()); config.update("userflush", prefs_get_user_flush_timer()); config.update("userflush_connected", prefs_get_user_flush_connected()); config.update("userstep", prefs_get_user_step()); config.update("latency", prefs_get_latency()); config.update("nullmsg", prefs_get_nullmsg()); config.update("shutdown_delay", prefs_get_shutdown_delay()); config.update("shutdown_decr", prefs_get_shutdown_decr()); config.update("ipban_check_int", prefs_get_ipban_check_int()); config.update("new_accounts", prefs_get_allow_new_accounts()); config.update("max_accounts", prefs_get_max_accounts()); config.update("kick_old_login", prefs_get_kick_old_login()); config.update("ask_new_channel", prefs_get_ask_new_channel()); config.update("report_all_games", prefs_get_report_all_games()); config.update("report_diablo_games", prefs_get_report_diablo_games()); config.update("hide_pass_games", prefs_get_hide_pass_games()); config.update("hide_started_games", prefs_get_hide_started_games()); config.update("hide_temp_channels", prefs_get_hide_temp_channels()); config.update("disc_is_loss", prefs_get_discisloss()); config.update("ladder_games", prefs_get_ladder_games()); config.update("ladder_prefix", prefs_get_ladder_prefix()); config.update("enable_conn_all", prefs_get_enable_conn_all()); config.update("hide_addr", prefs_get_hide_addr()); config.update("chanlog", prefs_get_chanlog()); config.update("quota", prefs_get_quota()); config.update("quota_lines", prefs_get_quota_lines()); config.update("quota_time", prefs_get_quota_time()); config.update("quota_wrapline", prefs_get_quota_wrapline()); config.update("quota_maxline", prefs_get_quota_maxline()); config.update("quota_dobae", prefs_get_quota_dobae()); config.update("mail_support", prefs_get_mail_support()); config.update("mail_quota", prefs_get_mail_quota()); config.update("log_notice", prefs_get_log_notice()); config.update("passfail_count", prefs_get_passfail_count()); config.update("passfail_bantime", prefs_get_passfail_bantime()); config.update("maxusers_per_channel", prefs_get_maxusers_per_channel()); config.update("savebyname", prefs_get_savebyname()); config.update("sync_on_logoff", prefs_get_sync_on_logoff()); config.update("hashtable_size", prefs_get_hashtable_size()); config.update("account_allowed_symbols", prefs_get_account_allowed_symbols()); config.update("account_force_username", prefs_get_account_force_username()); config.update("max_friends", prefs_get_max_friends()); config.update("track", prefs_get_track()); config.update("trackaddrs", prefs_get_trackserv_addrs()); config.update("location", prefs_get_location()); config.update("description", prefs_get_description()); config.update("url", prefs_get_url()); config.update("contact_name", prefs_get_contact_name()); config.update("contact_email", prefs_get_contact_email()); config.update("servername", prefs_get_servername()); config.update("max_connections", prefs_get_max_connections()); config.update("packet_limit", prefs_get_packet_limit()); config.update("max_concurrent_logins", prefs_get_max_concurrent_logins()); config.update("use_keepalive", prefs_get_use_keepalive()); config.update("max_conns_per_IP", prefs_get_max_conns_per_IP()); config.update("servaddrs", prefs_get_bnetdserv_addrs()); config.update("udptest_port", prefs_get_udptest_port()); config.update("w3routeaddr", prefs_get_w3route_addr()); config.update("initkill_timer", prefs_get_initkill_timer()); config.update("wolv1addrs", prefs_get_wolv1_addrs()); config.update("wolv2addrs", prefs_get_wolv2_addrs()); config.update("wgameresaddrs", prefs_get_wgameres_addrs()); config.update("apiregaddrs", prefs_get_apireg_addrs()); config.update("woltimezone", prefs_get_wol_timezone()); config.update("wollongitude", prefs_get_wol_longitude()); config.update("wollatitude", prefs_get_wol_latitude()); config.update("wol_autoupdate_serverhost", prefs_get_wol_autoupdate_serverhost()); config.update("wol_autoupdate_username", prefs_get_wol_autoupdate_username()); config.update("wol_autoupdate_password", prefs_get_wol_autoupdate_password()); config.update("ircaddrs", prefs_get_irc_addrs()); config.update("irc_network_name", prefs_get_irc_network_name()); config.update("hostname", prefs_get_hostname()); config.update("irc_latency", prefs_get_irc_latency()); config.update("telnetaddrs", prefs_get_telnet_addrs()); config.update("war3_ladder_update_secs", prefs_get_war3_ladder_update_secs()); config.update("XML_output_ladder", prefs_get_XML_output_ladder()); config.update("output_update_secs", prefs_get_output_update_secs()); config.update("XML_status_output", prefs_get_XML_status_output()); config.update("clan_newer_time", prefs_get_clan_newer_time()); config.update("clan_max_members", prefs_get_clan_max_members()); config.update("clan_channel_default_private", prefs_get_clan_channel_default_private()); config.update("clan_min_invites", prefs_get_clan_min_invites()); config.update("log_commands", prefs_get_log_commands()); config.update("log_command_groups", prefs_get_log_command_groups()); config.update("log_command_list", prefs_get_log_command_list()); } } /* Lua Events (called from scripts) */ #ifndef _LUA_EVENTS_ extern int lua_handle_command(t_connection * c, char const * text, t_luaevent_type luaevent) { t_account * account; const char * func_name; int result = -2; switch (luaevent) { case luaevent_command: func_name = "handle_command"; break; case luaevent_command_before: func_name = "handle_command_before"; break; default: return result; } try { if (!(account = conn_get_account(c))) return -2; std::map o_account = get_account_object(account); lua::transaction(vm) << lua::lookup(func_name) << o_account << text << lua::invoke >> result << lua::end; // invoke lua function } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return result; } extern void lua_handle_game(t_game * game, t_connection * c, t_luaevent_type luaevent) { t_account * account; const char * func_name; switch (luaevent) { case luaevent_game_create: func_name = "handle_game_create"; break; case luaevent_game_report: func_name = "handle_game_report"; break; case luaevent_game_end: func_name = "handle_game_end"; break; case luaevent_game_destroy: func_name = "handle_game_destroy"; break; case luaevent_game_changestatus: func_name = "handle_game_changestatus"; break; case luaevent_game_userjoin: func_name = "handle_game_userjoin"; break; case luaevent_game_userleft: func_name = "handle_game_userleft"; break; default: return; } try { std::map o_game = get_game_object(game); // handle_game_userjoin & handle_game_userleft if (c) { if (!(account = conn_get_account(c))) return; std::map o_account = get_account_object(account); lua::transaction(vm) << lua::lookup(func_name) << o_game << o_account << lua::invoke << lua::end; // invoke lua function } // other functions else { lua::transaction(vm) << lua::lookup(func_name) << o_game << lua::invoke << lua::end; // invoke lua function } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } } std::vector lua_handle_game_list(t_connection * c) { t_account * account; std::vector columns, data; std::vector result; try { if (!(account = conn_get_account(c))) return result; std::map o_account = get_account_object(account); lua::transaction(vm) << lua::lookup("handle_game_list") << o_account << lua::invoke >> columns >> data << lua::end; // invoke lua function // check consistency of data and columns if (columns.size() == 0 || columns.size() != data.size() || std::floor((float)(data.size() / columns.size())) != (data.size() / columns.size())) return result; // fill map result for (std::vector::size_type i = 1; i < data.size(); i += columns.size()) { // init empty game struct t_game * game = (t_game*)xmalloc(sizeof(t_game)); game->id = 0; game->name = NULL; // next columns for (int j = 1; j < columns.size(); j++) { if (columns[j] == "id") game->id = atoi(data[i+j-1].c_str()); else if (columns[j] == "name") game->name = xstrdup(data[i + j - 1].c_str()); } result.push_back(game); } } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return result; } extern int lua_handle_channel(t_channel * channel, t_connection * c, char const * message_text, t_message_type message_type, t_luaevent_type luaevent) { int result = 0; t_account * account; const char * func_name; switch (luaevent) { case luaevent_channel_message: func_name = "handle_channel_message"; break; case luaevent_channel_userjoin: func_name = "handle_channel_userjoin"; break; case luaevent_channel_userleft: func_name = "handle_channel_userleft"; break; default: return 0; } try { if (!(account = conn_get_account(c))) return 0; std::map o_account = get_account_object(account); std::map o_channel = get_channel_object(channel); // handle_channel_userleft & handle_channel_message if (message_text) lua::transaction(vm) << lua::lookup(func_name) << o_channel << o_account << message_text << message_type << lua::invoke >> result << lua::end; // invoke lua function // other functions else lua::transaction(vm) << lua::lookup(func_name) << o_channel << o_account << lua::invoke << lua::end; // invoke lua function } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return result; } extern int lua_handle_user(t_connection * c, t_connection * c_dst, char const * message_text, t_luaevent_type luaevent) { t_account * account, *account_dst; const char * func_name; int result = 0; switch (luaevent) { case luaevent_user_whisper: func_name = "handle_user_whisper"; break; case luaevent_user_login: func_name = "handle_user_login"; break; case luaevent_user_disconnect: func_name = "handle_user_disconnect"; break; default: return 0; } try { if (!(account = conn_get_account(c))) return 0; std::map o_account = get_account_object(account); // handle_server_whisper if (c_dst && message_text) { if (!(account_dst = conn_get_account(c_dst))) return 0; std::map o_account_dst = get_account_object(account_dst); lua::transaction(vm) << lua::lookup(func_name) << o_account << o_account_dst << message_text << lua::invoke >> result << lua::end; // invoke lua function } // other functions else lua::transaction(vm) << lua::lookup(func_name) << o_account << lua::invoke << lua::end; // invoke lua function } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return result; } extern const char * lua_handle_user_icon(t_connection * c, const char * iconinfo) { t_account * account; const char * result = NULL; try { if (!(account = conn_get_account(c))) return 0; std::map o_account = get_account_object(account); lua::transaction(vm) << lua::lookup("handle_user_icon") << o_account << iconinfo << lua::invoke >> result << lua::end; // invoke lua function } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } return result; } extern void lua_handle_server(t_luaevent_type luaevent) { const char * func_name; switch (luaevent) { case luaevent_server_start: func_name = "main"; // when all lua scripts are loaded break; case luaevent_server_mainloop: func_name = "handle_server_mainloop"; // one time per second break; case luaevent_server_rehash: func_name = "handle_server_rehash"; // when restart Lua VM break; default: return; } try { lua::transaction(vm) << lua::lookup(func_name) << lua::invoke << lua::end; // invoke lua function } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } } extern void lua_handle_client_readmemory(t_connection * c, int request_id, std::vector data) { t_account * account; try { if (!(account = conn_get_account(c))) return; std::map o_account = get_account_object(account); lua::transaction(vm) << lua::lookup("handle_client_readmemory") << o_account << request_id << data << lua::invoke << lua::end; // invoke lua function } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } } extern void lua_handle_client_extrawork(t_connection * c, int gametype, int length, const char * data) { t_account * account; try { if (!(account = conn_get_account(c))) return; std::map o_account = get_account_object(account); lua::transaction(vm) << lua::lookup("handle_client_extrawork") << o_account << gametype << length << data << lua::invoke << lua::end; // invoke lua function } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } catch (...) { eventlog(eventlog_level_error, __FUNCTION__, "lua exception\n"); } } #endif } } #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/luainterface.h000066400000000000000000000051351462640461400207020ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifdef WITH_LUA #ifndef JUST_NEED_TYPES #ifndef INCLUDED_LUAINTERFACE_PROTOS #define INCLUDED_LUAINTERFACE_PROTOS #ifdef WIN32 #pragma comment(lib, "lua5.1.lib") #endif #define JUST_NEED_TYPES #include "connection.h" #include "luawrapper.h" #include #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { typedef enum { luaevent_command, luaevent_command_before, luaevent_game_create, luaevent_game_report, luaevent_game_end, luaevent_game_destroy, luaevent_game_changestatus, luaevent_game_userjoin, luaevent_game_userleft, luaevent_channel_message, // user-to-channel luaevent_channel_userjoin, luaevent_channel_userleft, luaevent_user_whisper, // user-to-user luaevent_user_login, luaevent_user_disconnect, luaevent_server_start, luaevent_server_rehash, luaevent_server_mainloop, } t_luaevent_type; extern void lua_load(char const * scriptdir); extern void lua_unload(); extern int lua_handle_command(t_connection * c, char const * text, t_luaevent_type luaevent); extern void lua_handle_game(t_game * game, t_connection * c, t_luaevent_type luaevent); extern std::vector lua_handle_game_list(t_connection * c); extern int lua_handle_channel(t_channel * channel, t_connection * c, char const * message_text, t_message_type message_type, t_luaevent_type luaevent); extern int lua_handle_user(t_connection * c, t_connection * c_dst, char const * message_text, t_luaevent_type luaevent); extern const char * lua_handle_user_icon(t_connection * c, const char * iconinfo); extern void lua_handle_server(t_luaevent_type luaevent); extern void lua_handle_client_readmemory(t_connection * c, int request_id, std::vector data); extern void lua_handle_client_extrawork(t_connection * c, int gametype, int length, const char * data); } } #endif #endif #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/luaobjects.cpp000066400000000000000000000303271462640461400207270ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WITH_LUA #include "common/setup_before.h" #define ACCOUNT_INTERNAL_ACCESS #define GAME_INTERNAL_ACCESS #define CHANNEL_INTERNAL_ACCESS #define CLAN_INTERNAL_ACCESS #define TEAM_INTERNAL_ACCESS #include "team.h" #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/tag.h" #include "common/util.h" #include "common/eventlog.h" #include "common/list.h" #include "common/flags.h" #include "common/proginfo.h" #include "common/addr.h" #include "common/xstring.h" #include "connection.h" #include "message.h" #include "channel.h" #include "game.h" #include "account.h" #include "account_wrap.h" #include "timer.h" #include "ipban.h" #include "command_groups.h" #include "friends.h" #include "clan.h" #include "luaobjects.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { template T join(const A &begin, const A &end, const T &t); extern std::map get_account_object(unsigned int userid) { std::map o_account; if (userid == 0) return o_account; if (t_account * account = accountlist_find_account_by_uid(userid)) o_account = get_account_object(account); return o_account; } extern std::map get_account_object(const char * username) { std::map o_account; if (username[0] == '\0') return o_account; if (t_account * account = accountlist_find_account(username)) o_account = get_account_object(account); return o_account; } extern std::map get_account_object(t_account * account) { std::map o_account; if (!account) return o_account; // DO NOT ADD FIELDS FROM A DATABASE HERE - IT WILL CAUSE WASTE SQL QUERIES WHEN ITERATE ALL CONNECTIONS o_account["id"] = std_to_string(account_get_uid(account)); o_account["name"] = account_get_name(account); o_account["online"] = "false"; // set init as offline // if user online if (t_connection * c = account_get_conn(account)) { o_account["online"] = "true"; o_account["latency"] = std_to_string(conn_get_latency(c)); // non-game client doesn't provide a country if (const char * country = conn_get_country(c)) o_account["country"] = country; if (t_game *game = conn_get_game(c)) o_account["game_id"] = std_to_string(game_get_id(game)); if (t_channel *channel = conn_get_channel(c)) o_account["channel_id"] = std_to_string(channel_get_channelid(channel)); if (t_clienttag clienttag = conn_get_clienttag(c)) o_account["clienttag"] = clienttag_uint_to_str(clienttag); if (t_archtag archtag = conn_get_archtag(c)) o_account["archtag"] = clienttag_uint_to_str(archtag); if (const char * clientexe = conn_get_clientexe(c)) o_account["clientexe"] = clientexe; if (const char * clientver = conn_get_clientver(c)) o_account["clientver"] = clientver; o_account["gamelang"] = std_to_string(conn_get_gamelang(c)); if (int addr = conn_get_addr(c)) o_account["ip"] = addr_num_to_ip_str(addr); if (const char * away = conn_get_awaystr(c)) o_account["away"] = away; if (const char * dnd = conn_get_dndstr(c)) o_account["dnd"] = dnd; o_account["flags"] = std_to_string(conn_get_flags(c)); if (const char * charname = conn_get_charname(c)) o_account["charname"] = charname; o_account["idletime"] = std_to_string(conn_get_idletime(c)); } if (account->clanmember) o_account["clan_id"] = account->clanmember->clan->clanid; // - friends can be get from lua_account_get_friends // - teams can be get from lua_account_get_teams return o_account; } extern std::map get_game_object(unsigned int gameid) { std::map o_game; if (t_game * game = gamelist_find_game_byid(gameid)) o_game = get_game_object(game); return o_game; } extern std::map get_game_object(const char * gamename, t_clienttag clienttag, t_game_type gametype) { std::map o_game; if (t_game * game = gamelist_find_game(gamename, clienttag, gametype)) o_game = get_game_object(game); return o_game; } extern std::map get_game_object(t_game * game) { std::map o_game; if (!game) return o_game; o_game["id"] = std_to_string(game->id); o_game["name"] = game->name; o_game["pass"] = game->pass; o_game["info"] = game->info; o_game["type"] = std_to_string(game->type); o_game["flag"] = std_to_string(game->flag); o_game["address"] = addr_num_to_ip_str(game->addr); o_game["port"] = std_to_string(game->port); o_game["status"] = std_to_string(game->status); o_game["currentplayers"] = std_to_string(game->ref); o_game["totalplayers"] = std_to_string(game->count); o_game["maxplayers"] = std_to_string(game->maxplayers); if (game->mapname) o_game["mapname"] = game->mapname; o_game["option"] = std_to_string(game->option); o_game["maptype"] = std_to_string(game->maptype); o_game["tileset"] = std_to_string(game->tileset); o_game["speed"] = std_to_string(game->speed); o_game["mapsize_x"] = std_to_string(game->mapsize_x); o_game["mapsize_y"] = std_to_string(game->mapsize_y); if (t_connection *c = game->owner) { if (t_account *account = conn_get_account(c)) o_game["owner"] = account_get_name(account); } std::vector players; for (int i = 0; i < game->ref; i++) { if (t_account *account = game->players[i]) players.push_back(account_get_name(account)); } o_game["players"] = join(players.begin(), players.end(), std::string(",")); o_game["bad"] = std_to_string(game->bad); // if 1, then the results will be ignored std::vector results; if (game->results) { for (int i = 0; i < game->count; i++) results.push_back(std_to_string(game->results[i])); } o_game["results"] = join(results.begin(), results.end(), std::string(",")); // UNDONE: add report_heads and report_bodies: they are XML strings o_game["create_time"] = std_to_string(game->create_time); o_game["start_time"] = std_to_string(game->start_time); o_game["lastaccess_time"] = std_to_string(game->lastaccess_time); o_game["difficulty"] = std_to_string(game->difficulty); o_game["version"] = vernum_to_verstr(game->version); o_game["startver"] = std_to_string(game->startver); if (t_clienttag clienttag = game->clienttag) o_game["clienttag"] = clienttag_uint_to_str(clienttag); if (game->description) o_game["description"] = game->description; if (game->realmname) o_game["realmname"] = game->realmname; return o_game; } extern std::map get_channel_object(unsigned int channelid) { std::map o_channel; if (t_channel * channel = channellist_find_channel_bychannelid(channelid)) o_channel = get_channel_object(channel); return o_channel; } extern std::map get_channel_object(t_channel * channel) { std::map o_channel; if (!channel) return o_channel; o_channel["id"] = std_to_string(channel->id); o_channel["name"] = channel->name; if (channel->shortname) o_channel["shortname"] = channel->shortname; if (channel->country) o_channel["country"] = channel->country; o_channel["flags"] = std_to_string(channel->flags); o_channel["maxmembers"] = std_to_string(channel->maxmembers); o_channel["currmembers"] = std_to_string(channel->currmembers); o_channel["clienttag"] = clienttag_uint_to_str(channel->clienttag); if (channel->realmname) o_channel["realmname"] = channel->realmname; if (channel->logname) o_channel["logname"] = channel->logname; // Westwood Online Extensions o_channel["minmembers"] = std_to_string(channel->minmembers); o_channel["gameType"] = std_to_string(channel->gameType); if (channel->gameExtension) o_channel["gameExtension"] = channel->gameExtension; std::vector members; if (channel->memberlist) { t_channelmember *m = channel->memberlist; while (m) { if (t_account *account = conn_get_account(m->connection)) members.push_back(account_get_name(account)); m = m->next; } } o_channel["memberlist"] = join(members.begin(), members.end(), std::string(",")); std::vector bans; if (channel->banlist) { t_elem const * curr; LIST_TRAVERSE_CONST(channel->banlist, curr) { char * b; if (!(b = (char*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL name in banlist"); continue; } bans.push_back(b); } } o_channel["banlist"] = join(bans.begin(), bans.end(), std::string(",")); return o_channel; } extern std::map get_clan_object(t_clan * clan) { std::map o_clan; if (!clan) return o_clan; o_clan["id"] = std_to_string(clan->clanid); o_clan["tag"] = clantag_to_str(clan->tag); o_clan["clanname"] = clan->clanname; o_clan["created"] = std_to_string(clan->created); o_clan["creation_time"] = std_to_string(clan->creation_time); o_clan["clan_motd"] = clan->clan_motd; o_clan["channel_type"] = std_to_string(clan->channel_type); // 0--public 1--private // clanmembers can be get from api.clan_get_members return o_clan; } extern std::map get_clanmember_object(t_clanmember * member) { std::map o_member; if (!member) return o_member; o_member["username"] = account_get_name(member->memberacc); o_member["status"] = member->status; o_member["clan_id"] = std_to_string(member->clan->clanid); o_member["join_time"] = std_to_string(member->join_time); o_member["fullmember"] = std_to_string(member->fullmember);// 0 -- clanmember is only invited, 1 -- clanmember is fullmember return o_member; } extern std::map get_team_object(t_team * team) { std::map o_team; if (!team) return o_team; o_team["id"] = std_to_string(team->teamid); o_team["size"] = std_to_string(team->size); o_team["clienttag"] = clienttag_uint_to_str(team->clienttag); o_team["lastgame"] = std_to_string(team->lastgame); o_team["wins"] = std_to_string(team->wins); o_team["losses"] = std_to_string(team->losses); o_team["xp"] = std_to_string(team->xp); o_team["level"] = std_to_string(team->level); o_team["rank"] = std_to_string(team->rank); // usernames std::vector members; if (team->members) { for (int i = 0; i < MAX_TEAMSIZE; i++) members.push_back(account_get_name(team->members[i])); } o_team["members"] = join(members.begin(), members.end(), std::string(",")); return o_team; } extern std::map get_friend_object(t_friend * f) { std::map o_friend; if (!f) return o_friend; o_friend = get_account_object(f->friendacc); return o_friend; } /* Join two vector objects to string by delimeter */ template T join(const A &begin, const A &end, const T &t) { T result; for (A it = begin; it != end; it++) { if (!result.empty()) result.append(t); result.append(*it); } return result; } } } #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/luaobjects.h000066400000000000000000000041221462640461400203660ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifdef WITH_LUA #ifndef JUST_NEED_TYPES #ifndef INCLUDED_LUAOBJECTS_PROTOS #define INCLUDED_LUAOBJECTS_PROTOS #define JUST_NEED_TYPES #include "connection.h" #include #include #include #include #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern std::map get_account_object(unsigned int userid); extern std::map get_account_object(const char *username); extern std::map get_account_object(t_account *account); extern std::map get_game_object(unsigned int gameid); extern std::map get_game_object(const char * gamename, t_clienttag clienttag, t_game_type gametype); extern std::map get_game_object(t_game * game); extern std::map get_channel_object(unsigned int channelid); extern std::map get_channel_object(t_channel * channel); extern std::map get_clan_object(t_clan * clan); extern std::map get_clanmember_object(t_clanmember * member); extern std::map get_team_object(t_team * team); extern std::map get_friend_object(t_friend * f); } } #endif #endif #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/luawrapper.cpp000066400000000000000000000073421462640461400207570ustar00rootroot00000000000000/* * Copyright (c) 2009, Anton Burdinuk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the organization nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef WITH_LUA #include "luawrapper.h" namespace lua { invoke_type invoke; m_invoke_type m_invoke; release_type end; } void lua::throw_lua_exception(lua_State* st, const std::string& addinfo) { size_t len = 0; const char* p = lua_tolstring(st, -1, &len); std::string s(p, len); if (addinfo.length()) { s.append(" ", 1); s.append(addinfo); } lua_pop(st, 1); throw exception(s); } void lua::vm::initialize() { done(); st = lua_open(); if (!st) throw exception("can`t create lua virtual machine instance"); luaL_openlibs(st); } void lua::vm::load_file(const char* file) { if (!st) throw exception("lua virtual machine is not ready"); if (luaL_loadfile(st, file) || lua_pcall(st, 0, 0, 0)) throw_lua_exception(st); } void lua::vm::eval(const std::string& stmt, int offset) { enum { max_chunk_name_len = 64 }; if (offset > stmt.length()) offset = stmt.length(); const char* p = stmt.c_str() + offset; int n = stmt.length() - offset; if (luaL_loadbuffer(st, p, n, n > max_chunk_name_len ? (std::string(p, max_chunk_name_len) + "...").c_str() : p) || lua_pcall(st, 0, 0, 0)) throw_lua_exception(st); } void lua::bind::lookup(const char* name) { if (!refuse) { if (lua_type(st, cur_index) != LUA_TNIL) { lua_getfield(st, cur_index, name); cur_index = size(); } } } void lua::bind::end(void) { if (!refuse && mutex) { int n = size() - st_top; if (n > 0) lua_pop(st, n); mutex = 0; args_number = retvals_number = 0; cur_index = LUA_GLOBALSINDEX; } } void lua::bind::invoke(void) { int function_index = -(args_number + 1); if (!refuse && lua_isfunction(st, function_index)) { int n = size(); if (lua_pcall(st, args_number, LUA_MULTRET, 0)) throw_lua_exception(st); retvals_number = size() + args_number + 1 - n; } } void lua::bind::m_invoke(void) { if (!refuse) { int function_index = -(args_number + 1); int this_index = function_index - 1; if (lua_isfunction(st, function_index) && lua_istable(st, this_index)) { lua_pushvalue(st, this_index); args_number++; lua_insert(st, -args_number); int n = size(); if (lua_pcall(st, args_number, LUA_MULTRET, 0)) throw_lua_exception(st); retvals_number = size() + args_number + 1 - n; } } } #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/luawrapper.h000066400000000000000000000232071462640461400204220ustar00rootroot00000000000000/* * Copyright (c) 2009, Anton Burdinuk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the organization nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef WITH_LUA #ifndef INCLUDED_LUAWRAPPER_H #define INCLUDED_LUAWRAPPER_H extern "C" { #include #include #include } #include #include #include #include #include #include namespace lua { // lua exception class exception : public std::exception { public: explicit exception(const std::string s) : m_what(s) { } virtual ~exception() throw() { } virtual const char* what() const throw() { return m_what.c_str(); } protected: std::string m_what; }; // check Lua VM for errors and throw C++ lua::exception void throw_lua_exception(lua_State* st, const std::string& addinfo = std::string()); // Lua VM instance class vm { public: vm() : st(nullptr), mutex(0) { } explicit vm(lua_State* _st) : st(_st), mutex(0) { } ~vm() { this->done(); } // create new Lua VM instance void initialize(); // load and execute Lua script from file void load_file(const char* file); // load and execute Lua statement from string void eval(const std::string& stmt, int offset = 0); // register external C function void reg(const char* name, lua_CFunction func) { lua_register(st, name, func); } void reg(const char* package, const luaL_Reg* members) { luaL_register(st, package, members); } // destroy Lua VM instance void done() { if (st) { lua_close(st); } } lua_State* get_st() const { return st; } friend class stack; friend class bind; protected: lua_State* st; // mutex for lua::bind int mutex; }; // Lua stack wrapper (use in custom CFunctions only) class stack { public: // initialize stack stack() : st(nullptr), st_top(0) { } explicit stack(lua_State* _st) : st(_st), st_top(_st ? lua_gettop(_st) : 0) { } explicit stack(const vm& _vm) : st(_vm.st), st_top(_vm.st ? lua_gettop(_vm.st) : 0) { } // return current stack size int size() const { return lua_gettop(st); } // find field in table void find(const char* name, int index = LUA_GLOBALSINDEX) { lua_getfield(st, index, name); } // push to stack scalar void push(unsigned int v) { lua_pushinteger(st, v); } void push(int v) { lua_pushinteger(st, v); } void push_boolean(int v) { lua_pushboolean(st, v); } void push(double v) { lua_pushnumber(st, v); } void push(const std::string& v) { lua_pushlstring(st, v.c_str(), v.length()); } void push(const char* v) { lua_pushlstring(st, v, std::strlen(v)); } // push to stack std::map template void push(const std::map<_key, _val, _comp, _alloc>& v) { lua_newtable(st); for (typename std::map<_key, _val, _comp, _alloc>::const_iterator i = v.begin(); i != v.end(); ++i) { push(i->first); push(i->second); lua_rawset(st, -3); } } // push to stack std::vector template void push(const std::vector<_val, _alloc>& v) { lua_newtable(st); for (int i = 0; i < v.size(); ++i) { push(i); push(v[i]); lua_rawset(st, -3); } } // recv from stack scalar void get(unsigned int& v, int index) { v = lua_tointeger(st, index); } void get(int& v, int index) { v = lua_tointeger(st, index); } void get(bool& v, int index) { v = lua_toboolean(st, index); } void get(double& v, int index) { v = lua_tonumber(st, index); } void get(const char*& v, int index) { size_t len = 0; v = lua_tolstring(st, index, &len); } void get(std::string& v, int index) { size_t len = 0; const char* p = lua_tolstring(st, index, &len); if (p) v.assign(p, len); } // recv from stack std::map template void get(std::map<_key, _val, _comp, _alloc>& v, int index) { if (lua_type(st, index) == LUA_TTABLE) { if (index < 0) index = lua_gettop(st) + 1 + index; lua_pushnil(st); while (lua_next(st, index)) { _val _v; get(_v, -1); _key _k; get(_k, -2); v[_k] = _v; pop(); } } } // recv from stack std::vector (use std::map instead) template void get(std::vector<_val, _alloc>& v, int index) { if (lua_type(st, index) == LUA_TTABLE) { if (index < 0) index = lua_gettop(st) + 1 + index; lua_pushnil(st); while (lua_next(st, index)) { _val _v; get(_v, -1); if (lua_type(st, -2) == LUA_TNUMBER) { int _k = lua_tointeger(st, -2); if (_k >= 0) { if (_k >= v.size()) v.resize(_k + 1); v[_k] = _v; } } pop(); } } } // get value of stack with index check template void at(int index, T& v) { if (index > 0 && index <= size()) get(v, index); else v = T(); } // pop last values from stack void pop() { lua_pop(st, 1); } void popn(int n) { lua_pop(st, n); } protected: lua_State* st; int st_top; }; // manipulators for lua::bind class invoke_type {}; class m_invoke_type {}; class release_type {}; extern invoke_type invoke; // execute function extern m_invoke_type m_invoke; // execute function as method (insert this pointer before argument list) extern release_type end; // release lua::bind (pop staff from Lua stack) class lookup // find field in last table (or LUA_GLOBALSINDEX if first) { public: lookup(const std::string& _name) :name(_name) { } friend class bind; protected: std::string name; }; // class for direct access to Lua table fields class table : protected stack { public: table() : stack(nullptr), index(0) { } explicit table(lua_State* _st, int _index) : stack(_st), index(_index) { } explicit table(vm& _vm) :stack(_vm.get_st()), index(LUA_GLOBALSINDEX) { } // get field value template void query(const char* name, _VAL& val) { if (st) { lua_getfield(st, index, name); stack::get(val, -1); stack::pop(); } } // set field value template void update(const char* name, const _VAL& val) { if (st) { stack::push(val); lua_setfield(st, index, name); } } protected: int index; }; // Lua transaction class (only one per vm instance) class bind : protected stack { public: explicit bind(vm& _vm) : stack(_vm), refuse(0), args_number(0), retvals_number(0), cur_index(LUA_GLOBALSINDEX), mutex(_vm.mutex) { if (mutex) refuse++; else mutex++; } ~bind() { end(); } // find field in last table void lookup(const char* name); // return lua::table object for last table lua::table table() { if (lua_type(st, -1) == LUA_TTABLE) return lua::table(st, lua_gettop(st)); return lua::table(); } // execute last function void invoke(); // execute last function as method void m_invoke(); // end transaction (free Lua stack) void end(); // push function argument to Lua stack template bind& operator<<(const T& v) { if (!refuse) { push(v); args_number++; } return *this; } // pop function return value from stack template bind& operator >> (T& v) { if (refuse || retvals_number < 1) { // INFO: (HarpyWar) -2 is default value if Lua code failed to run due to error // it's needed for lua_handle_command() if (typeid(T) == typeid(int)) v = T(-2); else v = T(); } else { get(v, -retvals_number); retvals_number--; } return *this; } // for manipulators bind& operator<<(const lua::lookup& f) { bind::lookup(f.name.c_str()); return *this; } bind& operator<<(const lua::invoke_type&) { bind::invoke(); return *this; } bind& operator<<(const lua::m_invoke_type&) { bind::m_invoke(); return *this; } bind& operator<<(const lua::release_type&) { bind::end(); return *this; } protected: int args_number; int retvals_number; int cur_index; private: int& mutex; int refuse; }; using transaction = bind; using function = lookup; using field = lookup; } #endif #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/mail.cpp000066400000000000000000000300131462640461400175060ustar00rootroot00000000000000/* * Copyright (C) 2001,2006 Dizzy * Copyright (C) 2004 Donny Redmond (dredmond@linuxmail.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "mail.h" #include #include #include #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "compat/mkdir.h" #include "compat/statmacros.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/xstring.h" #include "account.h" #include "message.h" #include "prefs.h" #include "connection.h" #include "helpfile.h" #include "command.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static int identify_mail_function(const std::string&); static void mail_usage(t_connection*); static void mail_func_send(t_connection*, char const *, char const *); static void mail_func_read(t_connection*, std::string); static void mail_func_delete(t_connection*, std::string); static unsigned get_mail_quota(t_account *); /* Mail API */ /* for now this functions are only for internal use */ Mail::Mail(const std::string& sender, const std::string& message, const std::time_t timestamp) :sender_(sender), message_(message), timestamp_(timestamp) { } Mail::~Mail() throw() { } const std::string& Mail::sender() const { return sender_; } const std::string& Mail::message() const { return message_; } const std::time_t& Mail::timestamp() const { return timestamp_; } Mailbox::Mailbox(unsigned uid_) :uid(uid_), path(buildPath(prefs_get_maildir())), mdir(path, true) { } std::string Mailbox::buildPath(const std::string& root) const { std::ostringstream ostr; ostr << root << "/" << std::setfill('0') << std::setw(6) << uid; return ostr.str(); } void Mailbox::createOpenDir() { p_mkdir(prefs_get_maildir(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); p_mkdir(path.c_str(), S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH); mdir.open(path, false); } Mailbox::~Mailbox() throw() { } unsigned Mailbox::size() const { mdir.rewind(); unsigned count = 0; while ((mdir.read())) count++; return count; } bool Mailbox::empty() const { mdir.rewind(); if (mdir.read()) return false; return true; } void Mailbox::deliver(const std::string& sender, const std::string& mess) { if (!mdir) try { createOpenDir(); } catch (const Directory::OpenError&) { ERROR1("could not (re)open directory: '{}'", path.c_str()); throw DeliverError("could not (re)open directory: " + path); } std::ostringstream ostr; ostr << path << '/' << std::setfill('0') << std::setw(15) << static_cast(std::time(0)); std::ofstream fd(ostr.str().c_str()); if (!fd) { ERROR1("error opening mail file. check permissions: '{}'", path.c_str()); throw DeliverError("error opening mail file. check permissions: " + path); } fd << sender << std::endl << mess << std::endl; } Mail Mailbox::read(const std::string& fname, const std::time_t& timestamp) const { std::ifstream fd(fname.c_str()); if (!fd) { ERROR1("error opening mail file: {}", fname.c_str()); throw ReadError("error opening mail file: " + fname); } std::string sender; std::getline(fd, sender); std::string message; std::getline(fd, message); return Mail(sender, message, timestamp); } Mail Mailbox::read(unsigned int idx) const { mdir.rewind(); const char * dentry = mdir.read(); for (unsigned i = 0; i < idx && (dentry = mdir.read());) if (dentry[0] != '.') ++i; if (!dentry) { INFO0("mail not found"); throw ReadError("mail not found"); } std::string fname(path); fname += '/'; fname += dentry; return read(fname, std::atoi(dentry)); } void Mailbox::readAll(MailList& dest) const { mdir.rewind(); std::string fname(path); fname += '/'; const char* dentry; while ((dentry = mdir.read())) { if (dentry[0] == '.') continue; try { dest.push_back(read(fname + dentry, std::atoi(dentry))); } catch (const ReadError&) { /* ignore ReadError in reading a specific message and try to read as much as we can */ } } } void Mailbox::erase(unsigned int idx) { mdir.rewind(); const char* dentry = mdir.read(); for (unsigned i = 0; i < idx && (dentry = mdir.read());) if (dentry[0] != '.') ++i; if (!dentry) { WARN0("index out of range"); return; } std::string fname(path); fname += '/'; fname += dentry; if (std::remove(fname.c_str()) < 0) INFO2("could not remove file \"{}\" (std::remove: {})", fname.c_str(), std::strerror(errno)); } void Mailbox::clear() { std::string fname(path); fname += '/'; mdir.rewind(); const char* dentry; while ((dentry = mdir.read())) { std::remove((fname + dentry).c_str()); } } extern int handle_mail_command(t_connection * c, char const * text) { if (!prefs_get_mail_support()) { message_send_text(c, message_type_error, c, localize(c, "This server has NO mail support.")); return -1; } char const *subcommand; std::vector args = split_command(text, 3); if (args[1].empty()) { describe_command(c, args[0].c_str()); return -1; } subcommand = args[1].c_str(); // subcommand switch (identify_mail_function(subcommand)) { case MAIL_FUNC_SEND: if (args[3].empty()) { describe_command(c, args[0].c_str()); return -1; } mail_func_send(c, args[2].c_str(), args[3].c_str()); break; case MAIL_FUNC_READ: mail_func_read(c, args[2]); break; case MAIL_FUNC_DELETE: if (args[2].empty()) { describe_command(c, args[0].c_str()); return -1; } mail_func_delete(c, args[2]); break; default: describe_command(c, args[0].c_str()); } return 0; } static int identify_mail_function(const std::string& funcstr) { if (funcstr.empty() || !strcasecmp(funcstr.c_str(), "read") || !strcasecmp(funcstr.c_str(), "r")) return MAIL_FUNC_READ; if (!strcasecmp(funcstr.c_str(), "send") || !strcasecmp(funcstr.c_str(), "s")) return MAIL_FUNC_SEND; if (!strcasecmp(funcstr.c_str(), "delete") || !strcasecmp(funcstr.c_str(), "del")) return MAIL_FUNC_DELETE; return MAIL_FUNC_UNKNOWN; } static unsigned get_mail_quota(t_account * user) { int quota; char const * user_quota = account_get_strattr(user, "BNET\\auth\\mailquota"); if (!user_quota) quota = prefs_get_mail_quota(); else { quota = std::atoi(user_quota); if (quota < 1) quota = 1; if (quota > MAX_MAIL_QUOTA) quota = MAX_MAIL_QUOTA; } return quota; } static void mail_func_send(t_connection * c, char const * receiver, char const * message) { if (!c) { ERROR0("got NULL connection"); return; } t_account * recv = accountlist_find_account(receiver); if (!recv) { message_send_text(c, message_type_error, c, localize(c, "Receiver UNKNOWN!")); return; } Mailbox mbox(account_get_uid(recv)); if (get_mail_quota(recv) <= mbox.size()) { message_send_text(c, message_type_error, c, localize(c, "Receiver has reached his mail quota. Your message will NOT be sent.")); return; } try { mbox.deliver(conn_get_username(c), message); message_send_text(c, message_type_info, c, localize(c, "Your mail has been sent successfully.")); } catch (const Mailbox::DeliverError&) { message_send_text(c, message_type_error, c, localize(c, "There was an error completing your request!")); } } bool NonNumericChar(const char ch) { if (ch < '0' || ch > '9') return true; return false; } static void mail_func_read(t_connection * c, std::string token) { if (!c) { ERROR0("got NULL connection"); return; } t_account * user = conn_get_account(c); Mailbox mbox(account_get_uid(user)); if (token.empty()) { /* user wants to see the mail summary */ if (mbox.empty()) { message_send_text(c, message_type_info, c, localize(c, "You have no mail.")); return; } MailList mlist; mbox.readAll(mlist); std::ostringstream ostr; ostr << "You have " << mbox.size() << " messages. Your mail quote is set to " << get_mail_quota(user) << '.'; message_send_text(c, message_type_info, c, ostr.str().c_str()); message_send_text(c, message_type_info, c, localize(c, "ID Sender Date")); message_send_text(c, message_type_info, c, "-------------------------------------"); for (MailList::const_iterator it(mlist.begin()); it != mlist.end(); ++it) { ostr.str(""); ostr << std::setfill('0') << std::setw(2) << std::right << (it - mlist.begin()) << " " << std::setfill(' ') << std::setw(14) << std::left << it->sender() << ' '; char buff[128]; std::strftime(buff, sizeof(buff), "%a %b %d %H:%M:%S %Y", std::localtime(&it->timestamp())); ostr << buff; message_send_text(c, message_type_info, c, ostr.str().c_str()); } message_send_text(c, message_type_info, c, localize(c, "Use /mail read to read the content of any message")); } else { /* user wants to read a message */ if (std::find_if(token.begin(), token.end(), NonNumericChar) != token.end()) { message_send_text(c, message_type_error, c, localize(c, "Invalid index. Please use /mail read where is a number.")); return; } try { unsigned idx = std::atoi(token.c_str()); Mail mail(mbox.read(idx)); std::ostringstream ostr; ostr << "Message #" << idx << " from " << mail.sender() << " on "; char buff[128]; std::strftime(buff, sizeof(buff), "%a %b %d %H:%M:%S %Y", std::localtime(&mail.timestamp())); ostr << buff << ':'; message_send_text(c, message_type_info, c, ostr.str().c_str()); message_send_text(c, message_type_info, c, mail.message().c_str()); } catch (const Mailbox::ReadError&) { message_send_text(c, message_type_error, c, localize(c, "There was an error completing your request.")); } } } static void mail_func_delete(t_connection * c, std::string token) { if (!c) { ERROR0("got NULL connection"); return; } if (token.empty()) { message_send_text(c, message_type_error, c, localize(c, "Please specify which message to delete. Use the following syntax: /mail delete {|all} .")); return; } t_account * user = conn_get_account(c); Mailbox mbox(account_get_uid(user)); if (token == "all") { mbox.clear(); message_send_text(c, message_type_info, c, localize(c, "Successfully deleted messages.")); } else { if (std::find_if(token.begin(), token.end(), NonNumericChar) != token.end()) { message_send_text(c, message_type_error, c, localize(c, "Invalid index. Please use /mail delete {|all} where is a number.")); return; } mbox.erase(std::atoi(token.c_str())); message_send_text(c, message_type_info, c, localize(c, "Succesfully deleted message.")); } } extern unsigned check_mail(t_connection const * c) { if (!c) { ERROR0("got NULL connection"); return 0; } if (t_account * account = conn_get_account(c)) return Mailbox(account_get_uid(account)).size(); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/mail.h000066400000000000000000000053121462640461400171570ustar00rootroot00000000000000/* * Copyright (C) 2001,2006 Dizzy * Copyright (C) 2004 Donny Redmond (dredmond@linuxmail.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef INCLUDED_PVPGN_BNETD_MAIL #define INCLUDED_PVPGN_BNETD_MAIL #define MAX_FUNC_LEN 10 #define MAX_MAIL_QUOTA 10 #define MAIL_FUNC_SEND 1 #define MAIL_FUNC_READ 2 #define MAIL_FUNC_DELETE 3 #define MAIL_FUNC_UNKNOWN 4 #include #include #include #include #include "compat/pdir.h" #include "connection.h" namespace pvpgn { namespace bnetd { class Mail { public: Mail(const std::string& sender, const std::string& mess, const std::time_t timestamp); ~Mail() throw(); const std::string& sender() const; const std::string& message() const; const std::time_t& timestamp() const; private: const std::string sender_; const std::string message_; const std::time_t timestamp_; }; typedef std::deque MailList; class Mailbox { public: class ReadError : public std::runtime_error { public: ReadError(const std::string& mess) :std::runtime_error(mess) {} ~ReadError() throw() {} }; class DeliverError : public std::runtime_error { public: DeliverError(const std::string& mess) :std::runtime_error(mess) {} ~DeliverError() throw() {} }; explicit Mailbox(unsigned uid); ~Mailbox() throw(); unsigned size() const; bool empty() const; void deliver(const std::string& sender, const std::string& mess); Mail read(unsigned int) const; void readAll(MailList& dest) const; void erase(unsigned int); void clear(); private: unsigned uid; const std::string path; mutable Directory mdir; std::string buildPath(const std::string& root) const; void createOpenDir(); Mail read(const std::string& fname, const std::time_t& timestamp) const; Mailbox(const Mailbox&); Mailbox& operator=(const Mailbox&); }; extern int handle_mail_command(t_connection *, char const *); extern unsigned check_mail(t_connection const * c); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/main.cpp000066400000000000000000000425251462640461400175230ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef WIN32 # include "win32/service.h" #endif #ifdef WIN32_GUI # include "common/gui_printf.h" #endif #include "compat/stdfileno.h" #include "compat/psock.h" #include "compat/uname.h" #include "compat/pgetpid.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/fdwatch.h" #include "common/trans.h" #include "common/give_up_root_privileges.h" #include "common/version.h" #include "common/hexdump.h" #include "server.h" #include "prefs.h" #include "cmdline.h" #include "storage.h" #include "support.h" #include "anongame_maplists.h" #include "anongame.h" #include "connection.h" #include "game.h" #include "timer.h" #include "channel.h" #include "helpfile.h" #include "ipban.h" #include "adbanner.h" #include "autoupdate.h" #include "versioncheck.h" #include "news.h" #include "watch.h" #include "output.h" #include "attrlayer.h" #include "account.h" #include "ladder.h" #include "character.h" #include "tracker.h" #include "command_groups.h" #include "alias_command.h" #include "tournament.h" #include "icons.h" #include "anongame_infos.h" #include "anongame_wol.h" #include "clan.h" #include "team.h" #include "realm.h" #include "topic.h" #include "handle_apireg.h" #include "i18n.h" #include "userlog.h" #ifdef WIN32 #include "win32/windump.h" #endif #include "common/setup_after.h" #ifdef WITH_LUA #include "luainterface.h" #endif /* out of memory safety */ #define OOM_SAFE_MEM 1000000 /* 1 Mbyte of safety memory */ using namespace pvpgn::bnetd; using namespace pvpgn; static int * emergency_mem = new int[1048576]; // 1 MiB void new_oom_handler() { delete[] emergency_mem; eventlog(eventlog_level_fatal, __FUNCTION__, "out of memory, forcing immediate shutdown"); std::abort(); } // FIXME: Use new instead of malloc everywhere void *oom_buffer = NULL; static int bnetd_oom_handler(void) { /* no safety buffer, sorry :( */ if (!oom_buffer) return 0; /* free the safety buffer hoping next allocs will succeed */ free(oom_buffer); oom_buffer = NULL; eventlog(eventlog_level_fatal, __FUNCTION__, "out of memory, forcing immediate shutdown"); /* shutdown immediatly */ server_quit_delay(-1); return 1; /* ask xalloc codes to retry the allocation request */ } static int oom_setup(void) { /* use calloc so it initilizez the memory so it will make some lazy * allocators really alocate it (ex. the linux kernel) */ oom_buffer = calloc(1, OOM_SAFE_MEM); if (!oom_buffer) return -1; xalloc_setcb(bnetd_oom_handler); return 0; } static void oom_free(void) { free(oom_buffer); oom_buffer = NULL; } FILE *hexstrm = NULL; char serviceLongName[] = PVPGN_SOFTWARE " service"; char serviceName[] = "pvpgn"; char serviceDescription[] = "Player vs. Player Gaming Network - Server"; /* * by quetzal. indicates service status * -1 - not in service mode * 0 - stopped * 1 - running * 2 - paused */ int g_ServiceStatus = -1; /* added some more std::exit status --> put in "compat/exitstatus.h" ??? */ #define STATUS_OOM_FAILURE 20 #define STATUS_STORAGE_FAILURE 30 #define STATUS_PSOCK_FAILURE 35 #define STATUS_MAPLISTS_FAILURE 40 #define STATUS_MATCHLISTS_FAILURE 50 #define STATUS_LADDERLIST_FAILURE 60 #define STATUS_WAR3XPTABLES_FAILURE 70 #define STATUS_SUPPORT_FAILURE 80 #define STATUS_FDWATCH_FAILURE 90 int pre_server_startup(void); void post_server_shutdown(int status); int eventlog_startup(void); int fork_bnetd(int foreground); char * write_to_pidfile(void); void pvpgn_greeting(); int eventlog_startup(void) { char const * levels; char * temp; char const * tok; eventlog_clear_level(); if ((levels = prefs_get_loglevels())) { temp = xstrdup(levels); tok = std::strtok(temp, ","); /* std::strtok modifies the string it is passed */ while (tok) { if (eventlog_add_level(tok) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not add std::log level \"{}\"", tok); tok = std::strtok(NULL, ","); } xfree(temp); } #ifdef WIN32_GUI if (cmdline_get_gui()){ eventlog_add_level(eventlog_get_levelname_str(eventlog_level_gui)); } #endif if (eventlog_open(prefs_get_logfile()) < 0) { if (prefs_get_logfile()) { eventlog(eventlog_level_fatal, __FUNCTION__, "could not use file \"{}\" for the eventlog (exiting)", prefs_get_logfile()); } else { eventlog(eventlog_level_fatal, __FUNCTION__, "no logfile specified in configuration file \"{}\" (exiting)", cmdline_get_preffile()); } return -1; } eventlog(eventlog_level_info, __FUNCTION__, "logging event levels: {}", prefs_get_loglevels()); return 0; } int fork_bnetd(int foreground) { #ifdef DO_DAEMONIZE int pid; if (!foreground) { if (chdir("/") < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not change working directory to / (chdir: {})", std::strerror(errno)); return -1; } switch ((pid = fork())) { case -1: eventlog(eventlog_level_error, __FUNCTION__, "could not fork (fork: {})", std::strerror(errno)); return -1; case 0: /* child */ break; default: /* parent */ return pid; } #ifndef WITH_D2 close(STDINFD); close(STDOUTFD); close(STDERRFD); #endif # ifdef HAVE_SETPGID if (setpgid(0, 0) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgid: {})", std::strerror(errno)); return -1; } # else # ifdef HAVE_SETPGRP # ifdef SETPGRP_VOID if (setpgrp() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgrp: {})", std::strerror(errno)); return -1; } # else if (setpgrp(0, 0) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgrp: {})", std::strerror(errno)); return -1; } # endif # else # ifdef HAVE_SETSID if (setsid() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setsid: {})", std::strerror(errno)); return -1; } # else # error "One of setpgid(), setpgrp(), or setsid() is required" # endif # endif # endif } return 0; #endif return 0; } char * write_to_pidfile(void) { char *pidfile = xstrdup(prefs_get_pidfile()); if (pidfile) { if (pidfile[0] == '\0') { xfree((void *)pidfile); /* avoid warning */ return NULL; } #ifdef HAVE_GETPID std::FILE * fp; if (!(fp = std::fopen(pidfile, "w"))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to open pid file \"{}\" for writing (std::fopen: {})", pidfile, std::strerror(errno)); xfree((void *)pidfile); /* avoid warning */ return NULL; } else { std::fprintf(fp, "%u", (unsigned int)getpid()); if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close pid file \"{}\" after writing (std::fclose: {})", pidfile, std::strerror(errno)); } #else eventlog(eventlog_level_warn, __FUNCTION__, "no getpid() std::system call, disable pid file in bnetd.conf"); xfree((void *)pidfile); /* avoid warning */ return NULL; #endif } return pidfile; } /* Initialize config files */ int pre_server_startup(void) { pvpgn_greeting(); if (oom_setup() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "OOM init failed"); return STATUS_OOM_FAILURE; } std::set_new_handler(new_oom_handler); if (storage_init(prefs_get_storage_path()) < 0) { eventlog(eventlog_level_error, "pre_server_startup", "storage init failed"); return STATUS_STORAGE_FAILURE; } if (psock_init() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not initialize socket functions"); return STATUS_PSOCK_FAILURE; } if (support_check_files(prefs_get_supportfile()) < 0) { eventlog(eventlog_level_error, "pre_server_startup", "some needed files are missing"); eventlog(eventlog_level_error, "pre_server_startup", "please make sure you installed the supportfiles in {}", prefs_get_filedir()); return STATUS_SUPPORT_FAILURE; } if (anongame_maplists_create() < 0) { eventlog(eventlog_level_error, "pre_server_startup", "could not load maps"); return STATUS_MAPLISTS_FAILURE; } if (anongame_matchlists_create() < 0) { eventlog(eventlog_level_error, "pre_server_startup", "could not create matchlists"); return STATUS_MATCHLISTS_FAILURE; } if (fdwatch_init(prefs_get_max_connections())) { eventlog(eventlog_level_error, __FUNCTION__, "error initilizing fdwatch"); return STATUS_FDWATCH_FAILURE; } i18n_load(); connlist_create(); gamelist_create(); timerlist_create(); server_set_hostname(); channellist_create(); apireglist_create(); if (helpfile_init(prefs_get_helpfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load helpfile"); ipbanlist_create(); if (ipbanlist_load(prefs_get_ipbanfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load IP ban list"); try { if (AdBannerList.is_loaded()) { AdBannerList.unload(); } AdBannerList.load(prefs_get_adfile()); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } if (autoupdate_load(prefs_get_mpqfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load autoupdate list"); if (!load_versioncheck_conf(prefs_get_versioncheck_file())) { eventlog(eventlog_level_error, __FUNCTION__, "could not load versioncheck list"); } if (news_load(prefs_get_newsfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load news list"); watchlist.reset(new WatchComponent()); output_init(); attrlayer_init(); accountlist_create(); if (ladder_createxptable(prefs_get_xplevel_file(), prefs_get_xpcalc_file()) < 0) { eventlog(eventlog_level_error, "pre_server_startup", "could not load WAR3 xp calc tables"); return STATUS_WAR3XPTABLES_FAILURE; } ladders.load(); ladders.update(); if (characterlist_create("") < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load character list"); if (prefs_get_track()) /* setup the tracking mechanism */ tracker_set_servers(prefs_get_trackserv_addrs()); if (command_groups_load(prefs_get_command_groups_file()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load command_groups list"); aliasfile_load(prefs_get_aliasfile()); if (trans_load(prefs_get_transfile(), TRANS_BNETD) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load trans list"); tournament_init(prefs_get_tournament_file()); customicons_load(prefs_get_customicons_file()); anongame_infos_load(prefs_get_anongame_infos_file()); anongame_wol_matchlist_create(); clanlist_load(); teamlist_load(); if (realmlist_create(prefs_get_realmfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load realm list"); //topiclist_load(std::string(prefs_get_topicfile())); userlog_init(); #ifdef WITH_LUA lua_load(prefs_get_scriptdir()); #endif return 0; } void post_server_shutdown(int status) { switch (status) { case 0: //topiclist_unload(); realmlist_destroy(); teamlist_unload(); clanlist_unload(); tournament_destroy(); customicons_unload(); anongame_infos_unload(); anongame_wol_matchlist_destroy(); trans_unload(); aliasfile_unload(); command_groups_unload(); tracker_set_servers(NULL); characterlist_destroy(); ladder_destroyxptable(); case STATUS_WAR3XPTABLES_FAILURE: case STATUS_LADDERLIST_FAILURE: ladders.save(); accountlist_destroy(); attrlayer_cleanup(); watchlist.reset(); news_unload(); unload_versioncheck_conf(); autoupdate_unload(); ipbanlist_save(prefs_get_ipbanfile()); ipbanlist_destroy(); helpfile_unload(); apireglist_destroy(); channellist_destroy(); server_clear_hostname(); timerlist_destroy(); gamelist_destroy(); connlist_destroy(); fdwatch_close(); case STATUS_FDWATCH_FAILURE: anongame_matchlists_destroy(); case STATUS_MATCHLISTS_FAILURE: anongame_maplists_destroy(); case STATUS_MAPLISTS_FAILURE: case STATUS_SUPPORT_FAILURE: if (psock_deinit()) eventlog(eventlog_level_error, __FUNCTION__, "got error from psock_deinit()"); case STATUS_PSOCK_FAILURE: storage_close(); case STATUS_STORAGE_FAILURE: oom_free(); delete[] emergency_mem; case STATUS_OOM_FAILURE: case -1: break; default: eventlog(eventlog_level_error, __FUNCTION__, "got bad status \"{}\" during shutdown", status); } return; } void pvpgn_greeting() { #ifdef HAVE_GETPID eventlog(eventlog_level_info, __FUNCTION__, PVPGN_SOFTWARE " " PVPGN_VERSION " process {}", getpid()); #else eventlog(eventlog_level_info, __FUNCTION__, PVPGN_SOFTWARE" version "PVPGN_VERSION); #endif struct utsname utsbuf = {}; if (uname(&utsbuf) == 0) { eventlog(eventlog_level_info, __FUNCTION__, "running on {} ({} {}, {})", utsbuf.sysname, utsbuf.version, utsbuf.release, utsbuf.machine); } else { eventlog(eventlog_level_info, __FUNCTION__, "uname() failed"); } #ifdef WIN32_GUI gui_lvprintf(eventlog_level_info, "You are currently running " PVPGN_SOFTWARE " " PVPGN_VERSION "\n"); gui_lvprintf(eventlog_level_info, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"); gui_lvprintf(eventlog_level_info, "If you need support:\n"); gui_lvprintf(eventlog_level_info, " * Create an issue at https://github.com/pvpgn/pvpgn-server\n"); gui_lvprintf(eventlog_level_info, "\nServer is now running.\n"); gui_lvprintf(eventlog_level_info, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"); #else std::printf("You are currently running " PVPGN_SOFTWARE " " PVPGN_VERSION "\n"); std::printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"); std::printf("If you need support:\n"); std::printf(" * Create an issue at https://github.com/pvpgn/pvpgn-server\n"); std::printf("\nServer is now running.\n"); std::printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n"); #endif return; } #ifdef WIN32_GUI extern int app_main(int argc, char ** argv) #else extern int main(int argc, char ** argv) #endif { #ifdef WIN32 // create a dump file whenever the gateway crashes SetUnhandledExceptionFilter(unhandled_handler); #endif try { int a; char *pidfile; if ((a = cmdline_load(argc, argv)) != 1) return a; #ifdef DO_DAEMONIZE if ((a = fork_bnetd(cmdline_get_foreground())) != 0) return a < 0 ? a : 0; /* dont return code != 0 when things are OK! */ #endif eventlog_set(stderr); /* errors to eventlog from here on... */ if (prefs_load(cmdline_get_preffile()) < 0) { eventlog(eventlog_level_fatal, __FUNCTION__, "could not parse configuration file (exiting)"); return -1; } /* Start logging to std::log file */ if (eventlog_startup() == -1) return -1; /* eventlog goes to std::log file from here on... */ /* Give up root privileges */ /* Hakan: That's way too late to give up root privileges... Have to look for a better place */ if (give_up_root_privileges(prefs_get_effective_user(), prefs_get_effective_group()) < 0) { eventlog(eventlog_level_fatal, __FUNCTION__, "could not give up privileges (exiting)"); return -1; } /* Write the pidfile */ pidfile = write_to_pidfile(); if (cmdline_get_hexfile()) { if (!(hexstrm = std::fopen(cmdline_get_hexfile(), "w"))) eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for writing the hexdump (std::fopen: {})", cmdline_get_hexfile(), std::strerror(errno)); else std::fprintf(hexstrm, "# dump generated by " PVPGN_SOFTWARE " version " PVPGN_VERSION "\n"); } /* Run the pre server stuff */ a = pre_server_startup(); /* now process connections and network traffic */ if (a == 0) { if (server_process() < 0) eventlog(eventlog_level_fatal, __FUNCTION__, "failed to initialize network (exiting)"); } // run post server stuff and exit post_server_shutdown(a); // Close hexfile if (hexstrm) { std::fprintf(hexstrm, "# end of dump\n"); if (std::fclose(hexstrm) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close hexdump file \"{}\" after writing (std::fclose: {})", cmdline_get_hexfile(), std::strerror(errno)); } // Delete pidfile if (pidfile) { if (std::remove(pidfile) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove pid file \"{}\" (std::remove: {})", pidfile, std::strerror(errno)); xfree((void *)pidfile); /* avoid warning */ } if (a == 0) eventlog(eventlog_level_info, __FUNCTION__, "server has shut down"); prefs_unload(); cmdline_unload(); //guiOnClose #ifndef WIN32_GUI eventlog_close(); #endif if (a == 0) return 0; return -1; } catch (const std::exception& ex) { std::cerr << "FATAL ERROR: " << ex.what() << std::endl; } catch (...) { std::cerr << "UNKNOWN EXCEPTION TYPE" << std::endl; } return -1; } pvpgn-1.99.7.2.1+dfsg/src/bnetd/message.cpp000066400000000000000000001465221462640461400202250ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2001,2002 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define MESSAGE_INTERNAL_ACCESS #include "message.h" #include #include #include #include "compat/gethostname.h" #include "common/xalloc.h" #include "common/eventlog.h" #include "common/addr.h" #include "common/tag.h" #include "common/packet.h" #include "common/bot_protocol.h" #include "common/bn_type.h" #include "common/list.h" #include "common/util.h" #include "common/xstring.h" #include "account.h" #include "account_wrap.h" #include "channel.h" #include "channel_conv.h" #include "game.h" #include "mail.h" #include "prefs.h" #include "connection.h" #include "irc.h" #include "command.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static int message_telnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags); static int message_bot_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags); static int message_bnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags); static t_packet * message_cache_lookup(t_message * message, t_connection *dst, unsigned int flags); static char const * message_type_get_str(t_message_type type) { switch (type) { case message_type_adduser: return "adduser"; case message_type_join: return "join"; case message_type_part: return "part"; case message_type_whisper: return "whisper"; case message_type_talk: return "talk"; case message_type_broadcast: return "broadcast"; case message_type_channel: return "channel"; case message_type_userflags: return "userflags"; case message_type_whisperack: return "whisperack"; case message_type_friendwhisperack: //[zap-zero] 20020518 return "friendwhisperack"; case message_type_channelfull: return "channelfull"; case message_type_channeldoesnotexist: return "channeldoesnotexist"; case message_type_channelrestricted: return "channelrestricted"; case message_type_info: return "info"; case message_type_error: return "error"; case message_type_emote: return "emote"; case message_type_uniqueid: return "uniqueid"; case message_type_notice: return "notice"; case message_type_nick: return "nick"; case message_type_namreply: return "namreply"; case message_type_topic: return "topic"; case message_type_mode: return "mode"; case message_type_host: return "host"; case message_type_invmsg: return "invmsg"; case message_type_page: return "page"; case message_type_kick: return "kick"; case message_type_quit: return "quit"; case message_wol_joingame: return "wol_joingame"; case message_type_gameopt_talk: return "gameopt_talk"; case message_type_gameopt_whisper: return "gameopt_whisper"; case message_wol_start_game: return "wol_start_game"; case message_wol_advertr: return "wol_advertr"; case message_wol_chanchk: return "wol_chanchk"; case message_wol_userip: return "wol_userip"; case message_type_null: return "null"; default: return "UNKNOWN"; } } /* make sure none of the expanded format symbols is longer than this (with null) */ #define MAX_INC 64 extern char * message_format_line(t_connection const * c, char const * in) { char * out; unsigned int inpos; unsigned int outpos; unsigned int outlen = MAX_INC; unsigned int inlen; char clienttag_str[5]; out = (char*)xmalloc(outlen + 1); inlen = std::strlen(in); out[0] = 'I'; for (inpos = 0, outpos = 1; inpos < inlen; inpos++) { if (in[inpos] != '%') { out[outpos] = in[inpos]; outpos += 1; } else switch (in[++inpos]) { case '%': out[outpos++] = '%'; break; case 'a': std::sprintf(&out[outpos], "%u", accountlist_get_length()); outpos += std::strlen(&out[outpos]); break; case 'c': std::sprintf(&out[outpos], "%d", channellist_get_length()); outpos += std::strlen(&out[outpos]); break; case 'g': std::sprintf(&out[outpos], "%d", gamelist_get_length()); outpos += std::strlen(&out[outpos]); break; case 'h': if (gethostname(&out[outpos], MAX_INC) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not get hostname (gethostname: {})", std::strerror(errno)); std::strcpy(&out[outpos], "localhost"); /* not much else you can do */ } outpos += std::strlen(&out[outpos]); break; case 'i': std::sprintf(&out[outpos], UID_FORMATF, conn_get_userid(c)); outpos += std::strlen(&out[outpos]); break; case 'l': { char const * tname; std::strncpy(&out[outpos], (tname = (conn_get_chatname(c) ? conn_get_chatname(c) : conn_get_loggeduser(c))), MAX_USERNAME_LEN - 1); conn_unget_chatname(c, tname); } out[outpos + MAX_USERNAME_LEN - 1] = '\0'; outpos += std::strlen(&out[outpos]); break; case 'm': { unsigned mails = check_mail(c); if (mails > 0) std::sprintf(&out[outpos], "You have %u message(s) in your mailbox.", mails); else std::strcpy(&out[outpos], "You have no mail."); outpos += std::strlen(&out[outpos]); break; } case 'r': std::strncpy(&out[outpos], addr_num_to_ip_str(conn_get_addr(c)), MAX_INC - 1); out[outpos + MAX_INC - 1] = '\0'; outpos += std::strlen(&out[outpos]); break; case 's': std::sprintf(&out[outpos], "%s", prefs_get_servername()); outpos += std::strlen(&out[outpos]); break; case 't': std::sprintf(&out[outpos], "%s", tag_uint_to_str(clienttag_str, conn_get_clienttag(c))); outpos += std::strlen(&out[outpos]); break; case 'u': std::sprintf(&out[outpos], "%d", connlist_login_get_length()); outpos += std::strlen(&out[outpos]); break; case 'v': std::strcpy(&out[outpos], PVPGN_SOFTWARE " " PVPGN_VERSION); outpos += std::strlen(&out[outpos]); break; case 'C': /* simulated command */ out[0] = 'C'; break; case 'B': /* BROADCAST */ out[0] = 'B'; break; case 'E': /* ERROR */ out[0] = 'E'; break; case 'G': std::sprintf(&out[outpos], "%d", game_get_count_by_clienttag(conn_get_clienttag(c))); outpos += std::strlen(&out[outpos]); break; case 'H': std::strcpy(&out[outpos], prefs_get_contact_name()); outpos += std::strlen(&out[outpos]); break; case 'I': /* INFO */ out[0] = 'I'; break; case 'M': /* MESSAGE */ out[0] = 'M'; break; case 'N': std::strcpy(&out[outpos], clienttag_get_title(conn_get_clienttag(c))); outpos += std::strlen(&out[outpos]); break; case 'T': /* EMOTE */ out[0] = 'T'; break; case 'U': std::sprintf(&out[outpos], "%d", conn_get_user_count_by_clienttag(conn_get_clienttag(c))); outpos += std::strlen(&out[outpos]); break; case 'W': /* INFO */ out[0] = 'W'; break; default: eventlog(eventlog_level_warn, __FUNCTION__, "bad formatter \"%{}\"", in[inpos - 1]); } if ((outpos + MAX_INC) >= outlen) { char * newout; outlen += MAX_INC; newout = (char*)xrealloc(out, outlen); out = newout; } } out[outpos] = '\0'; return out; } static int message_telnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags) { char * msgtemp; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } switch (type) { case message_type_uniqueid: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } msgtemp = (char*)xmalloc(std::strlen(text) + 32); std::sprintf(msgtemp, "Your unique name: %s\r\n", text); break; case message_type_adduser: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(std::strlen(tname) + 32); std::sprintf(msgtemp, "[%s is here]\r\n", tname); conn_unget_chatcharname(me, tname); } break; case message_type_join: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (me == dst) return -1; else { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(std::strlen(tname) + 32); std::sprintf(msgtemp, "[%s enters]\r\n", tname); conn_unget_chatcharname(me, tname); } break; case message_type_part: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(std::strlen(tname) + 32); std::sprintf(msgtemp, "[%s leaves]\r\n", tname); conn_unget_chatcharname(me, tname); } break; case message_type_kick: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(std::strlen(tname) + 32); std::sprintf(msgtemp, "[%s has been kicked]\r\n", tname); conn_unget_chatcharname(me, tname); } break; case message_type_quit: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(std::strlen(tname) + 32); std::sprintf(msgtemp, "[%s quit]\r\n", tname); conn_unget_chatcharname(me, tname); } break; case message_type_whisper: case message_type_notice: case message_type_page: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; char const * newtext; if (me) tname = conn_get_chatcharname(me, dst); else tname = prefs_get_servername(); if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(std::strlen(tname) + 8 + std::strlen(newtext) + 4); std::sprintf(msgtemp, " %s\r\n", tname, newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(16 + std::strlen(tname)); std::sprintf(msgtemp, " \r\n", tname); } if (me) conn_unget_chatcharname(me, tname); } break; case message_type_talk: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; char const * newtext; if (me) tname = conn_get_chatcharname(me, dst); else tname = prefs_get_servername(); if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(std::strlen(tname) + 4 + std::strlen(newtext) + 4); std::sprintf(msgtemp, "<%s> %s\r\n", tname, newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(std::strlen(tname) + 8); std::sprintf(msgtemp, "<%s> \r\n", tname); } if (me) conn_unget_chatcharname(me, tname); } break; case message_type_broadcast: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * newtext; if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(16 + std::strlen(newtext) + 4); std::sprintf(msgtemp, "Broadcast: %s\r\n", newtext); /* FIXME: show source? */ xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(16); std::sprintf(msgtemp, "Broadcast: \r\n"); /* FIXME: show source? */ } } break; case message_type_channel: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } msgtemp = (char*)xmalloc(std::strlen(text) + 32); std::sprintf(msgtemp, "Joining channel: \"%s\"\r\n", text); break; case message_type_userflags: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } msgtemp = xstrdup(""); break; case message_type_whisperack: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } { char const * tname; char const * newtext; tname = conn_get_chatcharname(me, dst); if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(std::strlen(tname) + 8 + std::strlen(newtext) + 4); std::sprintf(msgtemp, " %s\r\n", tname, newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(std::strlen(tname) + 8 + std::strlen(text) + 4); std::sprintf(msgtemp, " %s\r\n", tname, text); } conn_unget_chatcharname(me, tname); } break; case message_type_friendwhisperack: // [zap-zero] 20020518 if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } { char const * newtext; if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(14 + 8 + std::strlen(newtext) + 4); std::sprintf(msgtemp, " %s\r\n", newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(14 + 8 + std::strlen(text) + 4); std::sprintf(msgtemp, " %s\r\n", text); } } break; case message_type_channelfull: /* FIXME */ msgtemp = xstrdup(""); break; case message_type_channeldoesnotexist: /* FIXME */ msgtemp = xstrdup(""); break; case message_type_channelrestricted: /* FIXME */ msgtemp = xstrdup(""); break; case message_type_info: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } { char const * newtext; if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(std::strlen(newtext) + 4); std::sprintf(msgtemp, "%s\r\n", newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(std::strlen(text) + 4); std::sprintf(msgtemp, "%s\r\n", text); } } break; case message_type_error: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } { char const * newtext; if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(8 + std::strlen(newtext) + 4); std::sprintf(msgtemp, "ERROR: %s\r\n", newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(8 + std::strlen(text) + 4); std::sprintf(msgtemp, "ERROR: %s\r\n", text); } } break; case message_type_emote: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; char const * newtext; tname = conn_get_chatcharname(me, dst); if ((newtext = escape_chars(text, std::strlen(text)))) { msgtemp = (char*)xmalloc(std::strlen(tname) + 4 + std::strlen(newtext) + 4); std::sprintf(msgtemp, "<%s %s>\r\n", tname, newtext); xfree((void *)newtext); /* avoid warning */ } else { msgtemp = (char*)xmalloc(std::strlen(tname) + 4 + std::strlen(text) + 4); std::sprintf(msgtemp, "<%s %s>\r\n", tname, text); } conn_unget_chatcharname(me, tname); } break; case message_type_mode: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(std::strlen(tname) + 32); std::sprintf(msgtemp, "%s change mode: %s\r\n", tname, text); conn_unget_chatcharname(me, tname); } break; default: eventlog(eventlog_level_error, __FUNCTION__, "got bad message type {}", (int)type); return -1; } { int retval; retval = packet_append_ntstring(packet, msgtemp); xfree(msgtemp); return retval; } } static int message_bot_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags) { char * msgtemp; char clienttag_str[5]; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } /* special-case the login banner so it doesn't have numbers * at the start of each line */ if (me && conn_get_state(me) != conn_state_loggedin && conn_get_state(me) != conn_state_destroy && type != message_type_null) /* this does not apply for NULL messages */ { if (!text) { #if 0 /* battle.net actually sends them during login */ if (type == message_type_null) return 0; /* don't display null messages during the login */ #endif eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for non-loggedin state"); return -1; } msgtemp = (char*)xmalloc(std::strlen(text) + 4); std::sprintf(msgtemp, "%s\r\n", text); } else switch (type) { case message_type_null: msgtemp = (char*)xmalloc(32); std::sprintf(msgtemp, "%u %s\r\n", EID_NULL, "NULL"); break; case message_type_uniqueid: /* FIXME: need to send this for some bots, also needed to support guest accounts */ if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } msgtemp = (char*)xmalloc(std::strlen(text) + 32); std::sprintf(msgtemp, "%u %s %s\r\n", EID_UNIQUENAME, "NAME", text); break; case message_type_adduser: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 32); std::sprintf(msgtemp, "%u %s %s %04x [%s]\r\n", EID_SHOWUSER, "USER", tname, conn_get_flags(me) | dstflags, tag_uint_to_str(clienttag_str, conn_get_fake_clienttag(me))); conn_unget_chatcharname(me, tname); } break; case message_type_join: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (me == dst) return -1; else { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 32); std::sprintf(msgtemp, "%u %s %s %04x [%s]\r\n", EID_JOIN, "JOIN", tname, conn_get_flags(me) | dstflags, tag_uint_to_str(clienttag_str, conn_get_fake_clienttag(me))); conn_unget_chatcharname(me, tname); } break; case message_type_part: case message_type_kick: case message_type_quit: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 32); std::sprintf(msgtemp, "%u %s %s %04x\r\n", EID_LEAVE, "LEAVE", tname, conn_get_flags(me) | dstflags); conn_unget_chatcharname(me, tname); } break; case message_type_whisper: case message_type_notice: case message_type_page: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; if (me) tname = conn_get_chatcharname(me, dst); else tname = prefs_get_servername(); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 32 + std::strlen(text)); std::sprintf(msgtemp, "%u %s %s %04x \"%s\"\r\n", EID_WHISPER, "WHISPER", tname, me ? conn_get_flags(me) | dstflags : dstflags, text); if (me) conn_unget_chatcharname(me, tname); } break; case message_type_talk: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 32 + std::strlen(text)); std::sprintf(msgtemp, "%u %s %s %04x \"%s\"\r\n", EID_TALK, "TALK", tname, conn_get_flags(me) | dstflags, text); conn_unget_chatcharname(me, tname); } break; case message_type_broadcast: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ msgtemp = (char*)xmalloc(32 + 32 + std::strlen(text)); std::sprintf(msgtemp, "%u %s \"%s\"\r\n", EID_BROADCAST, "_", text); /* FIXME: what does this look like on Battle.net? */ break; case message_type_channel: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } msgtemp = (char*)xmalloc(32 + std::strlen(text)); std::sprintf(msgtemp, "%u %s \"%s\"\r\n", EID_CHANNEL, "CHANNEL", text); break; case message_type_userflags: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 16); std::sprintf(msgtemp, "%u %s %s %04x\r\n", EID_USERFLAGS, "USER", tname, conn_get_flags(me) | dstflags); conn_unget_chatcharname(me, tname); } break; case message_type_whisperack: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 32 + std::strlen(text)); std::sprintf(msgtemp, "%u %s %s %04x \"%s\"\r\n", EID_WHISPERSENT, "WHISPER", tname, conn_get_flags(me) | dstflags, text); conn_unget_chatcharname(me, tname); } break; case message_type_friendwhisperack: // [zap-zero] 20020518 if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } { msgtemp = (char*)xmalloc(32 + 16 + 32 + std::strlen(text)); std::sprintf(msgtemp, "%u %s \"your friends\" %04x \"%s\"\r\n", EID_WHISPERSENT, "WHISPER", conn_get_flags(me) | dstflags, text); } break; case message_type_channelfull: msgtemp = (char*)xmalloc(32); std::sprintf(msgtemp, "%u \r\n", EID_CHANNELFULL); /* FIXME */ break; case message_type_channeldoesnotexist: msgtemp = (char*)xmalloc(32); std::sprintf(msgtemp, "%u \r\n", EID_CHANNELDOESNOTEXIST); /* FIXME */ break; case message_type_channelrestricted: msgtemp = (char*)xmalloc(32); std::sprintf(msgtemp, "%u \r\n", EID_CHANNELRESTRICTED); /* FIXME */ break; case message_type_info: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } msgtemp = (char*)xmalloc(32 + 16 + std::strlen(text)); std::sprintf(msgtemp, "%u %s \"%s\"\r\n", EID_INFO, "INFO", text); break; case message_type_error: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } msgtemp = (char*)xmalloc(32 + 16 + std::strlen(text)); std::sprintf(msgtemp, "%u %s \"%s\"\r\n", EID_ERROR, "ERROR", text); break; case message_type_emote: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ { char const * tname; tname = conn_get_chatcharname(me, dst); msgtemp = (char*)xmalloc(32 + std::strlen(tname) + 32 + std::strlen(text)); std::sprintf(msgtemp, "%u %s %s %04x \"%s\"\r\n", EID_EMOTE, "EMOTE", tname, conn_get_flags(me) | dstflags, text); conn_unget_chatcharname(me, tname); } break; default: eventlog(eventlog_level_error, __FUNCTION__, "got bad message type {}", (int)type); return -1; } if (std::strlen(msgtemp) > MAX_MESSAGE_LEN) msgtemp[MAX_MESSAGE_LEN] = '\0'; /* now truncate to max size */ { int retval; retval = packet_append_ntstring(packet, msgtemp); xfree(msgtemp); return retval; } } static int message_bnet_format(t_packet * packet, t_message_type type, t_connection * me, t_connection * dst, char const * text, unsigned int dstflags) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } // empty messages are needed to separate text (or example, in bnhelp output) if (text && text[0] == '\0') text = " "; /* empty messages crash some clients, just send whitespace */ std::string temp; if (text && (std::strlen(text) > MAX_MESSAGE_LEN)) { /* PELISH: We are trying to support MAX_IRC_MESSAGE_LEN for IRC and also MAX_MESSAGE_LEN for bnet */ temp = std::string(text, text + MAX_MESSAGE_LEN); text = temp.c_str(); } packet_set_size(packet, sizeof(t_server_message)); packet_set_type(packet, SERVER_MESSAGE); bn_int_set(&packet->u.server_message.player_ip, SERVER_MESSAGE_PLAYER_IP_DUMMY); bn_int_nset(&packet->u.server_message.account_num, SERVER_MESSAGE_ACCOUNT_NUM); bn_int_set(&packet->u.server_message.reg_auth, SERVER_MESSAGE_REG_AUTH); switch (type) { case message_type_adduser: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_ADDUSER); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; char const * playerinfo; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); if ((conn_get_clienttag(me) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(me) == CLIENTTAG_WAR3XP_UINT)) playerinfo = conn_get_w3_playerinfo(me); else playerinfo = conn_get_playerinfo(me); if (playerinfo == NULL) { playerinfo = ""; } packet_append_string(packet, playerinfo); } break; case message_type_join: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_JOIN); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); if (me == dst) return -1; else { char const * tname; char const * playerinfo; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); if ((conn_get_clienttag(me) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(me) == CLIENTTAG_WAR3XP_UINT)) playerinfo = conn_get_w3_playerinfo(me); else playerinfo = conn_get_playerinfo(me); if (playerinfo == NULL) { playerinfo = ""; } packet_append_string(packet, playerinfo); } break; case message_type_part: case message_type_kick: case message_type_quit: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_PART); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); packet_append_string(packet, ""); } break; case message_type_whisper: case message_type_notice: case message_type_page: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_WHISPER); bn_int_set(&packet->u.server_message.flags, me ? conn_get_flags(me) | dstflags : dstflags); bn_int_set(&packet->u.server_message.latency, me ? conn_get_latency(me) : 0); if (me) { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); } else packet_append_string(packet, prefs_get_servername()); packet_append_string(packet, text); break; case message_type_talk: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_TALK); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); packet_append_string(packet, text); } break; case message_type_broadcast: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_BROADCAST); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); packet_append_string(packet, text); } break; case message_type_channel: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_CHANNEL); { t_channel const * channel; if (!(channel = conn_get_channel(me))) bn_int_set(&packet->u.server_message.flags, 0); else bn_int_set(&packet->u.server_message.flags, cflags_to_bncflags(channel_get_flags(channel))); } bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; tname = conn_get_chatname(me); packet_append_string(packet, tname); conn_unget_chatname(me, tname); packet_append_string(packet, text); } break; case message_type_userflags: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_USERFLAGS); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; char const * playerinfo; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); if ((conn_get_clienttag(me) == CLIENTTAG_WARCRAFT3_UINT) || (conn_get_clienttag(me) == CLIENTTAG_WAR3XP_UINT)) playerinfo = conn_get_w3_playerinfo(me); else playerinfo = conn_get_playerinfo(me); if (playerinfo == NULL) { playerinfo = ""; } packet_append_string(packet, playerinfo); } break; case message_type_whisperack: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_WHISPERACK); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); packet_append_string(packet, text); } break; case message_type_friendwhisperack: // [zap-zero] 20020518 if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_WHISPERACK); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { packet_append_string(packet, "your friends"); packet_append_string(packet, text); } break; case message_type_channelfull: /* FIXME */ bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_CHANNELFULL); bn_int_set(&packet->u.server_message.flags, 0); bn_int_set(&packet->u.server_message.latency, 0); packet_append_string(packet, ""); packet_append_string(packet, ""); break; case message_type_channeldoesnotexist: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_CHANNELDOESNOTEXIST); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; tname = conn_get_chatname(me); packet_append_string(packet, tname); conn_unget_chatname(me, tname); packet_append_string(packet, text); } break; case message_type_channelrestricted: /* FIXME */ bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_CHANNELRESTRICTED); bn_int_set(&packet->u.server_message.flags, 0); bn_int_set(&packet->u.server_message.latency, 0); packet_append_string(packet, ""); packet_append_string(packet, ""); break; case message_type_info: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_INFO); bn_int_set(&packet->u.server_message.flags, 0); bn_int_set(&packet->u.server_message.latency, 0); packet_append_string(packet, ""); packet_append_string(packet, text); break; case message_type_error: if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_ERROR); bn_int_set(&packet->u.server_message.flags, 0); bn_int_set(&packet->u.server_message.latency, 0); packet_append_string(packet, ""); packet_append_string(packet, text); break; case message_type_emote: if (!me) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection for {}", message_type_get_str(type)); return -1; } if (!text) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL text for {}", message_type_get_str(type)); return -1; } if (dstflags&MF_X) return -1; /* player is ignored */ bn_int_set(&packet->u.server_message.type, SERVER_MESSAGE_TYPE_EMOTE); bn_int_set(&packet->u.server_message.flags, conn_get_flags(me) | dstflags); bn_int_set(&packet->u.server_message.latency, conn_get_latency(me)); { char const * tname; tname = conn_get_chatcharname(me, dst); packet_append_string(packet, tname); conn_unget_chatcharname(me, tname); packet_append_string(packet, text); } break; default: eventlog(eventlog_level_error, __FUNCTION__, "got bad message type {}", (int)type); return -1; } return 0; } extern t_message * message_create(t_message_type type, t_connection * src, char const * text) { t_message * message; message = (t_message*)xmalloc(sizeof(t_message)); message->num_cached = 0; message->packets = NULL; message->classes = NULL; message->dstflags = NULL; message->mclasses = NULL; message->type = type; message->src = src; message->text = text; return message; } extern int message_destroy(t_message * message) { unsigned int i; if (!message) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL message"); return -1; } if (message->packets) { for (i = 0; i < message->num_cached; i++) { if (message->packets[i]) packet_del_ref(message->packets[i]); } xfree(message->packets); } if (message->classes) xfree(message->classes); if (message->dstflags) xfree(message->dstflags); if (message->mclasses) xfree(message->mclasses); xfree(message); return 0; } static t_packet * message_cache_lookup(t_message * message, t_connection *dst, unsigned int dstflags) { unsigned int i = 0; t_packet * packet; t_message_class mclass; t_conn_class cclass; if (!message) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL message"); return NULL; } cclass = conn_get_class(dst); mclass = conn_get_message_class(message->src, dst); if (message->classes && message->mclasses && message->packets && message->dstflags) { for (i = 0; i < message->num_cached; i++) { if (message->classes[i] == cclass && message->dstflags[i] == dstflags && message->mclasses[i] == mclass) return message->packets[i]; } } { t_packet * * temp_packets; t_conn_class * temp_classes; unsigned int * temp_dstflags; t_message_class *temp_mclasses; if (!message->packets) temp_packets = (t_packet**)xmalloc(sizeof(t_packet *)*(message->num_cached + 1)); else temp_packets = (t_packet**)xrealloc(message->packets, sizeof(t_packet *)*(message->num_cached + 1)); if (!message->classes) temp_classes = (t_conn_class*)xmalloc(sizeof(t_conn_class)*(message->num_cached + 1)); else temp_classes = (t_conn_class*)xrealloc(message->classes, sizeof(t_conn_class)*(message->num_cached + 1)); if (!message->dstflags) temp_dstflags = (unsigned int *)xmalloc(sizeof(unsigned int)*(message->num_cached + 1)); else temp_dstflags = (unsigned int *)xrealloc(message->dstflags, sizeof(unsigned int)*(message->num_cached + 1)); if (!message->mclasses) temp_mclasses = (t_message_class*)xmalloc(sizeof(t_message_class)*(message->num_cached + 1)); else temp_mclasses = (t_message_class*)xrealloc(message->mclasses, sizeof(t_message_class)*(message->num_cached + 1)); message->packets = temp_packets; message->classes = temp_classes; message->dstflags = temp_dstflags; message->mclasses = temp_mclasses; } switch (cclass) { case conn_class_telnet: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return NULL; } if (message_telnet_format(packet, message->type, message->src, dst, message->text, dstflags) < 0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_bot: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return NULL; } if (message_bot_format(packet, message->type, message->src, dst, message->text, dstflags) < 0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_bnet: if (!(packet = packet_create(packet_class_bnet))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return NULL; } if (message_bnet_format(packet, message->type, message->src, dst, message->text, dstflags) < 0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_irc: case conn_class_wol: case conn_class_wserv: case conn_class_wgameres: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return NULL; } /* irc_message_format() is in irc.c */ if (irc_message_format(packet, message->type, message->src, dst, message->text, dstflags) < 0) { packet_del_ref(packet); packet = NULL; /* we can cache the NULL too */ } break; case conn_class_init: case conn_class_file: case conn_class_d2cs_bnetd: case conn_class_w3route: packet = NULL; break; /* cache the NULL but dont send any error, * this are normal connections */ default: eventlog(eventlog_level_error, __FUNCTION__, "unsupported connection class {}", (int)cclass); packet = NULL; /* we can cache the NULL too */ } message->num_cached++; message->packets[i] = packet; message->classes[i] = cclass; message->dstflags[i] = dstflags; message->mclasses[i] = mclass; return packet; } extern int message_send(t_message * message, t_connection * dst) { t_packet * packet; unsigned int dstflags; if (!message) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL message"); return -1; } if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst connection"); return -1; } dstflags = 0; if (message->src) { char const * tname; if ((tname = conn_get_chatname(message->src)) && conn_check_ignoring(dst, tname) == 1) { conn_unget_chatname(message->src, tname); dstflags |= MF_X; } if (tname) conn_unget_chatname(message->src, tname); } if (!(packet = message_cache_lookup(message, dst, dstflags))) return -1; /* FIXME: this is not needed now, message has dst */ if ((conn_get_class(dst) == conn_class_irc) || (conn_get_class(dst) == conn_class_wol) || (conn_get_class(dst) == conn_class_wserv) || (conn_get_class(dst) == conn_class_wgameres)) { /* HACK: IRC message always need the recipient and are therefore bad to cache. */ /* So we only cache a pseudo packet and convert it to a real packet later ... */ packet = packet_duplicate(packet); /* we want to modify packet so we have to create a copy ... */ if (irc_message_postformat(packet, dst) < 0) { packet_del_ref(packet); /* we don't need the previously created copy anymore ... */ return -1; } } conn_push_outqueue(dst, packet); if ((conn_get_class(dst) == conn_class_irc) || (conn_get_class(dst) == conn_class_wol) || (conn_get_class(dst) == conn_class_wserv) || (conn_get_class(dst) == conn_class_wgameres)) packet_del_ref(packet); /* we don't need the previously created copy anymore ... */ return 0; } extern int message_send_all(t_message * message) { t_connection * c; t_elem const * curr; int rez; if (!message) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL message"); return -1; } rez = -1; LIST_TRAVERSE_CONST(connlist(), curr) { c = (t_connection*)elem_get_data(curr); if (message_send(message, c) == 0) rez = 0; } return rez; } extern int message_send_text(t_connection * dst, t_message_type type, t_connection * src, std::string text) { return message_send_text(dst, type, src, text.c_str()); } extern int message_send_text(t_connection * dst, t_message_type type, t_connection * src, char const * text) { t_message * message; int rez; if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!(message = message_create(type, src, text))) return -1; rez = message_send(message, dst); message_destroy(message); return rez; } extern int message_send_admins(t_connection * src, t_message_type type, char const * text) { t_elem const * curr; t_connection * tc; int counter = 0; LIST_TRAVERSE_CONST(connlist(), curr) { tc = (t_connection*)elem_get_data(curr); if (!tc) continue; if (account_get_auth_admin(conn_get_account(tc), NULL) == 1 && tc != src) { message_send_text(tc, type, src, text); counter++; } } return counter; } extern int message_send_formatted(t_connection * dst, char const * text) { char * line; if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!(line = message_format_line(dst, text))) { eventlog(eventlog_level_error, __FUNCTION__, "could not format input text \"{}\"", text); return -1; } i18n_convert(dst, line); /* caller beware: empty messages can crash Blizzard clients */ switch (line[0]) { case 'C': if (line[1] == '/') handle_command(dst, &line[1]); else if (conn_get_channel(dst) && !conn_quota_exceeded(dst, &line[1])) channel_message_send(conn_get_channel(dst), message_type_talk, dst, &line[1]); break; case 'B': message_send_text(dst, message_type_broadcast, dst, &line[1]); break; case 'E': message_send_text(dst, message_type_error, dst, &line[1]); break; case 'M': message_send_text(dst, message_type_talk, dst, &line[1]); break; case 'T': message_send_text(dst, message_type_emote, dst, &line[1]); break; case 'I': case 'W': message_send_text(dst, message_type_info, dst, &line[1]); break; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown message type '{}'", line[0]); xfree(line); return -1; } xfree(line); return 0; } extern int message_send_file(t_connection * dst, std::FILE * fd) { char * buff; if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL fd"); return -1; } while ((buff = file_get_line(fd))) { message_send_formatted(dst, buff); } file_get_line(NULL); // clear file_get_line buffer return 0; } /* Show message box on a client side (https://github.com/pvpgn/pvpgn-server/issues/15) */ extern int messagebox_show(t_connection * dst, char const * text, char const * caption, int type) { t_packet *rpacket; std::string newtext = str_replace_nl(text); if ((rpacket = packet_create(packet_class_bnet))) { packet_set_size(rpacket, sizeof(t_server_messagebox)); packet_set_type(rpacket, SERVER_MESSAGEBOX); bn_int_set(&rpacket->u.server_messagebox.style, type); packet_append_string(rpacket, newtext.c_str()); packet_append_string(rpacket, caption); conn_push_outqueue(dst, rpacket); packet_del_ref(rpacket); } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/message.h000066400000000000000000000101471462640461400176630ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_MESSAGE_TYPES #define INCLUDED_MESSAGE_TYPES #ifdef MESSAGE_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES # include "common/packet.h" # include "connection.h" #else # define JUST_NEED_TYPES # include "common/packet.h" # include "connection.h" # undef JUST_NEED_TYPES #endif #endif namespace pvpgn { namespace bnetd { typedef enum { message_type_adduser, message_type_join, message_type_part, message_type_whisper, message_type_talk, message_type_broadcast, message_type_channel, message_type_userflags, message_type_whisperack, message_type_friendwhisperack, message_type_channelfull, message_type_channeldoesnotexist, message_type_channelrestricted, message_type_info, message_type_error, message_type_emote, message_type_uniqueid, message_type_mode, message_type_kick, message_type_quit, /** * IRC specific messages */ message_type_nick, message_type_notice, message_type_namreply, message_type_topic, /** * Westwood Online Extensions */ message_type_host, message_type_invmsg, message_type_page, message_wol_joingame, message_type_gameopt_talk, message_type_gameopt_whisper, message_wol_start_game, message_wol_advertr, message_wol_chanchk, message_wol_userip, message_type_null } t_message_type; typedef enum { message_class_normal, message_class_charjoin /* use char*account (if account isnt d2 char is "") */ } t_message_class; typedef struct message #ifdef MESSAGE_INTERNAL_ACCESS { unsigned int num_cached; t_packet * * packets; /* cached messages */ t_conn_class * classes; /* classes of cached message connections */ unsigned int * dstflags; /* overlaid flags of cached messages */ t_message_class * mclasses; /* classes of cached messages */ /* ---- */ t_message_type type; /* format of message */ t_connection * src; /* originator message */ char const * text; /* text of message */ } #endif t_message; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_MESSAGE_PROTOS #define INCLUDED_MESSAGE_PROTOS #include #define JUST_NEED_TYPES #include #include "connection.h" #include "common/bnet_protocol.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern char * message_format_line(t_connection const * c, char const * in); extern t_message * message_create(t_message_type type, t_connection * src, char const * text); extern int message_destroy(t_message * message); extern int message_send(t_message * message, t_connection * dst); extern int message_send_all(t_message * message); extern int message_send_admins(t_connection * src, t_message_type type, char const * text); /* the following are "shortcuts" to avoid calling message_create(), message_send(), message_destroy() */ extern int message_send_text(t_connection * dst, t_message_type type, t_connection * src, std::string text); extern int message_send_text(t_connection * dst, t_message_type type, t_connection * src, char const * text); extern int message_send_formatted(t_connection * dst, char const * text); extern int message_send_file(t_connection * dst, std::FILE * fd); extern int messagebox_show(t_connection * dst, char const * text, char const * caption = "", int type = SERVER_MESSAGEBOX_OK); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/news.cpp000066400000000000000000000152161462640461400175500ustar00rootroot00000000000000/* * Copyright (C) 2000 Alexey Belyaev (spider@omskart.ru) * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define NEWS_INTERNAL_ACCESS #include "news.h" #include #include #include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "common/tag.h" #include "i18n.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_elist news_head; static int _news_parsetime(char *buff, struct std::tm *date, unsigned line) { char *p; date->tm_hour = 6; date->tm_min = 6; // need to set non-zero values or else date is displayed wrong date->tm_sec = 6; date->tm_isdst = -1; if (!(p = std::strchr(buff, '/'))) return -1; *p = '\0'; date->tm_mon = std::atoi(buff) - 1; if ((date->tm_mon<0) || (date->tm_mon>11)) { eventlog(eventlog_level_error, __FUNCTION__, "found invalid month ({}) in news date. (format: {MM/DD/YYYY}) on line {}", date->tm_mon, line); } buff = p + 1; if (!(p = std::strchr(buff, '/'))) return -1; *p = '\0'; date->tm_mday = std::atoi(buff); if ((date->tm_mday<1) || (date->tm_mday>31)) { eventlog(eventlog_level_error, __FUNCTION__, "found invalid month day ({}) in news date. (format: {MM/DD/YYYY}) on line {}", date->tm_mday, line); return -1; } buff = p + 1; if (!(p = std::strchr(buff, '}'))) return -1; *p = '\0'; date->tm_year = std::atoi(buff) - 1900; if (date->tm_year > 137) //limited due to 32bit t_time { eventlog(eventlog_level_error, __FUNCTION__, "found invalid year ({}) (>2037) in news date. on line {}", date->tm_year + 1900, line); return -1; } return 0; } static void _news_insert_index(t_news_index *ni, const char *buff, unsigned len, int date_set) { t_elist *curr; t_news_index *cni; elist_for_each(curr, &news_head) { cni = elist_entry(curr, t_news_index, list); if (cni->date <= ni->date) break; } if (curr != &news_head && cni->date == ni->date) { if (date_set == 1) eventlog(eventlog_level_warn, __FUNCTION__, "found another news item for same date, trying to join both"); if ((lstr_get_len(&cni->body) + len + 2) > 1023) eventlog(eventlog_level_error, __FUNCTION__, "failed in joining news, cause news too long - skipping"); else { lstr_set_str(&cni->body, (char*)xrealloc(lstr_get_str(&cni->body), lstr_get_len(&cni->body) + len + 1 + 1)); std::strcpy(lstr_get_str(&cni->body) + lstr_get_len(&cni->body), buff); *(lstr_get_str(&cni->body) + lstr_get_len(&cni->body) + len) = '\n'; *(lstr_get_str(&cni->body) + lstr_get_len(&cni->body) + len + 1) = '\0'; lstr_set_len(&cni->body, lstr_get_len(&cni->body) + len + 1); } xfree((void *)ni); } else { /* adding new index entry */ lstr_set_str(&ni->body, (char*)xmalloc(len + 2)); std::strcpy(lstr_get_str(&ni->body), buff); std::strcat(lstr_get_str(&ni->body), "\n"); lstr_set_len(&ni->body, len + 1); elist_add_tail(curr, &ni->list); } } static void _news_insert_default(void) { const char * deftext = "No news today"; t_news_index *ni; ni = (t_news_index*)xmalloc(sizeof(t_news_index)); ni->date = std::time(NULL); _news_insert_index(ni, deftext, std::strlen(deftext), 1); } extern int news_load(const char *filename) { std::FILE * fp; unsigned int line; unsigned int len; char buff[256]; struct std::tm date; char date_set; t_news_index *ni; elist_init(&news_head); date_set = 0; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL fullname"); return -1; } // FIXME: (HarpyWar) change news loading when user log on to send a localized version if ((fp = std::fopen(i18n_filename(filename, GAMELANG_ENGLISH_UINT).c_str(), "rt")) == NULL) { eventlog(eventlog_level_warn, __FUNCTION__, "can't open news file"); _news_insert_default(); return 0; } for (line = 1; std::fgets(buff, sizeof(buff), fp); line++) { len = std::strlen(buff); while (len && (buff[len - 1] == '\n' || buff[len - 1] == '\r')) len--; if (!len) continue; /* empty line */ buff[len] = '\0'; if (buff[0] == '{') { if (_news_parsetime(buff + 1, &date, line)) { eventlog(eventlog_level_error, __FUNCTION__, "error parsing news date on line {}", line); std::fclose(fp); return -1; } date_set = 1; } else { ni = (t_news_index*)xmalloc(sizeof(t_news_index)); if (date_set) { ni->date = std::mktime(&date); } else { ni->date = std::time(NULL); eventlog(eventlog_level_warn, __FUNCTION__, "(first) news entry seems to be missing a timestamp, please check your news file on line {}", line); } _news_insert_index(ni, buff, len, date_set); date_set = 2; } } std::fclose(fp); if (elist_empty(&news_head)) { eventlog(eventlog_level_warn, __FUNCTION__, "no news configured"); _news_insert_default(); } return 0; } /* Free up all of the elements in the linked list */ extern int news_unload(void) { t_elist * curr, *save; t_news_index * ni; elist_for_each_safe(curr, &news_head, save) { ni = elist_entry(curr, t_news_index, list); elist_del(&ni->list); xfree((void *)lstr_get_str(&ni->body)); xfree((void *)ni); } elist_init(&news_head); return 0; } extern unsigned int news_get_lastnews(void) { if (elist_empty(&news_head)) return 0; return ((elist_entry(news_head.next, t_news_index, list))->date); } extern unsigned int news_get_firstnews(void) { if (elist_empty(&news_head)) return 0; return ((elist_entry(news_head.prev, t_news_index, list))->date); } extern void news_traverse(t_news_cb cb, void *data) { t_elist *curr; t_news_index *cni; assert(cb); elist_for_each(curr, &news_head) { cni = elist_entry(curr, t_news_index, list); if (cb(cni->date, &cni->body, data)) break; } } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/news.h000066400000000000000000000030551462640461400172130ustar00rootroot00000000000000/* * Copyright (C) 2000 Alexey Belyaev (spider@omskart.ru) * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_NEWS_TYPES #define INCLUDED_NEWS_TYPES #include #include "common/elist.h" #include "common/lstr.h" namespace pvpgn { namespace bnetd { typedef struct news_index #ifdef NEWS_INTERNAL_ACCESS { std::time_t date; t_lstr body; t_elist list; } #endif t_news_index; typedef int(*t_news_cb)(std::time_t, t_lstr *, void *); } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef NEWS_INTERNAL_ACCESS #define NEWS_INTERNAL_ACCESS namespace pvpgn { namespace bnetd { extern int news_load(const char *filename); extern int news_unload(void); extern unsigned int news_get_firstnews(void); extern unsigned int news_get_lastnews(void); extern void news_traverse(t_news_cb cb, void *data); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/output.cpp000066400000000000000000000147361462640461400201420ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "output.h" #include #include #include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "common/tag.h" #include "common/list.h" #include "common/util.h" #include "prefs.h" #include "game.h" #include "channel.h" #include "connection.h" #include "account.h" #include "server.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { std::string status_filename; int output_standard_writer(std::FILE * fp); /* * Initialisation Output * */ extern void output_init() { eventlog(eventlog_level_info, __FUNCTION__, "initializing output file"); if (prefs_get_XML_status_output()) status_filename = fmt::format("{}/{}", prefs_get_outputdir(), "server.xml"); else status_filename = fmt::format("{}/{}", prefs_get_outputdir(), "server.dat"); return; } /* * Write Functions * */ static int _glist_cb_xml(t_game *game, void *data) { char clienttag_str[5]; std::fprintf((std::FILE*)data, "\t\t%u%s%s\n", game_get_id(game), game_get_name(game), tag_uint_to_str(clienttag_str, game_get_clienttag(game))); return 0; } static int _glist_cb_simple(t_game *game, void *data) { static int number; char clienttag_str[5]; if (!data) { number = 1; return 0; } std::fprintf((std::FILE*)data, "game%d=%s,%u,%s\n", number, tag_uint_to_str(clienttag_str, game_get_clienttag(game)), game_get_id(game), game_get_name(game)); number++; return 0; } int output_standard_writer(std::FILE * fp) { t_elem const *curr; t_connection *conn; t_channel const *channel; t_game *game; char const *channel_name; int number; char clienttag_str[5]; int uptime = server_get_uptime(); if (prefs_get_XML_status_output()) { int seconds; int minutes; int hours; int days; days = (uptime / (60 * 60 * 24)); hours = (uptime / (60 * 60)) % 24; minutes = (uptime / 60) % 60; seconds = uptime % 60; std::fprintf(fp, "\n\n"); std::fprintf(fp, "\t\t%s\n", PVPGN_VERSION); std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\t\t\t%d\n", days); std::fprintf(fp, "\t\t\t%d\n", hours); std::fprintf(fp, "\t\t\t%d\n", minutes); std::fprintf(fp, "\t\t\t%d\n", seconds); std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\t\t%d\n", connlist_login_get_length()); LIST_TRAVERSE_CONST(connlist(), curr) { conn = (t_connection*)elem_get_data(curr); if (conn_get_account(conn)) { std::fprintf(fp, "\t\t%s%s%s", conn_get_username(conn), tag_uint_to_str(clienttag_str, conn_get_clienttag(conn)), conn_get_clientver(conn)); const char * country = conn_get_country(conn); if (!country) country = "-"; std::fprintf(fp, "%s", country); if ((game = conn_get_game(conn))) std::fprintf(fp, "%u", game_get_id(game)); std::fprintf(fp, "\n"); } } std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\t\t%d\n", gamelist_get_length()); gamelist_traverse(_glist_cb_xml, fp, gamelist_source_none); std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\t\t%d\n", channellist_get_length()); LIST_TRAVERSE_CONST(channellist(), curr) { channel = (t_channel*)elem_get_data(curr); channel_name = channel_get_name(channel); std::fprintf(fp, "\t\t%s\n", channel_name); } std::fprintf(fp, "\t\t\n"); std::fprintf(fp, "\n"); return 0; } else { std::fprintf(fp, "[STATUS]\nVersion=%s\nUptime=%s\nGames=%d\nUsers=%d\nChannels=%d\nUserAccounts=%d\n", PVPGN_VERSION, seconds_to_timestr(uptime), gamelist_get_length(), connlist_login_get_length(), channellist_get_length(), accountlist_get_length()); // Status std::fprintf(fp, "[CHANNELS]\n"); number = 1; LIST_TRAVERSE_CONST(channellist(), curr) { channel = (t_channel*)elem_get_data(curr); channel_name = channel_get_name(channel); std::fprintf(fp, "channel%d=%s\n", number, channel_name); number++; } std::fprintf(fp, "[GAMES]\n"); _glist_cb_simple(NULL, NULL); /* init number */ gamelist_traverse(_glist_cb_simple, fp, gamelist_source_none); std::fprintf(fp, "[USERS]\n"); number = 1; LIST_TRAVERSE_CONST(connlist(), curr) { conn = (t_connection*)elem_get_data(curr); if (conn_get_account(conn)) { std::fprintf(fp, "user%d=%s,%s,%s", number, tag_uint_to_str(clienttag_str, conn_get_clienttag(conn)), conn_get_username(conn), conn_get_clientver(conn)); const char * country = conn_get_country(conn); if (!country) country = "-"; std::fprintf(fp, ",%s", country); if ((game = conn_get_game(conn))) std::fprintf(fp, ",%u", game_get_id(game)); std::fprintf(fp, "\n"); number++; } } return 0; } } extern int output_write_to_file() { if (status_filename.empty()) { eventlog(eventlog_level_error, __FUNCTION__, "got empty filename"); return -1; } std::FILE* fp = std::fopen(status_filename.c_str(), "w"); if (!fp) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for writing (std::fopen: {})", status_filename, std::strerror(errno)); return -1; } output_standard_writer(fp); std::fclose(fp); return 0; } } }pvpgn-1.99.7.2.1+dfsg/src/bnetd/output.h000066400000000000000000000016111462640461400175730ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __OUTPUT_H_INCLUDED__ #define __OUTPUT_H_INCLUDED__ namespace pvpgn { namespace bnetd { extern void output_init(); extern int output_write_to_file(); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/prefs.cpp000066400000000000000000003220171462640461400177130ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 2004,2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define PREFS_INTERNAL_ACCESS #include "prefs.h" #include #include "common/conf.h" #include "common/eventlog.h" #include "common/setup_after.h" #define NONE 0 namespace pvpgn { namespace bnetd { static struct { /* files and paths */ char const * filedir; char const * i18ndir; char const * storage_path; char const * logfile; char const * loglevels; char const * localizefile; char const * motdfile; char const * motdw3file; char const * newsfile; char const * channelfile; char const * pidfile; char const * adfile; char const * topicfile; char const * DBlayoutfile; unsigned int usersync; unsigned int userflush; unsigned int userflush_connected; unsigned int userstep; char const * servername; char const * hostname; unsigned int track; char const * location; char const * description; char const * url; char const * contact_name; char const * contact_email; unsigned int latency; unsigned int irc_latency; unsigned int shutdown_delay; unsigned int shutdown_decr; unsigned int new_accounts; unsigned int max_accounts; unsigned int kick_old_login; unsigned int ask_new_channel; unsigned int hide_pass_games; unsigned int hide_started_games; unsigned int hide_temp_channels; unsigned int hide_addr; unsigned int enable_conn_all; char const * reportdir; unsigned int report_all_games; unsigned int report_diablo_games; char const * iconfile; char const * war3_iconfile; char const * star_iconfile; char const * tosfile; char const * mpqfile; char const * trackaddrs; char const * servaddrs; char const * w3routeaddr; char const * ircaddrs; unsigned int use_keepalive; unsigned int udptest_port; char const * ipbanfile; unsigned int disc_is_loss; char const * helpfile; char const * transfile; unsigned int chanlog; char const * chanlogdir; char const * userlogdir; unsigned int quota; unsigned int quota_lines; unsigned int quota_time; unsigned int quota_wrapline; unsigned int quota_maxline; unsigned int ladder_init_rating; unsigned int quota_dobae; char const * realmfile; char const * issuefile; char const * effective_user; char const * effective_group; unsigned int nullmsg; unsigned int mail_support; unsigned int mail_quota; char const * maildir; char const * log_notice; unsigned int savebyname; unsigned int allow_bad_version; unsigned int allow_unknown_version; char const * versioncheck_file; unsigned int d2cs_version; unsigned int allow_d2cs_setname; unsigned int hashtable_size; char const * telnetaddrs; unsigned int ipban_check_int; char const * version_exeinfo_match; unsigned int version_exeinfo_maxdiff; unsigned int max_concurrent_logins; char const * mapsfile; char const * xplevelfile; char const * xpcalcfile; unsigned int initkill_timer; unsigned int war3_ladder_update_secs; unsigned int output_update_secs; char const * ladderdir; char const * statusdir; unsigned int XML_output_ladder; unsigned int XML_status_output; char const * account_allowed_symbols; unsigned int account_force_username; char const * command_groups_file; char const * tournament_file; char const * customicons_file; char const * scriptdir; char const * aliasfile; char const * anongame_infos_file; unsigned int max_conns_per_IP; unsigned int max_friends; unsigned int clan_newer_time; unsigned int clan_max_members; unsigned int clan_channel_default_private; unsigned int clan_min_invites; unsigned int passfail_count; unsigned int passfail_bantime; unsigned int maxusers_per_channel; char const * supportfile; char const * allowed_clients; char const * ladder_games; char const * ladder_prefix; unsigned int max_connections; unsigned int packet_limit; unsigned int sync_on_logoff; char const * irc_network_name; unsigned int localize_by_country; unsigned int log_commands; char const * log_command_groups; char const * log_command_list; char const * apiregaddrs; char const * wolv1addrs; char const * wolv2addrs; char const * wgameresaddrs; char const * woltimezone; char const * wollongitude; char const * wollatitude; char const * wol_autoupdate_serverhost; char const * wol_autoupdate_username; char const * wol_autoupdate_password; } prefs_runtime_config; static int conf_set_filedir(const char *valstr); static const char *conf_get_filedir(void); static int conf_setdef_filedir(void); static int conf_set_i18ndir(const char *valstr); static const char *conf_get_i18ndir(void); static int conf_setdef_i18ndir(void); static int conf_set_storage_path(const char *valstr); static const char *conf_get_storage_path(void); static int conf_setdef_storage_path(void); static int conf_set_logfile(const char *valstr); static const char *conf_get_logfile(void); static int conf_setdef_logfile(void); static int conf_set_loglevels(const char *valstr); static const char *conf_get_loglevels(void); static int conf_setdef_loglevels(void); static int conf_set_localizefile(const char *valstr); static const char *conf_get_localizefile(void); static int conf_setdef_localizefile(void); static int conf_set_motdfile(const char *valstr); static const char *conf_get_motdfile(void); static int conf_setdef_motdfile(void); static int conf_set_motdw3file(const char *valstr); static const char *conf_get_motdw3file(void); static int conf_setdef_motdw3file(void); static int conf_set_newsfile(const char *valstr); static const char *conf_get_newsfile(void); static int conf_setdef_newsfile(void); static int conf_set_channelfile(const char *valstr); static const char *conf_get_channelfile(void); static int conf_setdef_channelfile(void); static int conf_set_pidfile(const char *valstr); static const char *conf_get_pidfile(void); static int conf_setdef_pidfile(void); static int conf_set_adfile(const char *valstr); static const char *conf_get_adfile(void); static int conf_setdef_adfile(void); static int conf_set_topicfile(const char *valstr); static const char *conf_get_topicfile(void); static int conf_setdef_topicfile(void); static int conf_set_DBlayoutfile(const char *valstr); static const char *conf_get_DBlayoutfile(void); static int conf_setdef_DBlayoutfile(void); static int conf_set_supportfile(const char *valstr); static const char *conf_get_supportfile(void); static int conf_setdef_supportfile(void); static int conf_set_usersync(const char *valstr); static const char *conf_get_usersync(void); static int conf_setdef_usersync(void); static int conf_set_userflush(const char *valstr); static const char *conf_get_userflush(void); static int conf_setdef_userflush(void); static int conf_set_userflush_connected(const char *valstr); static const char *conf_get_userflush_connected(void); static int conf_setdef_userflush_connected(void); static int conf_set_userstep(const char *valstr); static const char *conf_get_userstep(void); static int conf_setdef_userstep(void); static int conf_set_servername(const char *valstr); static const char *conf_get_servername(void); static int conf_setdef_servername(void); static int conf_set_hostname(const char *valstr); static const char *conf_get_hostname(void); static int conf_setdef_hostname(void); static int conf_set_track(const char *valstr); static const char *conf_get_track(void); static int conf_setdef_track(void); static int conf_set_location(const char *valstr); static const char *conf_get_location(void); static int conf_setdef_location(void); static int conf_set_description(const char *valstr); static const char *conf_get_description(void); static int conf_setdef_description(void); static int conf_set_url(const char *valstr); static const char *conf_get_url(void); static int conf_setdef_url(void); static int conf_set_contact_name(const char *valstr); static const char *conf_get_contact_name(void); static int conf_setdef_contact_name(void); static int conf_set_contact_email(const char *valstr); static const char *conf_get_contact_email(void); static int conf_setdef_contact_email(void); static int conf_set_latency(const char *valstr); static const char *conf_get_latency(void); static int conf_setdef_latency(void); static int conf_set_irc_latency(const char *valstr); static const char *conf_get_irc_latency(void); static int conf_setdef_irc_latency(void); static int conf_set_shutdown_delay(const char *valstr); static const char *conf_get_shutdown_delay(void); static int conf_setdef_shutdown_delay(void); static int conf_set_shutdown_decr(const char *valstr); static const char *conf_get_shutdown_decr(void); static int conf_setdef_shutdown_decr(void); static int conf_set_new_accounts(const char *valstr); static const char *conf_get_new_accounts(void); static int conf_setdef_new_accounts(void); static int conf_set_max_accounts(const char *valstr); static const char *conf_get_max_accounts(void); static int conf_setdef_max_accounts(void); static int conf_set_kick_old_login(const char *valstr); static const char *conf_get_kick_old_login(void); static int conf_setdef_kick_old_login(void); static int conf_set_ask_new_channel(const char *valstr); static const char *conf_get_ask_new_channel(void); static int conf_setdef_ask_new_channel(void); static int conf_set_hide_pass_games(const char *valstr); static const char *conf_get_hide_pass_games(void); static int conf_setdef_hide_pass_games(void); static int conf_set_hide_started_games(const char *valstr); static const char *conf_get_hide_started_games(void); static int conf_setdef_hide_started_games(void); static int conf_set_hide_temp_channels(const char *valstr); static const char *conf_get_hide_temp_channels(void); static int conf_setdef_hide_temp_channels(void); static int conf_set_hide_addr(const char *valstr); static const char *conf_get_hide_addr(void); static int conf_setdef_hide_addr(void); static int conf_set_enable_conn_all(const char *valstr); static const char *conf_get_enable_conn_all(void); static int conf_setdef_enable_conn_all(void); static int conf_set_reportdir(const char *valstr); static const char *conf_get_reportdir(void); static int conf_setdef_reportdir(void); static int conf_set_report_all_games(const char *valstr); static const char *conf_get_report_all_games(void); static int conf_setdef_report_all_games(void); static int conf_set_report_diablo_games(const char *valstr); static const char *conf_get_report_diablo_games(void); static int conf_setdef_report_diablo_games(void); static int conf_set_iconfile(const char *valstr); static const char *conf_get_iconfile(void); static int conf_setdef_iconfile(void); static int conf_set_war3_iconfile(const char *valstr); static const char *conf_get_war3_iconfile(void); static int conf_setdef_war3_iconfile(void); static int conf_set_star_iconfile(const char *valstr); static const char *conf_get_star_iconfile(void); static int conf_setdef_star_iconfile(void); static int conf_set_tosfile(const char *valstr); static const char *conf_get_tosfile(void); static int conf_setdef_tosfile(void); static int conf_set_mpqfile(const char *valstr); static const char *conf_get_mpqfile(void); static int conf_setdef_mpqfile(void); static int conf_set_trackaddrs(const char *valstr); static const char *conf_get_trackaddrs(void); static int conf_setdef_trackaddrs(void); static int conf_set_servaddrs(const char *valstr); static const char *conf_get_servaddrs(void); static int conf_setdef_servaddrs(void); static int conf_set_w3routeaddr(const char *valstr); static const char *conf_get_w3routeaddr(void); static int conf_setdef_w3routeaddr(void); static int conf_set_ircaddrs(const char *valstr); static const char *conf_get_ircaddrs(void); static int conf_setdef_ircaddrs(void); static int conf_set_use_keepalive(const char *valstr); static const char *conf_get_use_keepalive(void); static int conf_setdef_use_keepalive(void); static int conf_set_udptest_port(const char *valstr); static const char *conf_get_udptest_port(void); static int conf_setdef_udptest_port(void); static int conf_set_ipbanfile(const char *valstr); static const char *conf_get_ipbanfile(void); static int conf_setdef_ipbanfile(void); static int conf_set_disc_is_loss(const char *valstr); static const char *conf_get_disc_is_loss(void); static int conf_setdef_disc_is_loss(void); static int conf_set_helpfile(const char *valstr); static const char *conf_get_helpfile(void); static int conf_setdef_helpfile(void); static int conf_set_transfile(const char *valstr); static const char *conf_get_transfile(void); static int conf_setdef_transfile(void); static int conf_set_chanlog(const char *valstr); static const char *conf_get_chanlog(void); static int conf_setdef_chanlog(void); static int conf_set_chanlogdir(const char *valstr); static const char *conf_get_chanlogdir(void); static int conf_setdef_chanlogdir(void); static int conf_set_userlogdir(const char *valstr); static const char *conf_get_userlogdir(void); static int conf_setdef_userlogdir(void); static int conf_set_quota(const char *valstr); static const char *conf_get_quota(void); static int conf_setdef_quota(void); static int conf_set_quota_lines(const char *valstr); static const char *conf_get_quota_lines(void); static int conf_setdef_quota_lines(void); static int conf_set_quota_time(const char *valstr); static const char *conf_get_quota_time(void); static int conf_setdef_quota_time(void); static int conf_set_quota_wrapline(const char *valstr); static const char *conf_get_quota_wrapline(void); static int conf_setdef_quota_wrapline(void); static int conf_set_quota_maxline(const char *valstr); static const char *conf_get_quota_maxline(void); static int conf_setdef_quota_maxline(void); static int conf_set_ladder_init_rating(const char *valstr); static const char *conf_get_ladder_init_rating(void); static int conf_setdef_ladder_init_rating(void); static int conf_set_quota_dobae(const char *valstr); static const char *conf_get_quota_dobae(void); static int conf_setdef_quota_dobae(void); static int conf_set_realmfile(const char *valstr); static const char *conf_get_realmfile(void); static int conf_setdef_realmfile(void); static int conf_set_issuefile(const char *valstr); static const char *conf_get_issuefile(void); static int conf_setdef_issuefile(void); static int conf_set_effective_user(const char *valstr); static const char *conf_get_effective_user(void); static int conf_setdef_effective_user(void); static int conf_set_effective_group(const char *valstr); static const char *conf_get_effective_group(void); static int conf_setdef_effective_group(void); static int conf_set_nullmsg(const char *valstr); static const char *conf_get_nullmsg(void); static int conf_setdef_nullmsg(void); static int conf_set_mail_support(const char *valstr); static const char *conf_get_mail_support(void); static int conf_setdef_mail_support(void); static int conf_set_mail_quota(const char *valstr); static const char *conf_get_mail_quota(void); static int conf_setdef_mail_quota(void); static int conf_set_maildir(const char *valstr); static const char *conf_get_maildir(void); static int conf_setdef_maildir(void); static int conf_set_log_notice(const char *valstr); static const char *conf_get_log_notice(void); static int conf_setdef_log_notice(void); static int conf_set_savebyname(const char *valstr); static const char *conf_get_savebyname(void); static int conf_setdef_savebyname(void); static int conf_set_allow_bad_version(const char *valstr); static const char *conf_get_allow_bad_version(void); static int conf_setdef_allow_bad_version(void); static int conf_set_allow_unknown_version(const char *valstr); static const char *conf_get_allow_unknown_version(void); static int conf_setdef_allow_unknown_version(void); static int conf_set_versioncheck_file(const char *valstr); static const char *conf_get_versioncheck_file(void); static int conf_setdef_versioncheck_file(void); static int conf_set_d2cs_version(const char *valstr); static const char *conf_get_d2cs_version(void); static int conf_setdef_d2cs_version(void); static int conf_set_allow_d2cs_setname(const char *valstr); static const char *conf_get_allow_d2cs_setname(void); static int conf_setdef_allow_d2cs_setname(void); static int conf_set_hashtable_size(const char *valstr); static const char *conf_get_hashtable_size(void); static int conf_setdef_hashtable_size(void); static int conf_set_telnetaddrs(const char *valstr); static const char *conf_get_telnetaddrs(void); static int conf_setdef_telnetaddrs(void); static int conf_set_ipban_check_int(const char *valstr); static const char *conf_get_ipban_check_int(void); static int conf_setdef_ipban_check_int(void); static int conf_set_max_concurrent_logins(const char *valstr); static const char *conf_get_max_concurrent_logins(void); static int conf_setdef_max_concurrent_logins(void); static int conf_set_mapsfile(const char *valstr); static const char *conf_get_mapsfile(void); static int conf_setdef_mapsfile(void); static int conf_set_xplevelfile(const char *valstr); static const char *conf_get_xplevelfile(void); static int conf_setdef_xplevelfile(void); static int conf_set_xpcalcfile(const char *valstr); static const char *conf_get_xpcalcfile(void); static int conf_setdef_xpcalcfile(void); static int conf_set_initkill_timer(const char *valstr); static const char *conf_get_initkill_timer(void); static int conf_setdef_initkill_timer(void); static int conf_set_war3_ladder_update_secs(const char *valstr); static const char *conf_get_war3_ladder_update_secs(void); static int conf_setdef_war3_ladder_update_secs(void); static int conf_set_output_update_secs(const char *valstr); static const char *conf_get_output_update_secs(void); static int conf_setdef_output_update_secs(void); static int conf_set_ladderdir(const char *valstr); static const char *conf_get_ladderdir(void); static int conf_setdef_ladderdir(void); static int conf_set_statusdir(const char *valstr); static const char *conf_get_statusdir(void); static int conf_setdef_statusdir(void); static int conf_set_XML_output_ladder(const char *valstr); static const char *conf_get_XML_output_ladder(void); static int conf_setdef_XML_output_ladder(void); static int conf_set_XML_status_output(const char *valstr); static const char *conf_get_XML_status_output(void); static int conf_setdef_XML_status_output(void); static int conf_set_account_allowed_symbols(const char *valstr); static const char *conf_get_account_allowed_symbols(void); static int conf_setdef_account_allowed_symbols(void); static int conf_set_account_force_username(const char *valstr); static const char *conf_get_account_force_username(void); static int conf_setdef_account_force_username(void); static int conf_set_command_groups_file(const char *valstr); static const char *conf_get_command_groups_file(void); static int conf_setdef_command_groups_file(void); static int conf_set_tournament_file(const char *valstr); static const char *conf_get_tournament_file(void); static int conf_setdef_tournament_file(void); static int conf_set_customicons_file(const char *valstr); static const char *conf_get_customicons_file(void); static int conf_setdef_customicons_file(void); static int conf_set_scriptdir(const char *valstr); static const char *conf_get_scriptdir(void); static int conf_setdef_scriptdir(void); static int conf_set_aliasfile(const char *valstr); static const char *conf_get_aliasfile(void); static int conf_setdef_aliasfile(void); static int conf_set_anongame_infos_file(const char *valstr); static const char *conf_get_anongame_infos_file(void); static int conf_setdef_anongame_infos_file(void); static int conf_set_max_conns_per_IP(const char *valstr); static const char *conf_get_max_conns_per_IP(void); static int conf_setdef_max_conns_per_IP(void); static int conf_set_max_friends(const char *valstr); static const char *conf_get_max_friends(void); static int conf_setdef_max_friends(void); static int conf_set_clan_newer_time(const char *valstr); static const char *conf_get_clan_newer_time(void); static int conf_setdef_clan_newer_time(void); static int conf_set_clan_max_members(const char *valstr); static const char *conf_get_clan_max_members(void); static int conf_setdef_clan_max_members(void); static int conf_set_clan_channel_default_private(const char *valstr); static const char *conf_get_clan_channel_default_private(void); static int conf_setdef_clan_channel_default_private(void); static int conf_set_clan_min_invites(const char *valstr); static const char *conf_get_clan_min_invites(void); static int conf_setdef_clan_min_invites(void); static int conf_set_passfail_count(const char *valstr); static const char *conf_get_passfail_count(void); static int conf_setdef_passfail_count(void); static int conf_set_passfail_bantime(const char *valstr); static const char *conf_get_passfail_bantime(void); static int conf_setdef_passfail_bantime(void); static int conf_set_maxusers_per_channel(const char *valstr); static const char *conf_get_maxusers_per_channel(void); static int conf_setdef_maxusers_per_channel(void); static int conf_set_allowed_clients(const char *valstr); static const char *conf_get_allowed_clients(void); static int conf_setdef_allowed_clients(void); static int conf_set_ladder_games(const char *valstr); static const char *conf_get_ladder_games(void); static int conf_setdef_ladder_games(void); static int conf_set_max_connections(const char *valstr); static const char *conf_get_max_connections(void); static int conf_setdef_max_connections(void); static int conf_set_packet_limit(const char *valstr); static const char *conf_get_packet_limit(void); static int conf_setdef_packet_limit(void); static int conf_set_sync_on_logoff(const char *valstr); static const char *conf_get_sync_on_logoff(void); static int conf_setdef_sync_on_logoff(void); static int conf_set_ladder_prefix(const char *valstr); static const char *conf_get_ladder_prefix(void); static int conf_setdef_ladder_prefix(void); static int conf_setdef_irc_network_name(void); static int conf_set_irc_network_name(const char *valstr); static const char *conf_get_irc_network_name(void); static int conf_set_localize_by_country(const char *valstr); static const char *conf_get_localize_by_country(void); static int conf_setdef_localize_by_country(void); static int conf_set_log_commands(const char *valstr); static const char *conf_get_log_commands(void); static int conf_setdef_log_commands(void); static int conf_set_log_command_groups(const char *valstr); static const char *conf_get_log_command_groups(void); static int conf_setdef_log_command_groups(void); static int conf_set_log_command_list(const char *valstr); static const char *conf_get_log_command_list(void); static int conf_setdef_log_command_list(void); static int conf_setdef_apireg_addrs(void); static int conf_set_apireg_addrs(const char *valstr); static const char *conf_get_apireg_addrs(void); static int conf_setdef_wgameres_addrs(void); static int conf_set_wgameres_addrs(const char *valstr); static const char *conf_get_wgameres_addrs(void); static int conf_setdef_wolv1_addrs(void); static int conf_set_wolv1_addrs(const char *valstr); static const char *conf_get_wolv1_addrs(void); static int conf_setdef_wolv2_addrs(void); static int conf_set_wolv2_addrs(const char *valstr); static const char *conf_get_wolv2_addrs(void); static int conf_set_wol_timezone(const char *valstr); static const char *conf_get_wol_timezone(void); static int conf_setdef_wol_timezone(void); static int conf_set_wol_longitude(const char *valstr); static const char *conf_get_wol_longitude(void); static int conf_setdef_wol_longitude(void); static int conf_set_wol_latitude(const char *valstr); static const char *conf_get_wol_latitude(void); static int conf_setdef_wol_latitude(void); static int conf_set_wol_autoupdate_serverhost(const char *valstr); static const char *conf_get_wol_autoupdate_serverhost(void); static int conf_setdef_wol_autoupdate_serverhost(void); static int conf_set_wol_autoupdate_username(const char *valstr); static const char *conf_get_wol_autoupdate_username(void); static int conf_setdef_wol_autoupdate_username(void); static int conf_set_wol_autoupdate_password(const char *valstr); static const char *conf_get_wol_autoupdate_password(void); static int conf_setdef_wol_autoupdate_password(void); /* directive set method get method */ static t_conf_entry conf_table[] = { { "filedir", conf_set_filedir, conf_get_filedir, conf_setdef_filedir }, { "i18ndir", conf_set_i18ndir, conf_get_i18ndir, conf_setdef_i18ndir }, { "storage_path", conf_set_storage_path, conf_get_storage_path, conf_setdef_storage_path }, { "logfile", conf_set_logfile, conf_get_logfile, conf_setdef_logfile }, { "loglevels", conf_set_loglevels, conf_get_loglevels, conf_setdef_loglevels }, { "localizefile", conf_set_localizefile, conf_get_localizefile, conf_setdef_localizefile }, { "motdfile", conf_set_motdfile, conf_get_motdfile, conf_setdef_motdfile }, { "motdw3file", conf_set_motdw3file, conf_get_motdw3file, conf_setdef_motdw3file }, { "newsfile", conf_set_newsfile, conf_get_newsfile, conf_setdef_newsfile }, { "channelfile", conf_set_channelfile, conf_get_channelfile, conf_setdef_channelfile }, { "pidfile", conf_set_pidfile, conf_get_pidfile, conf_setdef_pidfile }, { "adfile", conf_set_adfile, conf_get_adfile, conf_setdef_adfile }, { "topicfile", conf_set_topicfile, conf_get_topicfile, conf_setdef_topicfile }, { "DBlayoutfile", conf_set_DBlayoutfile, conf_get_DBlayoutfile, conf_setdef_DBlayoutfile }, { "supportfile", conf_set_supportfile, conf_get_supportfile, conf_setdef_supportfile }, { "usersync", conf_set_usersync, conf_get_usersync, conf_setdef_usersync }, { "userflush", conf_set_userflush, conf_get_userflush, conf_setdef_userflush }, { "userflush_connected", conf_set_userflush_connected, conf_get_userflush_connected, conf_setdef_userflush_connected }, { "userstep", conf_set_userstep, conf_get_userstep, conf_setdef_userstep }, { "servername", conf_set_servername, conf_get_servername, conf_setdef_servername }, { "hostname", conf_set_hostname, conf_get_hostname, conf_setdef_hostname }, { "track", conf_set_track, conf_get_track, conf_setdef_track }, { "location", conf_set_location, conf_get_location, conf_setdef_location }, { "description", conf_set_description, conf_get_description, conf_setdef_description }, { "url", conf_set_url, conf_get_url, conf_setdef_url }, { "contact_name", conf_set_contact_name, conf_get_contact_name, conf_setdef_contact_name }, { "contact_email", conf_set_contact_email, conf_get_contact_email, conf_setdef_contact_email }, { "latency", conf_set_latency, conf_get_latency, conf_setdef_latency }, { "irc_latency", conf_set_irc_latency, conf_get_irc_latency, conf_setdef_irc_latency }, { "shutdown_delay", conf_set_shutdown_delay, conf_get_shutdown_delay, conf_setdef_shutdown_delay }, { "shutdown_decr", conf_set_shutdown_decr, conf_get_shutdown_decr, conf_setdef_shutdown_decr }, { "new_accounts", conf_set_new_accounts, conf_get_new_accounts, conf_setdef_new_accounts }, { "max_accounts", conf_set_max_accounts, conf_get_max_accounts, conf_setdef_max_accounts }, { "kick_old_login", conf_set_kick_old_login, conf_get_kick_old_login, conf_setdef_kick_old_login }, { "ask_new_channel", conf_set_ask_new_channel, conf_get_ask_new_channel, conf_setdef_ask_new_channel }, { "hide_pass_games", conf_set_hide_pass_games, conf_get_hide_pass_games, conf_setdef_hide_pass_games }, { "hide_started_games", conf_set_hide_started_games, conf_get_hide_started_games, conf_setdef_hide_started_games }, { "hide_temp_channels", conf_set_hide_temp_channels, conf_get_hide_temp_channels, conf_setdef_hide_temp_channels }, { "hide_addr", conf_set_hide_addr, conf_get_hide_addr, conf_setdef_hide_addr }, { "enable_conn_all", conf_set_enable_conn_all, conf_get_enable_conn_all, conf_setdef_enable_conn_all }, { "reportdir", conf_set_reportdir, conf_get_reportdir, conf_setdef_reportdir }, { "report_all_games", conf_set_report_all_games, conf_get_report_all_games, conf_setdef_report_all_games }, { "report_diablo_games", conf_set_report_diablo_games, conf_get_report_diablo_games, conf_setdef_report_diablo_games }, { "iconfile", conf_set_iconfile, conf_get_iconfile, conf_setdef_iconfile }, { "war3_iconfile", conf_set_war3_iconfile, conf_get_war3_iconfile, conf_setdef_war3_iconfile }, { "star_iconfile", conf_set_star_iconfile, conf_get_star_iconfile, conf_setdef_star_iconfile }, { "tosfile", conf_set_tosfile, conf_get_tosfile, conf_setdef_tosfile }, { "mpqfile", conf_set_mpqfile, conf_get_mpqfile, conf_setdef_mpqfile }, { "trackaddrs", conf_set_trackaddrs, conf_get_trackaddrs, conf_setdef_trackaddrs }, { "servaddrs", conf_set_servaddrs, conf_get_servaddrs, conf_setdef_servaddrs }, { "w3routeaddr", conf_set_w3routeaddr, conf_get_w3routeaddr, conf_setdef_w3routeaddr }, { "ircaddrs", conf_set_ircaddrs, conf_get_ircaddrs, conf_setdef_ircaddrs }, { "use_keepalive", conf_set_use_keepalive, conf_get_use_keepalive, conf_setdef_use_keepalive }, { "udptest_port", conf_set_udptest_port, conf_get_udptest_port, conf_setdef_udptest_port }, { "ipbanfile", conf_set_ipbanfile, conf_get_ipbanfile, conf_setdef_ipbanfile }, { "disc_is_loss", conf_set_disc_is_loss, conf_get_disc_is_loss, conf_setdef_disc_is_loss }, { "helpfile", conf_set_helpfile, conf_get_helpfile, conf_setdef_helpfile }, { "transfile", conf_set_transfile, conf_get_transfile, conf_setdef_transfile }, { "chanlog", conf_set_chanlog, conf_get_chanlog, conf_setdef_chanlog }, { "chanlogdir", conf_set_chanlogdir, conf_get_chanlogdir, conf_setdef_chanlogdir }, { "userlogdir", conf_set_userlogdir, conf_get_userlogdir, conf_setdef_userlogdir }, { "quota", conf_set_quota, conf_get_quota, conf_setdef_quota }, { "quota_lines", conf_set_quota_lines, conf_get_quota_lines, conf_setdef_quota_lines }, { "quota_time", conf_set_quota_time, conf_get_quota_time, conf_setdef_quota_time }, { "quota_wrapline", conf_set_quota_wrapline, conf_get_quota_wrapline, conf_setdef_quota_wrapline }, { "quota_maxline", conf_set_quota_maxline, conf_get_quota_maxline, conf_setdef_quota_maxline }, { "ladder_init_rating", conf_set_ladder_init_rating, conf_get_ladder_init_rating, conf_setdef_ladder_init_rating }, { "quota_dobae", conf_set_quota_dobae, conf_get_quota_dobae, conf_setdef_quota_dobae }, { "realmfile", conf_set_realmfile, conf_get_realmfile, conf_setdef_realmfile }, { "issuefile", conf_set_issuefile, conf_get_issuefile, conf_setdef_issuefile }, { "effective_user", conf_set_effective_user, conf_get_effective_user, conf_setdef_effective_user }, { "effective_group", conf_set_effective_group, conf_get_effective_group, conf_setdef_effective_group }, { "nullmsg", conf_set_nullmsg, conf_get_nullmsg, conf_setdef_nullmsg }, { "mail_support", conf_set_mail_support, conf_get_mail_support, conf_setdef_mail_support }, { "mail_quota", conf_set_mail_quota, conf_get_mail_quota, conf_setdef_mail_quota }, { "maildir", conf_set_maildir, conf_get_maildir, conf_setdef_maildir }, { "log_notice", conf_set_log_notice, conf_get_log_notice, conf_setdef_log_notice }, { "savebyname", conf_set_savebyname, conf_get_savebyname, conf_setdef_savebyname }, { "allow_bad_version", conf_set_allow_bad_version, conf_get_allow_bad_version, conf_setdef_allow_bad_version }, { "allow_unknown_version", conf_set_allow_unknown_version, conf_get_allow_unknown_version, conf_setdef_allow_unknown_version }, { "versioncheck_file", conf_set_versioncheck_file, conf_get_versioncheck_file, conf_setdef_versioncheck_file }, { "d2cs_version", conf_set_d2cs_version, conf_get_d2cs_version, conf_setdef_d2cs_version }, { "allow_d2cs_setname", conf_set_allow_d2cs_setname, conf_get_allow_d2cs_setname, conf_setdef_allow_d2cs_setname }, { "hashtable_size", conf_set_hashtable_size, conf_get_hashtable_size, conf_setdef_hashtable_size }, { "telnetaddrs", conf_set_telnetaddrs, conf_get_telnetaddrs, conf_setdef_telnetaddrs }, { "ipban_check_int", conf_set_ipban_check_int, conf_get_ipban_check_int, conf_setdef_ipban_check_int }, { "max_concurrent_logins", conf_set_max_concurrent_logins, conf_get_max_concurrent_logins, conf_setdef_max_concurrent_logins }, { "mapsfile", conf_set_mapsfile, conf_get_mapsfile, conf_setdef_mapsfile }, { "xplevelfile", conf_set_xplevelfile, conf_get_xplevelfile, conf_setdef_xplevelfile }, { "xpcalcfile", conf_set_xpcalcfile, conf_get_xpcalcfile, conf_setdef_xpcalcfile }, { "initkill_timer", conf_set_initkill_timer, conf_get_initkill_timer, conf_setdef_initkill_timer }, { "war3_ladder_update_secs", conf_set_war3_ladder_update_secs, conf_get_war3_ladder_update_secs, conf_setdef_war3_ladder_update_secs }, { "output_update_secs", conf_set_output_update_secs, conf_get_output_update_secs, conf_setdef_output_update_secs }, { "ladderdir", conf_set_ladderdir, conf_get_ladderdir, conf_setdef_ladderdir }, { "statusdir", conf_set_statusdir, conf_get_statusdir, conf_setdef_statusdir }, { "XML_output_ladder", conf_set_XML_output_ladder, conf_get_XML_output_ladder, conf_setdef_XML_output_ladder }, { "XML_status_output", conf_set_XML_status_output, conf_get_XML_status_output, conf_setdef_XML_status_output }, { "account_allowed_symbols", conf_set_account_allowed_symbols, conf_get_account_allowed_symbols, conf_setdef_account_allowed_symbols }, { "account_force_username", conf_set_account_force_username, conf_get_account_force_username, conf_setdef_account_force_username }, { "command_groups_file", conf_set_command_groups_file, conf_get_command_groups_file, conf_setdef_command_groups_file }, { "tournament_file", conf_set_tournament_file, conf_get_tournament_file, conf_setdef_tournament_file }, { "customicons_file", conf_set_customicons_file, conf_get_customicons_file, conf_setdef_customicons_file }, { "scriptdir", conf_set_scriptdir, conf_get_scriptdir, conf_setdef_scriptdir }, { "aliasfile", conf_set_aliasfile, conf_get_aliasfile, conf_setdef_aliasfile }, { "anongame_infos_file", conf_set_anongame_infos_file, conf_get_anongame_infos_file, conf_setdef_anongame_infos_file }, { "max_conns_per_IP", conf_set_max_conns_per_IP, conf_get_max_conns_per_IP, conf_setdef_max_conns_per_IP }, { "max_friends", conf_set_max_friends, conf_get_max_friends, conf_setdef_max_friends }, { "clan_newer_time", conf_set_clan_newer_time, conf_get_clan_newer_time, conf_setdef_clan_newer_time }, { "clan_max_members", conf_set_clan_max_members, conf_get_clan_max_members, conf_setdef_clan_max_members }, { "clan_channel_default_private", conf_set_clan_channel_default_private, conf_get_clan_channel_default_private, conf_setdef_clan_channel_default_private }, { "clan_min_invites", conf_set_clan_min_invites, conf_get_clan_min_invites, conf_setdef_clan_min_invites }, { "passfail_count", conf_set_passfail_count, conf_get_passfail_count, conf_setdef_passfail_count }, { "passfail_bantime", conf_set_passfail_bantime, conf_get_passfail_bantime, conf_setdef_passfail_bantime }, { "maxusers_per_channel", conf_set_maxusers_per_channel, conf_get_maxusers_per_channel, conf_setdef_maxusers_per_channel }, { "allowed_clients", conf_set_allowed_clients, conf_get_allowed_clients, conf_setdef_allowed_clients }, { "ladder_games", conf_set_ladder_games, conf_get_ladder_games, conf_setdef_ladder_games }, { "max_connections", conf_set_max_connections, conf_get_max_connections, conf_setdef_max_connections }, { "packet_limit", conf_set_packet_limit, conf_get_packet_limit, conf_setdef_packet_limit }, { "sync_on_logoff", conf_set_sync_on_logoff, conf_get_sync_on_logoff, conf_setdef_sync_on_logoff }, { "ladder_prefix", conf_set_ladder_prefix, conf_get_ladder_prefix, conf_setdef_ladder_prefix }, { "irc_network_name", conf_set_irc_network_name, conf_get_irc_network_name, conf_setdef_irc_network_name }, { "localize_by_country", conf_set_localize_by_country, conf_get_localize_by_country, conf_setdef_localize_by_country }, { "log_commands", conf_set_log_commands, conf_get_log_commands, conf_setdef_log_commands }, { "log_command_groups", conf_set_log_command_groups, conf_get_log_command_groups, conf_setdef_log_command_groups }, { "log_command_list", conf_set_log_command_list, conf_get_log_command_list, conf_setdef_log_command_list }, { "apiregaddrs", conf_set_apireg_addrs, conf_get_apireg_addrs, conf_setdef_apireg_addrs }, { "wgameresaddrs", conf_set_wgameres_addrs, conf_get_wgameres_addrs, conf_setdef_wgameres_addrs }, { "wolv1addrs", conf_set_wolv1_addrs, conf_get_wolv1_addrs, conf_setdef_wolv1_addrs }, { "wolv2addrs", conf_set_wolv2_addrs, conf_get_wolv2_addrs, conf_setdef_wolv2_addrs }, { "woltimezone", conf_set_wol_timezone, conf_get_wol_timezone, conf_setdef_wol_timezone }, { "wollongitude", conf_set_wol_longitude, conf_get_wol_longitude, conf_setdef_wol_longitude }, { "wollatitude", conf_set_wol_latitude, conf_get_wol_latitude, conf_setdef_wol_latitude }, { "wol_autoupdate_serverhost", conf_set_wol_autoupdate_serverhost, conf_get_wol_autoupdate_serverhost, conf_setdef_wol_autoupdate_serverhost }, { "wol_autoupdate_username", conf_set_wol_autoupdate_username, conf_get_wol_autoupdate_username, conf_setdef_wol_autoupdate_username }, { "wol_autoupdate_password", conf_set_wol_autoupdate_password, conf_get_wol_autoupdate_password, conf_setdef_wol_autoupdate_password }, { NULL, NULL, NULL, NONE }, }; extern int prefs_load(char const * filename) { std::FILE *fd; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } fd = std::fopen(filename, "rt"); if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file '{}'", filename); return -1; } if (conf_load_file(fd, conf_table)) { eventlog(eventlog_level_error, __FUNCTION__, "error loading config file '{}'", filename); std::fclose(fd); return -1; } std::fclose(fd); return 0; } extern void prefs_unload(void) { conf_unload(conf_table); } extern char const * prefs_get_storage_path(void) { return prefs_runtime_config.storage_path; } static int conf_set_storage_path(const char *valstr) { return conf_set_str(&prefs_runtime_config.storage_path, valstr, NULL); } static int conf_setdef_storage_path(void) { return conf_set_str(&prefs_runtime_config.storage_path, NULL, BNETD_STORAGE_PATH); } static const char* conf_get_storage_path(void) { return prefs_runtime_config.storage_path; } extern char const * prefs_get_filedir(void) { return prefs_runtime_config.filedir; } static int conf_set_filedir(const char *valstr) { return conf_set_str(&prefs_runtime_config.filedir, valstr, NULL); } static int conf_setdef_filedir(void) { return conf_set_str(&prefs_runtime_config.filedir, NULL, BNETD_FILE_DIR); } static const char* conf_get_filedir(void) { return prefs_runtime_config.filedir; } extern char const * prefs_get_i18ndir(void) { return prefs_runtime_config.i18ndir; } static int conf_set_i18ndir(const char *valstr) { return conf_set_str(&prefs_runtime_config.i18ndir, valstr, NULL); } static int conf_setdef_i18ndir(void) { return conf_set_str(&prefs_runtime_config.i18ndir, NULL, BNETD_I18N_DIR); } static const char* conf_get_i18ndir(void) { return prefs_runtime_config.i18ndir; } extern char const * prefs_get_logfile(void) { return prefs_runtime_config.logfile; } static int conf_set_logfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.logfile, valstr, NULL); } static int conf_setdef_logfile(void) { return conf_set_str(&prefs_runtime_config.logfile, NULL, BNETD_LOG_FILE); } static const char* conf_get_logfile(void) { return prefs_runtime_config.logfile; } extern char const * prefs_get_loglevels(void) { return prefs_runtime_config.loglevels; } static int conf_set_loglevels(const char *valstr) { return conf_set_str(&prefs_runtime_config.loglevels, valstr, NULL); } static int conf_setdef_loglevels(void) { return conf_set_str(&prefs_runtime_config.loglevels, NULL, BNETD_LOG_LEVELS); } static const char* conf_get_loglevels(void) { return prefs_runtime_config.loglevels; } extern char const * prefs_get_localizefile(void) { return prefs_runtime_config.localizefile; } static int conf_set_localizefile(const char *valstr) { return conf_set_str(&prefs_runtime_config.localizefile, valstr, NULL); } static int conf_setdef_localizefile(void) { return conf_set_str(&prefs_runtime_config.localizefile, NULL, BNETD_LOCALIZE_FILE); } static const char* conf_get_localizefile(void) { return prefs_runtime_config.localizefile; } extern char const * prefs_get_motdfile(void) { return prefs_runtime_config.motdfile; } static int conf_set_motdfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.motdfile, valstr, NULL); } static int conf_setdef_motdfile(void) { return conf_set_str(&prefs_runtime_config.motdfile, NULL, BNETD_MOTD_FILE); } static const char* conf_get_motdfile(void) { return prefs_runtime_config.motdfile; } extern char const * prefs_get_motdw3file(void) { return prefs_runtime_config.motdw3file; } static int conf_set_motdw3file(const char *valstr) { return conf_set_str(&prefs_runtime_config.motdw3file, valstr, NULL); } static int conf_setdef_motdw3file(void) { return conf_set_str(&prefs_runtime_config.motdw3file, NULL, BNETD_MOTDW3_FILE); } static const char* conf_get_motdw3file(void) { return prefs_runtime_config.motdw3file; } extern char const * prefs_get_newsfile(void) { return prefs_runtime_config.newsfile; } static int conf_set_newsfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.newsfile, valstr, NULL); } static int conf_setdef_newsfile(void) { return conf_set_str(&prefs_runtime_config.newsfile, NULL, BNETD_NEWS_FILE); } static const char* conf_get_newsfile(void) { return prefs_runtime_config.newsfile; } extern char const * prefs_get_adfile(void) { return prefs_runtime_config.adfile; } static int conf_set_adfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.adfile, valstr, NULL); } static int conf_setdef_adfile(void) { return conf_set_str(&prefs_runtime_config.adfile, NULL, BNETD_AD_FILE); } static const char* conf_get_adfile(void) { return prefs_runtime_config.adfile; } extern char const * prefs_get_topicfile(void) { return prefs_runtime_config.topicfile; } static int conf_set_topicfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.topicfile, valstr, NULL); } static int conf_setdef_topicfile(void) { return conf_set_str(&prefs_runtime_config.topicfile, NULL, BNETD_TOPIC_FILE); } static const char* conf_get_topicfile(void) { return prefs_runtime_config.topicfile; } extern char const * prefs_get_DBlayoutfile(void) { return prefs_runtime_config.DBlayoutfile; } static int conf_set_DBlayoutfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.DBlayoutfile, valstr, NULL); } static int conf_setdef_DBlayoutfile(void) { return conf_set_str(&prefs_runtime_config.DBlayoutfile, NULL, BNETD_DBLAYOUT_FILE); } static const char* conf_get_DBlayoutfile(void) { return prefs_runtime_config.DBlayoutfile; } extern unsigned int prefs_get_user_sync_timer(void) { return prefs_runtime_config.usersync; } static int conf_set_usersync(const char *valstr) { return conf_set_int(&prefs_runtime_config.usersync, valstr, 0); } static int conf_setdef_usersync(void) { return conf_set_int(&prefs_runtime_config.usersync, NULL, BNETD_USERSYNC); } static const char* conf_get_usersync(void) { return conf_get_int(prefs_runtime_config.usersync); } extern unsigned int prefs_get_user_flush_timer(void) { return prefs_runtime_config.userflush; } static int conf_set_userflush(const char *valstr) { return conf_set_int(&prefs_runtime_config.userflush, valstr, 0); } static int conf_setdef_userflush(void) { return conf_set_int(&prefs_runtime_config.userflush, NULL, BNETD_USERFLUSH); } static const char* conf_get_userflush(void) { return conf_get_int(prefs_runtime_config.userflush); } extern unsigned int prefs_get_user_flush_connected(void) { return prefs_runtime_config.userflush_connected; } static int conf_set_userflush_connected(const char *valstr) { return conf_set_bool(&prefs_runtime_config.userflush_connected, valstr, 0); } static int conf_setdef_userflush_connected(void) { return conf_set_bool(&prefs_runtime_config.userflush_connected, NULL, 0); } static const char* conf_get_userflush_connected(void) { return conf_get_bool(prefs_runtime_config.userflush_connected); } extern unsigned int prefs_get_user_step(void) { return prefs_runtime_config.userstep; } static int conf_set_userstep(const char *valstr) { return conf_set_int(&prefs_runtime_config.userstep, valstr, 0); } static int conf_setdef_userstep(void) { return conf_set_int(&prefs_runtime_config.userstep, NULL, BNETD_USERSTEP); } static const char* conf_get_userstep(void) { return conf_get_int(prefs_runtime_config.userstep); } extern char const * prefs_get_servername(void) { return prefs_runtime_config.servername; } static int conf_set_servername(const char *valstr) { return conf_set_str(&prefs_runtime_config.servername, valstr, NULL); } static int conf_setdef_servername(void) { return conf_set_str(&prefs_runtime_config.servername, NULL, BNETD_SERVERNAME); } static const char* conf_get_servername(void) { return prefs_runtime_config.servername; } extern char const * prefs_get_hostname(void) { return prefs_runtime_config.hostname; } static int conf_set_hostname(const char *valstr) { return conf_set_str(&prefs_runtime_config.hostname, valstr, NULL); } static int conf_setdef_hostname(void) { return conf_set_str(&prefs_runtime_config.hostname, NULL, ""); } static const char* conf_get_hostname(void) { return prefs_runtime_config.hostname; } extern unsigned int prefs_get_track(void) { return prefs_runtime_config.track; } static int conf_set_track(const char *valstr) { unsigned int rez; conf_set_int(&prefs_runtime_config.track, valstr, 0); rez = prefs_runtime_config.track; if (rez > 0 && rez < 60) rez = 60; return 0; } static int conf_setdef_track(void) { return conf_set_int(&prefs_runtime_config.track, NULL, BNETD_TRACK_TIME); } static const char* conf_get_track(void) { return conf_get_int(prefs_runtime_config.track); } extern char const * prefs_get_location(void) { return prefs_runtime_config.location; } static int conf_set_location(const char *valstr) { return conf_set_str(&prefs_runtime_config.location, valstr, NULL); } static int conf_setdef_location(void) { return conf_set_str(&prefs_runtime_config.location, NULL, ""); } static const char* conf_get_location(void) { return prefs_runtime_config.location; } extern char const * prefs_get_description(void) { return prefs_runtime_config.description; } static int conf_set_description(const char *valstr) { return conf_set_str(&prefs_runtime_config.description, valstr, NULL); } static int conf_setdef_description(void) { return conf_set_str(&prefs_runtime_config.description, NULL, ""); } static const char* conf_get_description(void) { return prefs_runtime_config.description; } extern char const * prefs_get_url(void) { return prefs_runtime_config.url; } static int conf_set_url(const char *valstr) { return conf_set_str(&prefs_runtime_config.url, valstr, NULL); } static int conf_setdef_url(void) { return conf_set_str(&prefs_runtime_config.url, NULL, ""); } static const char* conf_get_url(void) { return prefs_runtime_config.url; } extern char const * prefs_get_contact_name(void) { return prefs_runtime_config.contact_name; } static int conf_set_contact_name(const char *valstr) { return conf_set_str(&prefs_runtime_config.contact_name, valstr, NULL); } static int conf_setdef_contact_name(void) { return conf_set_str(&prefs_runtime_config.contact_name, NULL, ""); } static const char* conf_get_contact_name(void) { return prefs_runtime_config.contact_name; } extern char const * prefs_get_contact_email(void) { return prefs_runtime_config.contact_email; } static int conf_set_contact_email(const char *valstr) { return conf_set_str(&prefs_runtime_config.contact_email, valstr, NULL); } static int conf_setdef_contact_email(void) { return conf_set_str(&prefs_runtime_config.contact_email, NULL, ""); } static const char* conf_get_contact_email(void) { return prefs_runtime_config.contact_email; } extern unsigned int prefs_get_latency(void) { return prefs_runtime_config.latency; } static int conf_set_latency(const char *valstr) { return conf_set_int(&prefs_runtime_config.latency, valstr, 0); } static int conf_setdef_latency(void) { return conf_set_int(&prefs_runtime_config.latency, NULL, BNETD_LATENCY); } static const char* conf_get_latency(void) { return conf_get_int(prefs_runtime_config.latency); } extern unsigned int prefs_get_irc_latency(void) { return prefs_runtime_config.irc_latency; } static int conf_set_irc_latency(const char *valstr) { return conf_set_int(&prefs_runtime_config.irc_latency, valstr, 0); } static int conf_setdef_irc_latency(void) { return conf_set_int(&prefs_runtime_config.irc_latency, NULL, BNETD_IRC_LATENCY); } static const char* conf_get_irc_latency(void) { return conf_get_int(prefs_runtime_config.irc_latency); } extern unsigned int prefs_get_shutdown_delay(void) { return prefs_runtime_config.shutdown_delay; } static int conf_set_shutdown_delay(const char *valstr) { return conf_set_int(&prefs_runtime_config.shutdown_delay, valstr, 0); } static int conf_setdef_shutdown_delay(void) { return conf_set_int(&prefs_runtime_config.shutdown_delay, NULL, BNETD_SHUTDELAY); } static const char* conf_get_shutdown_delay(void) { return conf_get_int(prefs_runtime_config.shutdown_delay); } extern unsigned int prefs_get_shutdown_decr(void) { return prefs_runtime_config.shutdown_decr; } static int conf_set_shutdown_decr(const char *valstr) { return conf_set_int(&prefs_runtime_config.shutdown_decr, valstr, 0); } static int conf_setdef_shutdown_decr(void) { return conf_set_int(&prefs_runtime_config.shutdown_decr, NULL, BNETD_SHUTDECR); } static const char* conf_get_shutdown_decr(void) { return conf_get_int(prefs_runtime_config.shutdown_decr); } extern unsigned int prefs_get_allow_new_accounts(void) { return prefs_runtime_config.new_accounts; } static int conf_set_new_accounts(const char *valstr) { return conf_set_bool(&prefs_runtime_config.new_accounts, valstr, 0); } static int conf_setdef_new_accounts(void) { return conf_set_bool(&prefs_runtime_config.new_accounts, NULL, 1); } static const char* conf_get_new_accounts(void) { return conf_get_bool(prefs_runtime_config.new_accounts); } extern unsigned int prefs_get_max_accounts(void) { return prefs_runtime_config.max_accounts; } static int conf_set_max_accounts(const char *valstr) { return conf_set_int(&prefs_runtime_config.max_accounts, valstr, 0); } static int conf_setdef_max_accounts(void) { return conf_set_int(&prefs_runtime_config.max_accounts, NULL, 0); } static const char* conf_get_max_accounts(void) { return conf_get_int(prefs_runtime_config.max_accounts); } extern unsigned int prefs_get_kick_old_login(void) { return prefs_runtime_config.kick_old_login; } static int conf_set_kick_old_login(const char *valstr) { return conf_set_bool(&prefs_runtime_config.kick_old_login, valstr, 0); } static int conf_setdef_kick_old_login(void) { return conf_set_bool(&prefs_runtime_config.kick_old_login, NULL, 1); } static const char* conf_get_kick_old_login(void) { return conf_get_bool(prefs_runtime_config.kick_old_login); } extern char const * prefs_get_channelfile(void) { return prefs_runtime_config.channelfile; } static int conf_set_channelfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.channelfile, valstr, NULL); } static int conf_setdef_channelfile(void) { return conf_set_str(&prefs_runtime_config.channelfile, NULL, BNETD_CHANNEL_FILE); } static const char* conf_get_channelfile(void) { return prefs_runtime_config.channelfile; } extern unsigned int prefs_get_ask_new_channel(void) { return prefs_runtime_config.ask_new_channel; } static int conf_set_ask_new_channel(const char *valstr) { return conf_set_bool(&prefs_runtime_config.ask_new_channel, valstr, 0); } static int conf_setdef_ask_new_channel(void) { return conf_set_bool(&prefs_runtime_config.ask_new_channel, NULL, 1); } static const char* conf_get_ask_new_channel(void) { return conf_get_bool(prefs_runtime_config.ask_new_channel); } extern unsigned int prefs_get_hide_pass_games(void) { return prefs_runtime_config.hide_pass_games; } static int conf_set_hide_pass_games(const char *valstr) { return conf_set_bool(&prefs_runtime_config.hide_pass_games, valstr, 0); } static int conf_setdef_hide_pass_games(void) { return conf_set_bool(&prefs_runtime_config.hide_pass_games, NULL, 1); } static const char* conf_get_hide_pass_games(void) { return conf_get_bool(prefs_runtime_config.hide_pass_games); } extern unsigned int prefs_get_hide_started_games(void) { return prefs_runtime_config.hide_started_games; } static int conf_set_hide_started_games(const char *valstr) { return conf_set_bool(&prefs_runtime_config.hide_started_games, valstr, 0); } static int conf_setdef_hide_started_games(void) { return conf_set_bool(&prefs_runtime_config.hide_started_games, NULL, 1); } static const char* conf_get_hide_started_games(void) { return conf_get_bool(prefs_runtime_config.hide_started_games); } extern unsigned int prefs_get_hide_temp_channels(void) { return prefs_runtime_config.hide_temp_channels; } static int conf_set_hide_temp_channels(const char *valstr) { return conf_set_bool(&prefs_runtime_config.hide_temp_channels, valstr, 0); } static int conf_setdef_hide_temp_channels(void) { return conf_set_bool(&prefs_runtime_config.hide_temp_channels, NULL, 1); } static const char* conf_get_hide_temp_channels(void) { return conf_get_bool(prefs_runtime_config.hide_temp_channels); } extern unsigned prefs_get_hide_addr(void) { return prefs_runtime_config.hide_addr; } static int conf_set_hide_addr(const char *valstr) { return conf_set_bool(&prefs_runtime_config.hide_addr, valstr, 0); } static int conf_setdef_hide_addr(void) { return conf_set_bool(&prefs_runtime_config.hide_addr, NULL, 1); } static const char* conf_get_hide_addr(void) { return conf_get_bool(prefs_runtime_config.hide_addr); } extern unsigned int prefs_get_enable_conn_all(void) { return prefs_runtime_config.enable_conn_all; } static int conf_set_enable_conn_all(const char *valstr) { return conf_set_bool(&prefs_runtime_config.enable_conn_all, valstr, 0); } static int conf_setdef_enable_conn_all(void) { return conf_set_bool(&prefs_runtime_config.enable_conn_all, NULL, 0); } static const char* conf_get_enable_conn_all(void) { return conf_get_bool(prefs_runtime_config.enable_conn_all); } extern char const * prefs_get_reportdir(void) { return prefs_runtime_config.reportdir; } static int conf_set_reportdir(const char *valstr) { return conf_set_str(&prefs_runtime_config.reportdir, valstr, NULL); } static int conf_setdef_reportdir(void) { return conf_set_str(&prefs_runtime_config.reportdir, NULL, BNETD_REPORT_DIR); } static const char* conf_get_reportdir(void) { return prefs_runtime_config.reportdir; } extern unsigned int prefs_get_report_all_games(void) { return prefs_runtime_config.report_all_games; } static int conf_set_report_all_games(const char *valstr) { return conf_set_bool(&prefs_runtime_config.report_all_games, valstr, 0); } static int conf_setdef_report_all_games(void) { return conf_set_bool(&prefs_runtime_config.report_all_games, NULL, 0); } static const char* conf_get_report_all_games(void) { return conf_get_bool(prefs_runtime_config.report_all_games); } extern unsigned int prefs_get_report_diablo_games(void) { return prefs_runtime_config.report_diablo_games; } static int conf_set_report_diablo_games(const char *valstr) { return conf_set_bool(&prefs_runtime_config.report_diablo_games, valstr, 0); } static int conf_setdef_report_diablo_games(void) { return conf_set_bool(&prefs_runtime_config.report_diablo_games, NULL, 0); } static const char* conf_get_report_diablo_games(void) { return conf_get_bool(prefs_runtime_config.report_diablo_games); } extern char const * prefs_get_pidfile(void) { return prefs_runtime_config.pidfile; } static int conf_set_pidfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.pidfile, valstr, NULL); } static int conf_setdef_pidfile(void) { return conf_set_str(&prefs_runtime_config.pidfile, NULL, BNETD_PID_FILE); } static const char* conf_get_pidfile(void) { return prefs_runtime_config.pidfile; } extern char const * prefs_get_iconfile(void) { return prefs_runtime_config.iconfile; } static int conf_set_iconfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.iconfile, valstr, NULL); } static int conf_setdef_iconfile(void) { return conf_set_str(&prefs_runtime_config.iconfile, NULL, BNETD_ICON_FILE); } static const char* conf_get_iconfile(void) { return prefs_runtime_config.iconfile; } extern char const * prefs_get_war3_iconfile(void) { return prefs_runtime_config.war3_iconfile; } static int conf_set_war3_iconfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.war3_iconfile, valstr, NULL); } static int conf_setdef_war3_iconfile(void) { return conf_set_str(&prefs_runtime_config.war3_iconfile, NULL, BNETD_WAR3_ICON_FILE); } static const char* conf_get_war3_iconfile(void) { return prefs_runtime_config.war3_iconfile; } extern char const * prefs_get_star_iconfile(void) { return prefs_runtime_config.star_iconfile; } static int conf_set_star_iconfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.star_iconfile, valstr, NULL); } static int conf_setdef_star_iconfile(void) { return conf_set_str(&prefs_runtime_config.star_iconfile, NULL, BNETD_STAR_ICON_FILE); } static const char* conf_get_star_iconfile(void) { return prefs_runtime_config.star_iconfile; } extern char const * prefs_get_tosfile(void) { return prefs_runtime_config.tosfile; } static int conf_set_tosfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.tosfile, valstr, NULL); } static int conf_setdef_tosfile(void) { return conf_set_str(&prefs_runtime_config.tosfile, NULL, BNETD_TOS_FILE); } static const char* conf_get_tosfile(void) { return prefs_runtime_config.tosfile; } extern char const * prefs_get_mpqfile(void) { return prefs_runtime_config.mpqfile; } static int conf_set_mpqfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.mpqfile, valstr, NULL); } static int conf_setdef_mpqfile(void) { return conf_set_str(&prefs_runtime_config.mpqfile, NULL, BNETD_MPQ_FILE); } static const char* conf_get_mpqfile(void) { return prefs_runtime_config.mpqfile; } extern char const * prefs_get_trackserv_addrs(void) { return prefs_runtime_config.trackaddrs; } static int conf_set_trackaddrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.trackaddrs, valstr, NULL); } static int conf_setdef_trackaddrs(void) { return conf_set_str(&prefs_runtime_config.trackaddrs, NULL, BNETD_TRACK_ADDRS); } static const char* conf_get_trackaddrs(void) { return prefs_runtime_config.trackaddrs; } extern char const * prefs_get_bnetdserv_addrs(void) { return prefs_runtime_config.servaddrs; } static int conf_set_servaddrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.servaddrs, valstr, NULL); } static int conf_setdef_servaddrs(void) { return conf_set_str(&prefs_runtime_config.servaddrs, NULL, BNETD_SERV_ADDRS); } static const char* conf_get_servaddrs(void) { return prefs_runtime_config.servaddrs; } extern char const * prefs_get_w3route_addr(void) { return prefs_runtime_config.w3routeaddr; } static int conf_set_w3routeaddr(const char *valstr) { return conf_set_str(&prefs_runtime_config.w3routeaddr, valstr, NULL); } static int conf_setdef_w3routeaddr(void) { return conf_set_str(&prefs_runtime_config.w3routeaddr, NULL, BNETD_W3ROUTE_ADDR); } static const char* conf_get_w3routeaddr(void) { return prefs_runtime_config.w3routeaddr; } extern char const * prefs_get_irc_addrs(void) { return prefs_runtime_config.ircaddrs; } static int conf_set_ircaddrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.ircaddrs, valstr, NULL); } static int conf_setdef_ircaddrs(void) { return conf_set_str(&prefs_runtime_config.ircaddrs, NULL, BNETD_IRC_ADDRS); } static const char* conf_get_ircaddrs(void) { return prefs_runtime_config.ircaddrs; } extern unsigned int prefs_get_use_keepalive(void) { return prefs_runtime_config.use_keepalive; } static int conf_set_use_keepalive(const char *valstr) { return conf_set_bool(&prefs_runtime_config.use_keepalive, valstr, 0); } static int conf_setdef_use_keepalive(void) { return conf_set_bool(&prefs_runtime_config.use_keepalive, NULL, 0); } static const char* conf_get_use_keepalive(void) { return conf_get_bool(prefs_runtime_config.use_keepalive); } extern unsigned int prefs_get_udptest_port(void) { return prefs_runtime_config.udptest_port; } static int conf_set_udptest_port(const char *valstr) { return conf_set_int(&prefs_runtime_config.udptest_port, valstr, 0); } static int conf_setdef_udptest_port(void) { return conf_set_int(&prefs_runtime_config.udptest_port, NULL, BNETD_DEF_TEST_PORT); } static const char* conf_get_udptest_port(void) { return conf_get_int(prefs_runtime_config.udptest_port); } extern char const * prefs_get_ipbanfile(void) { return prefs_runtime_config.ipbanfile; } static int conf_set_ipbanfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.ipbanfile, valstr, NULL); } static int conf_setdef_ipbanfile(void) { return conf_set_str(&prefs_runtime_config.ipbanfile, NULL, BNETD_IPBAN_FILE); } static const char* conf_get_ipbanfile(void) { return prefs_runtime_config.ipbanfile; } extern unsigned int prefs_get_discisloss(void) { return prefs_runtime_config.disc_is_loss; } static int conf_set_disc_is_loss(const char *valstr) { return conf_set_bool(&prefs_runtime_config.disc_is_loss, valstr, 0); } static int conf_setdef_disc_is_loss(void) { return conf_set_bool(&prefs_runtime_config.disc_is_loss, NULL, 0); } static const char* conf_get_disc_is_loss(void) { return conf_get_bool(prefs_runtime_config.disc_is_loss); } extern char const * prefs_get_helpfile(void) { return prefs_runtime_config.helpfile; } static int conf_set_helpfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.helpfile, valstr, NULL); } static int conf_setdef_helpfile(void) { return conf_set_str(&prefs_runtime_config.helpfile, NULL, BNETD_HELP_FILE); } static const char* conf_get_helpfile(void) { return prefs_runtime_config.helpfile; } extern char const * prefs_get_transfile(void) { return prefs_runtime_config.transfile; } static int conf_set_transfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.transfile, valstr, NULL); } static int conf_setdef_transfile(void) { return conf_set_str(&prefs_runtime_config.transfile, NULL, BNETD_TRANS_FILE); } static const char* conf_get_transfile(void) { return prefs_runtime_config.transfile; } extern unsigned int prefs_get_chanlog(void) { return prefs_runtime_config.chanlog; } static int conf_set_chanlog(const char *valstr) { return conf_set_bool(&prefs_runtime_config.chanlog, valstr, 0); } static int conf_setdef_chanlog(void) { return conf_set_bool(&prefs_runtime_config.chanlog, NULL, BNETD_CHANLOG); } static const char* conf_get_chanlog(void) { return conf_get_bool(prefs_runtime_config.chanlog); } extern char const * prefs_get_chanlogdir(void) { return prefs_runtime_config.chanlogdir; } static int conf_set_chanlogdir(const char *valstr) { return conf_set_str(&prefs_runtime_config.chanlogdir, valstr, NULL); } static int conf_setdef_chanlogdir(void) { return conf_set_str(&prefs_runtime_config.chanlogdir, NULL, BNETD_CHANLOG_DIR); } static const char* conf_get_chanlogdir(void) { return prefs_runtime_config.chanlogdir; } extern char const * prefs_get_userlogdir(void) { return prefs_runtime_config.userlogdir; } static int conf_set_userlogdir(const char *valstr) { return conf_set_str(&prefs_runtime_config.userlogdir, valstr, NULL); } static int conf_setdef_userlogdir(void) { return conf_set_str(&prefs_runtime_config.userlogdir, NULL, BNETD_USERLOG_DIR); } static const char* conf_get_userlogdir(void) { return prefs_runtime_config.userlogdir; } extern unsigned int prefs_get_quota(void) { return prefs_runtime_config.quota; } static int conf_set_quota(const char *valstr) { return conf_set_bool(&prefs_runtime_config.quota, valstr, 0); } static int conf_setdef_quota(void) { return conf_set_bool(&prefs_runtime_config.quota, NULL, 0); } static const char* conf_get_quota(void) { return conf_get_bool(prefs_runtime_config.quota); } extern unsigned int prefs_get_quota_lines(void) { unsigned int rez; rez = prefs_runtime_config.quota_lines; if (rez<1) rez = 1; if (rez>100) rez = 100; return rez; } static int conf_set_quota_lines(const char *valstr) { return conf_set_int(&prefs_runtime_config.quota_lines, valstr, 0); } static int conf_setdef_quota_lines(void) { return conf_set_int(&prefs_runtime_config.quota_lines, NULL, BNETD_QUOTA_LINES); } static const char* conf_get_quota_lines(void) { return conf_get_int(prefs_runtime_config.quota_lines); } extern unsigned int prefs_get_quota_time(void) { unsigned int rez; rez = prefs_runtime_config.quota_time; if (rez<1) rez = 1; if (rez>10) rez = 60; return rez; } static int conf_set_quota_time(const char *valstr) { return conf_set_int(&prefs_runtime_config.quota_time, valstr, 0); } static int conf_setdef_quota_time(void) { return conf_set_int(&prefs_runtime_config.quota_time, NULL, BNETD_QUOTA_TIME); } static const char* conf_get_quota_time(void) { return conf_get_int(prefs_runtime_config.quota_time); } extern unsigned int prefs_get_quota_wrapline(void) { unsigned int rez; rez = prefs_runtime_config.quota_wrapline; if (rez<1) rez = 1; if (rez>256) rez = 256; return rez; } static int conf_set_quota_wrapline(const char *valstr) { return conf_set_int(&prefs_runtime_config.quota_wrapline, valstr, 0); } static int conf_setdef_quota_wrapline(void) { return conf_set_int(&prefs_runtime_config.quota_wrapline, NULL, BNETD_QUOTA_WLINE); } static const char* conf_get_quota_wrapline(void) { return conf_get_int(prefs_runtime_config.quota_wrapline); } extern unsigned int prefs_get_quota_maxline(void) { unsigned int rez; rez = prefs_runtime_config.quota_maxline; if (rez<1) rez = 1; if (rez>256) rez = 256; return rez; } static int conf_set_quota_maxline(const char *valstr) { return conf_set_int(&prefs_runtime_config.quota_maxline, valstr, 0); } static int conf_setdef_quota_maxline(void) { return conf_set_int(&prefs_runtime_config.quota_maxline, NULL, BNETD_QUOTA_MLINE); } static const char* conf_get_quota_maxline(void) { return conf_get_int(prefs_runtime_config.quota_maxline); } extern unsigned int prefs_get_ladder_init_rating(void) { return prefs_runtime_config.ladder_init_rating; } static int conf_set_ladder_init_rating(const char *valstr) { return conf_set_int(&prefs_runtime_config.ladder_init_rating, valstr, 0); } static int conf_setdef_ladder_init_rating(void) { return conf_set_int(&prefs_runtime_config.ladder_init_rating, NULL, BNETD_LADDER_INIT_RAT); } static const char* conf_get_ladder_init_rating(void) { return conf_get_int(prefs_runtime_config.ladder_init_rating); } extern unsigned int prefs_get_quota_dobae(void) { unsigned int rez; rez = prefs_runtime_config.quota_dobae; if (rez<1) rez = 1; if (rez>100) rez = 100; return rez; } static int conf_set_quota_dobae(const char *valstr) { return conf_set_int(&prefs_runtime_config.quota_dobae, valstr, 0); } static int conf_setdef_quota_dobae(void) { return conf_set_int(&prefs_runtime_config.quota_dobae, NULL, BNETD_QUOTA_DOBAE); } static const char* conf_get_quota_dobae(void) { return conf_get_int(prefs_runtime_config.quota_dobae); } extern char const * prefs_get_realmfile(void) { return prefs_runtime_config.realmfile; } static int conf_set_realmfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.realmfile, valstr, NULL); } static int conf_setdef_realmfile(void) { return conf_set_str(&prefs_runtime_config.realmfile, NULL, BNETD_REALM_FILE); } static const char* conf_get_realmfile(void) { return prefs_runtime_config.realmfile; } extern char const * prefs_get_issuefile(void) { return prefs_runtime_config.issuefile; } static int conf_set_issuefile(const char *valstr) { return conf_set_str(&prefs_runtime_config.issuefile, valstr, NULL); } static int conf_setdef_issuefile(void) { return conf_set_str(&prefs_runtime_config.issuefile, NULL, BNETD_ISSUE_FILE); } static const char* conf_get_issuefile(void) { return prefs_runtime_config.issuefile; } extern char const * prefs_get_effective_user(void) { return prefs_runtime_config.effective_user; } static int conf_set_effective_user(const char *valstr) { return conf_set_str(&prefs_runtime_config.effective_user, valstr, NULL); } static int conf_setdef_effective_user(void) { return conf_set_str(&prefs_runtime_config.effective_user, NULL, NULL); } static const char* conf_get_effective_user(void) { return prefs_runtime_config.effective_user; } extern char const * prefs_get_effective_group(void) { return prefs_runtime_config.effective_group; } static int conf_set_effective_group(const char *valstr) { return conf_set_str(&prefs_runtime_config.effective_group, valstr, NULL); } static int conf_setdef_effective_group(void) { return conf_set_str(&prefs_runtime_config.effective_group, NULL, NULL); } static const char* conf_get_effective_group(void) { return prefs_runtime_config.effective_group; } extern unsigned int prefs_get_nullmsg(void) { return prefs_runtime_config.nullmsg; } static int conf_set_nullmsg(const char *valstr) { return conf_set_int(&prefs_runtime_config.nullmsg, valstr, 0); } static int conf_setdef_nullmsg(void) { return conf_set_int(&prefs_runtime_config.nullmsg, NULL, BNETD_DEF_NULLMSG); } static const char* conf_get_nullmsg(void) { return conf_get_int(prefs_runtime_config.nullmsg); } extern unsigned int prefs_get_mail_support(void) { return prefs_runtime_config.mail_support; } static int conf_set_mail_support(const char *valstr) { return conf_set_bool(&prefs_runtime_config.mail_support, valstr, 0); } static int conf_setdef_mail_support(void) { return conf_set_bool(&prefs_runtime_config.mail_support, NULL, BNETD_MAIL_SUPPORT); } static const char* conf_get_mail_support(void) { return conf_get_bool(prefs_runtime_config.mail_support); } extern unsigned int prefs_get_mail_quota(void) { unsigned int rez; rez = prefs_runtime_config.mail_quota; if (rez<1) rez = 1; if (rez>30) rez = 30; return rez; } static int conf_set_mail_quota(const char *valstr) { return conf_set_int(&prefs_runtime_config.mail_quota, valstr, 0); } static int conf_setdef_mail_quota(void) { return conf_set_int(&prefs_runtime_config.mail_quota, NULL, BNETD_MAIL_QUOTA); } static const char* conf_get_mail_quota(void) { return conf_get_int(prefs_runtime_config.mail_quota); } extern char const * prefs_get_maildir(void) { return prefs_runtime_config.maildir; } static int conf_set_maildir(const char *valstr) { return conf_set_str(&prefs_runtime_config.maildir, valstr, NULL); } static int conf_setdef_maildir(void) { return conf_set_str(&prefs_runtime_config.maildir, NULL, BNETD_MAIL_DIR); } static const char* conf_get_maildir(void) { return prefs_runtime_config.maildir; } extern char const * prefs_get_log_notice(void) { return prefs_runtime_config.log_notice; } static int conf_set_log_notice(const char *valstr) { return conf_set_str(&prefs_runtime_config.log_notice, valstr, NULL); } static int conf_setdef_log_notice(void) { return conf_set_str(&prefs_runtime_config.log_notice, NULL, BNETD_LOG_NOTICE); } static const char* conf_get_log_notice(void) { return prefs_runtime_config.log_notice; } extern unsigned int prefs_get_savebyname(void) { return prefs_runtime_config.savebyname; } static int conf_set_savebyname(const char *valstr) { return conf_set_bool(&prefs_runtime_config.savebyname, valstr, 0); } static int conf_setdef_savebyname(void) { return conf_set_bool(&prefs_runtime_config.savebyname, NULL, 1); } static const char* conf_get_savebyname(void) { return conf_get_bool(prefs_runtime_config.savebyname); } extern unsigned int prefs_get_allow_bad_version(void) { return prefs_runtime_config.allow_bad_version; } static int conf_set_allow_bad_version(const char *valstr) { return conf_set_bool(&prefs_runtime_config.allow_bad_version, valstr, 0); } static int conf_setdef_allow_bad_version(void) { return conf_set_bool(&prefs_runtime_config.allow_bad_version, NULL, 0); } static const char* conf_get_allow_bad_version(void) { return conf_get_bool(prefs_runtime_config.allow_bad_version); } extern unsigned int prefs_get_allow_unknown_version(void) { return prefs_runtime_config.allow_unknown_version; } static int conf_set_allow_unknown_version(const char *valstr) { return conf_set_bool(&prefs_runtime_config.allow_unknown_version, valstr, 0); } static int conf_setdef_allow_unknown_version(void) { return conf_set_bool(&prefs_runtime_config.allow_unknown_version, NULL, 0); } static const char* conf_get_allow_unknown_version(void) { return conf_get_bool(prefs_runtime_config.allow_unknown_version); } extern char const * prefs_get_versioncheck_file(void) { return prefs_runtime_config.versioncheck_file; } static int conf_set_versioncheck_file(const char *valstr) { return conf_set_str(&prefs_runtime_config.versioncheck_file, valstr, NULL); } static int conf_setdef_versioncheck_file(void) { return conf_set_str(&prefs_runtime_config.versioncheck_file, NULL, PVPGN_VERSIONCHECK); } static const char* conf_get_versioncheck_file(void) { return prefs_runtime_config.versioncheck_file; } extern unsigned int prefs_allow_d2cs_setname(void) { return prefs_runtime_config.allow_d2cs_setname; } static int conf_set_allow_d2cs_setname(const char *valstr) { return conf_set_bool(&prefs_runtime_config.allow_d2cs_setname, valstr, 0); } static int conf_setdef_allow_d2cs_setname(void) { return conf_set_bool(&prefs_runtime_config.allow_d2cs_setname, NULL, 1); } static const char* conf_get_allow_d2cs_setname(void) { return conf_get_bool(prefs_runtime_config.allow_d2cs_setname); } extern unsigned int prefs_get_d2cs_version(void) { return prefs_runtime_config.d2cs_version; } static int conf_set_d2cs_version(const char *valstr) { return conf_set_int(&prefs_runtime_config.d2cs_version, valstr, 0); } static int conf_setdef_d2cs_version(void) { return conf_set_int(&prefs_runtime_config.d2cs_version, NULL, 0); } static const char* conf_get_d2cs_version(void) { return conf_get_int(prefs_runtime_config.d2cs_version); } extern unsigned int prefs_get_hashtable_size(void) { return prefs_runtime_config.hashtable_size; } static int conf_set_hashtable_size(const char *valstr) { return conf_set_int(&prefs_runtime_config.hashtable_size, valstr, 0); } static int conf_setdef_hashtable_size(void) { return conf_set_int(&prefs_runtime_config.hashtable_size, NULL, BNETD_HASHTABLE_SIZE); } static const char* conf_get_hashtable_size(void) { return conf_get_int(prefs_runtime_config.hashtable_size); } extern char const * prefs_get_telnet_addrs(void) { return prefs_runtime_config.telnetaddrs; } static int conf_set_telnetaddrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.telnetaddrs, valstr, NULL); } static int conf_setdef_telnetaddrs(void) { return conf_set_str(&prefs_runtime_config.telnetaddrs, NULL, BNETD_TELNET_ADDRS); } static const char* conf_get_telnetaddrs(void) { return prefs_runtime_config.telnetaddrs; } extern unsigned int prefs_get_ipban_check_int(void) { return prefs_runtime_config.ipban_check_int; } static int conf_set_ipban_check_int(const char *valstr) { return conf_set_int(&prefs_runtime_config.ipban_check_int, valstr, 0); } static int conf_setdef_ipban_check_int(void) { return conf_set_int(&prefs_runtime_config.ipban_check_int, NULL, 30); } static const char* conf_get_ipban_check_int(void) { return conf_get_int(prefs_runtime_config.ipban_check_int); } extern unsigned int prefs_get_max_concurrent_logins(void) { return prefs_runtime_config.max_concurrent_logins; } static int conf_set_max_concurrent_logins(const char *valstr) { return conf_set_int(&prefs_runtime_config.max_concurrent_logins, valstr, 0); } static int conf_setdef_max_concurrent_logins(void) { return conf_set_int(&prefs_runtime_config.max_concurrent_logins, NULL, 0); } static const char* conf_get_max_concurrent_logins(void) { return conf_get_int(prefs_runtime_config.max_concurrent_logins); } extern char const * prefs_get_mapsfile(void) { return prefs_runtime_config.mapsfile; } static int conf_set_mapsfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.mapsfile, valstr, NULL); } static int conf_setdef_mapsfile(void) { return conf_set_str(&prefs_runtime_config.mapsfile, NULL, NULL); } static const char* conf_get_mapsfile(void) { return prefs_runtime_config.mapsfile; } extern char const * prefs_get_xplevel_file(void) { return prefs_runtime_config.xplevelfile; } static int conf_set_xplevelfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.xplevelfile, valstr, NULL); } static int conf_setdef_xplevelfile(void) { return conf_set_str(&prefs_runtime_config.xplevelfile, NULL, NULL); } static const char* conf_get_xplevelfile(void) { return prefs_runtime_config.xplevelfile; } extern char const * prefs_get_xpcalc_file(void) { return prefs_runtime_config.xpcalcfile; } static int conf_set_xpcalcfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.xpcalcfile, valstr, NULL); } static int conf_setdef_xpcalcfile(void) { return conf_set_str(&prefs_runtime_config.xpcalcfile, NULL, NULL); } static const char* conf_get_xpcalcfile(void) { return prefs_runtime_config.xpcalcfile; } extern int prefs_get_initkill_timer(void) { return prefs_runtime_config.initkill_timer; } static int conf_set_initkill_timer(const char *valstr) { return conf_set_int(&prefs_runtime_config.initkill_timer, valstr, 0); } static int conf_setdef_initkill_timer(void) { return conf_set_int(&prefs_runtime_config.initkill_timer, NULL, 0); } static const char* conf_get_initkill_timer(void) { return conf_get_int(prefs_runtime_config.initkill_timer); } extern int prefs_get_war3_ladder_update_secs(void) { return prefs_runtime_config.war3_ladder_update_secs; } static int conf_set_war3_ladder_update_secs(const char *valstr) { return conf_set_int(&prefs_runtime_config.war3_ladder_update_secs, valstr, 0); } static int conf_setdef_war3_ladder_update_secs(void) { return conf_set_int(&prefs_runtime_config.war3_ladder_update_secs, NULL, 0); } static const char* conf_get_war3_ladder_update_secs(void) { return conf_get_int(prefs_runtime_config.war3_ladder_update_secs); } extern int prefs_get_output_update_secs(void) { return prefs_runtime_config.output_update_secs; } static int conf_set_output_update_secs(const char *valstr) { return conf_set_int(&prefs_runtime_config.output_update_secs, valstr, 0); } static int conf_setdef_output_update_secs(void) { return conf_set_int(&prefs_runtime_config.output_update_secs, NULL, 0); } static const char* conf_get_output_update_secs(void) { return conf_get_int(prefs_runtime_config.output_update_secs); } extern char const * prefs_get_ladderdir(void) { return prefs_runtime_config.ladderdir; } static int conf_set_ladderdir(const char *valstr) { return conf_set_str(&prefs_runtime_config.ladderdir, valstr, NULL); } static int conf_setdef_ladderdir(void) { return conf_set_str(&prefs_runtime_config.ladderdir, NULL, BNETD_LADDER_DIR); } static const char* conf_get_ladderdir(void) { return prefs_runtime_config.ladderdir; } extern char const * prefs_get_outputdir(void) { return prefs_runtime_config.statusdir; } static int conf_set_statusdir(const char *valstr) { return conf_set_str(&prefs_runtime_config.statusdir, valstr, NULL); } static int conf_setdef_statusdir(void) { return conf_set_str(&prefs_runtime_config.statusdir, NULL, BNETD_STATUS_DIR); } static const char* conf_get_statusdir(void) { return prefs_runtime_config.statusdir; } extern int prefs_get_XML_output_ladder(void) { return prefs_runtime_config.XML_output_ladder; } static int conf_set_XML_output_ladder(const char *valstr) { return conf_set_bool(&prefs_runtime_config.XML_output_ladder, valstr, 0); } static int conf_setdef_XML_output_ladder(void) { return conf_set_bool(&prefs_runtime_config.XML_output_ladder, NULL, 0); } static const char* conf_get_XML_output_ladder(void) { return conf_get_bool(prefs_runtime_config.XML_output_ladder); } extern int prefs_get_XML_status_output(void) { return prefs_runtime_config.XML_status_output; } static int conf_set_XML_status_output(const char *valstr) { return conf_set_bool(&prefs_runtime_config.XML_status_output, valstr, 0); } static int conf_setdef_XML_status_output(void) { return conf_set_bool(&prefs_runtime_config.XML_status_output, NULL, 0); } static const char* conf_get_XML_status_output(void) { return conf_get_bool(prefs_runtime_config.XML_status_output); } extern char const * prefs_get_account_allowed_symbols(void) { return prefs_runtime_config.account_allowed_symbols; } static int conf_set_account_allowed_symbols(const char *valstr) { return conf_set_str(&prefs_runtime_config.account_allowed_symbols, valstr, NULL); } static int conf_setdef_account_allowed_symbols(void) { return conf_set_str(&prefs_runtime_config.account_allowed_symbols, NULL, PVPGN_DEFAULT_SYMB); } static const char* conf_get_account_allowed_symbols(void) { return prefs_runtime_config.account_allowed_symbols; } extern unsigned int prefs_get_account_force_username(void) { return prefs_runtime_config.account_force_username; } static int conf_set_account_force_username(const char *valstr) { return conf_set_bool(&prefs_runtime_config.account_force_username, valstr, 0); } static int conf_setdef_account_force_username(void) { return conf_set_bool(&prefs_runtime_config.account_force_username, NULL, 0); } static const char* conf_get_account_force_username(void) { return conf_get_bool(prefs_runtime_config.account_force_username); } extern char const * prefs_get_command_groups_file(void) { return prefs_runtime_config.command_groups_file; } static int conf_set_command_groups_file(const char *valstr) { return conf_set_str(&prefs_runtime_config.command_groups_file, valstr, NULL); } static int conf_setdef_command_groups_file(void) { return conf_set_str(&prefs_runtime_config.command_groups_file, NULL, BNETD_COMMAND_GROUPS_FILE); } static const char* conf_get_command_groups_file(void) { return prefs_runtime_config.command_groups_file; } extern char const * prefs_get_tournament_file(void) { return prefs_runtime_config.tournament_file; } static int conf_set_tournament_file(const char *valstr) { return conf_set_str(&prefs_runtime_config.tournament_file, valstr, NULL); } static int conf_setdef_tournament_file(void) { return conf_set_str(&prefs_runtime_config.tournament_file, NULL, BNETD_TOURNAMENT_FILE); } static const char* conf_get_tournament_file(void) { return prefs_runtime_config.tournament_file; } extern char const * prefs_get_customicons_file(void) { return prefs_runtime_config.customicons_file; } static int conf_set_customicons_file(const char *valstr) { return conf_set_str(&prefs_runtime_config.customicons_file, valstr, NULL); } static int conf_setdef_customicons_file(void) { return conf_set_str(&prefs_runtime_config.customicons_file, NULL, BNETD_CUSTOMICONS_FILE); } static const char* conf_get_customicons_file(void) { return prefs_runtime_config.customicons_file; } extern char const * prefs_get_scriptdir(void) { return prefs_runtime_config.scriptdir; } static int conf_set_scriptdir(const char *valstr) { return conf_set_str(&prefs_runtime_config.scriptdir, valstr, NULL); } static int conf_setdef_scriptdir(void) { return conf_set_str(&prefs_runtime_config.scriptdir, NULL, BNETD_SCRIPT_DIR); } static const char* conf_get_scriptdir(void) { return prefs_runtime_config.scriptdir; } extern char const * prefs_get_aliasfile(void) { return prefs_runtime_config.aliasfile; } static int conf_set_aliasfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.aliasfile, valstr, NULL); } static int conf_setdef_aliasfile(void) { return conf_set_str(&prefs_runtime_config.aliasfile, NULL, BNETD_ALIASFILE); } static const char* conf_get_aliasfile(void) { return prefs_runtime_config.aliasfile; } extern char const * prefs_get_anongame_infos_file(void) { return prefs_runtime_config.anongame_infos_file; } static int conf_set_anongame_infos_file(const char *valstr) { return conf_set_str(&prefs_runtime_config.anongame_infos_file, valstr, NULL); } static int conf_setdef_anongame_infos_file(void) { return conf_set_str(&prefs_runtime_config.anongame_infos_file, NULL, PVPGN_AINFO_FILE); } static const char* conf_get_anongame_infos_file(void) { return prefs_runtime_config.anongame_infos_file; } extern unsigned int prefs_get_max_conns_per_IP(void) { return prefs_runtime_config.max_conns_per_IP; } static int conf_set_max_conns_per_IP(const char *valstr) { return conf_set_int(&prefs_runtime_config.max_conns_per_IP, valstr, 0); } static int conf_setdef_max_conns_per_IP(void) { return conf_set_int(&prefs_runtime_config.max_conns_per_IP, NULL, 0); } static const char* conf_get_max_conns_per_IP(void) { return conf_get_int(prefs_runtime_config.max_conns_per_IP); } extern int prefs_get_max_friends(void) { return prefs_runtime_config.max_friends; } static int conf_set_max_friends(const char *valstr) { return conf_set_int(&prefs_runtime_config.max_friends, valstr, 0); } static int conf_setdef_max_friends(void) { return conf_set_int(&prefs_runtime_config.max_friends, NULL, MAX_FRIENDS); } static const char* conf_get_max_friends(void) { return conf_get_int(prefs_runtime_config.max_friends); } extern unsigned int prefs_get_clan_newer_time(void) { return prefs_runtime_config.clan_newer_time; } static int conf_set_clan_newer_time(const char *valstr) { return conf_set_int(&prefs_runtime_config.clan_newer_time, valstr, 0); } static int conf_setdef_clan_newer_time(void) { return conf_set_int(&prefs_runtime_config.clan_newer_time, NULL, CLAN_NEWER_TIME); } static const char* conf_get_clan_newer_time(void) { return conf_get_int(prefs_runtime_config.clan_newer_time); } extern unsigned int prefs_get_clan_max_members(void) { return prefs_runtime_config.clan_max_members; } static int conf_set_clan_max_members(const char *valstr) { int rez = conf_set_int(&prefs_runtime_config.clan_max_members, valstr, 0); if (!rez && valstr) { if (prefs_runtime_config.clan_max_members < CLAN_MIN_MEMBERS) { WARN1("Cannot set clan max members to {} lower than 10, setting to 10.", prefs_runtime_config.clan_max_members); prefs_runtime_config.clan_max_members = CLAN_MIN_MEMBERS; } else if (prefs_runtime_config.clan_max_members > CLAN_MAX_MEMBERS) { WARN1("Cannot set clan max members to {} higher than 100, setting to 100.", prefs_runtime_config.clan_max_members); prefs_runtime_config.clan_max_members = CLAN_MAX_MEMBERS; } } return rez; } static int conf_setdef_clan_max_members(void) { return conf_set_int(&prefs_runtime_config.clan_max_members, NULL, CLAN_DEFAULT_MAX_MEMBERS); } static const char* conf_get_clan_max_members(void) { return conf_get_int(prefs_runtime_config.clan_max_members); } extern unsigned int prefs_get_clan_channel_default_private(void) { return prefs_runtime_config.clan_channel_default_private; } static int conf_set_clan_channel_default_private(const char *valstr) { return conf_set_bool(&prefs_runtime_config.clan_channel_default_private, valstr, 0); } static int conf_setdef_clan_channel_default_private(void) { return conf_set_bool(&prefs_runtime_config.clan_channel_default_private, NULL, 0); } static const char* conf_get_clan_channel_default_private(void) { return conf_get_bool(prefs_runtime_config.clan_channel_default_private); } extern unsigned int prefs_get_clan_min_invites(void) { return prefs_runtime_config.clan_min_invites; } static int conf_set_clan_min_invites(const char *valstr) { return conf_set_int(&prefs_runtime_config.clan_min_invites, valstr, 0); } static int conf_setdef_clan_min_invites(void) { return conf_set_int(&prefs_runtime_config.clan_min_invites, NULL, CLAN_DEFAULT_MIN_INVITES); } static const char* conf_get_clan_min_invites(void) { return conf_get_int(prefs_runtime_config.clan_min_invites); } extern unsigned int prefs_get_passfail_count(void) { return prefs_runtime_config.passfail_count; } static int conf_set_passfail_count(const char *valstr) { return conf_set_int(&prefs_runtime_config.passfail_count, valstr, 0); } static int conf_setdef_passfail_count(void) { return conf_set_int(&prefs_runtime_config.passfail_count, NULL, 0); } static const char* conf_get_passfail_count(void) { return conf_get_int(prefs_runtime_config.passfail_count); } extern unsigned int prefs_get_passfail_bantime(void) { return prefs_runtime_config.passfail_bantime; } static int conf_set_passfail_bantime(const char *valstr) { return conf_set_int(&prefs_runtime_config.passfail_bantime, valstr, 0); } static int conf_setdef_passfail_bantime(void) { return conf_set_int(&prefs_runtime_config.passfail_bantime, NULL, 300); } static const char* conf_get_passfail_bantime(void) { return conf_get_int(prefs_runtime_config.passfail_bantime); } extern unsigned int prefs_get_maxusers_per_channel(void) { return prefs_runtime_config.maxusers_per_channel; } static int conf_set_maxusers_per_channel(const char *valstr) { return conf_set_int(&prefs_runtime_config.maxusers_per_channel, valstr, 0); } static int conf_setdef_maxusers_per_channel(void) { return conf_set_int(&prefs_runtime_config.maxusers_per_channel, NULL, 0); } static const char* conf_get_maxusers_per_channel(void) { return conf_get_int(prefs_runtime_config.maxusers_per_channel); } extern char const * prefs_get_supportfile(void) { return prefs_runtime_config.supportfile; } static int conf_set_supportfile(const char *valstr) { return conf_set_str(&prefs_runtime_config.supportfile, valstr, NULL); } static int conf_setdef_supportfile(void) { return conf_set_str(&prefs_runtime_config.supportfile, NULL, BNETD_SUPPORT_FILE); } static const char* conf_get_supportfile(void) { return prefs_runtime_config.supportfile; } extern char const * prefs_get_allowed_clients(void) { return prefs_runtime_config.allowed_clients; } static int conf_set_allowed_clients(const char *valstr) { return conf_set_str(&prefs_runtime_config.allowed_clients, valstr, NULL); } static int conf_setdef_allowed_clients(void) { return conf_set_str(&prefs_runtime_config.allowed_clients, NULL, NULL); } static const char* conf_get_allowed_clients(void) { return prefs_runtime_config.allowed_clients; } extern char const * prefs_get_ladder_games(void) { return prefs_runtime_config.ladder_games; } static int conf_set_ladder_games(const char *valstr) { return conf_set_str(&prefs_runtime_config.ladder_games, valstr, NULL); } static int conf_setdef_ladder_games(void) { return conf_set_str(&prefs_runtime_config.ladder_games, NULL, NULL); } static const char* conf_get_ladder_games(void) { return prefs_runtime_config.ladder_games; } extern char const * prefs_get_ladder_prefix(void) { return prefs_runtime_config.ladder_prefix; } static int conf_set_ladder_prefix(const char *valstr) { return conf_set_str(&prefs_runtime_config.ladder_prefix, valstr, NULL); } static int conf_setdef_ladder_prefix(void) { return conf_set_str(&prefs_runtime_config.ladder_prefix, NULL, NULL); } static const char* conf_get_ladder_prefix(void) { return prefs_runtime_config.ladder_prefix; } extern unsigned int prefs_get_max_connections(void) { return prefs_runtime_config.max_connections; } static int conf_set_max_connections(const char *valstr) { return conf_set_int(&prefs_runtime_config.max_connections, valstr, 0); } static int conf_setdef_max_connections(void) { return conf_set_int(&prefs_runtime_config.max_connections, NULL, BNETD_MAX_SOCKETS); } static const char* conf_get_max_connections(void) { return conf_get_int(prefs_runtime_config.max_connections); } extern unsigned int prefs_get_packet_limit(void) { return prefs_runtime_config.packet_limit; } static int conf_set_packet_limit(const char *valstr) { return conf_set_int(&prefs_runtime_config.packet_limit, valstr, 0); } static int conf_setdef_packet_limit(void) { return conf_set_int(&prefs_runtime_config.packet_limit, NULL, BNETD_PACKET_LIMIT); } static const char* conf_get_packet_limit(void) { return conf_get_int(prefs_runtime_config.packet_limit); } extern unsigned int prefs_get_sync_on_logoff(void) { return prefs_runtime_config.sync_on_logoff; } static int conf_set_sync_on_logoff(const char *valstr) { return conf_set_bool(&prefs_runtime_config.sync_on_logoff, valstr, 0); } static int conf_setdef_sync_on_logoff(void) { return conf_set_bool(&prefs_runtime_config.sync_on_logoff, NULL, 0); } static const char* conf_get_sync_on_logoff(void) { return conf_get_bool(prefs_runtime_config.sync_on_logoff); } extern char const * prefs_get_irc_network_name(void) { return prefs_runtime_config.irc_network_name; } static int conf_set_irc_network_name(const char *valstr) { return conf_set_str(&prefs_runtime_config.irc_network_name, valstr, NULL); } static int conf_setdef_irc_network_name(void) { return conf_set_str(&prefs_runtime_config.irc_network_name, NULL, BNETD_IRC_NETWORK_NAME); } static const char* conf_get_irc_network_name(void) { return prefs_runtime_config.irc_network_name; } extern unsigned int prefs_get_localize_by_country(void) { return prefs_runtime_config.localize_by_country; } static int conf_set_localize_by_country(const char *valstr) { return conf_set_bool(&prefs_runtime_config.localize_by_country, valstr, 0); } static int conf_setdef_localize_by_country(void) { return conf_set_bool(&prefs_runtime_config.localize_by_country, NULL, 0); } static const char* conf_get_localize_by_country(void) { return conf_get_bool(prefs_runtime_config.localize_by_country); } extern unsigned int prefs_get_log_commands(void) { return prefs_runtime_config.log_commands; } static int conf_set_log_commands(const char *valstr) { return conf_set_bool(&prefs_runtime_config.log_commands, valstr, 0); } static int conf_setdef_log_commands(void) { return conf_set_bool(&prefs_runtime_config.log_commands, NULL, 1); } static const char* conf_get_log_commands(void) { return conf_get_bool(prefs_runtime_config.log_commands); } extern char const * prefs_get_log_command_groups(void) { return prefs_runtime_config.log_command_groups; } static int conf_set_log_command_groups(const char *valstr) { return conf_set_str(&prefs_runtime_config.log_command_groups, valstr, NULL); } static int conf_setdef_log_command_groups(void) { return conf_set_str(&prefs_runtime_config.log_command_groups, NULL, BNETD_LOG_COMMAND_GROUPS); } static const char* conf_get_log_command_groups(void) { return prefs_runtime_config.log_command_groups; } extern char const * prefs_get_log_command_list(void) { return prefs_runtime_config.log_command_list; } static int conf_set_log_command_list(const char *valstr) { return conf_set_str(&prefs_runtime_config.log_command_list, valstr, NULL); } static int conf_setdef_log_command_list(void) { return conf_set_str(&prefs_runtime_config.log_command_list, NULL, BNETD_LOG_COMMAND_LIST); } static const char* conf_get_log_command_list(void) { return prefs_runtime_config.log_command_list; } /** * Westwood Online Extensions */ extern char const * prefs_get_apireg_addrs(void) { return prefs_runtime_config.apiregaddrs; } static int conf_set_apireg_addrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.apiregaddrs, valstr, NULL); } static int conf_setdef_apireg_addrs(void) { return conf_set_str(&prefs_runtime_config.apiregaddrs, NULL, BNETD_APIREG_ADDRS); } static const char* conf_get_apireg_addrs(void) { return prefs_runtime_config.apiregaddrs; } extern char const * prefs_get_wgameres_addrs(void) { return prefs_runtime_config.wgameresaddrs; } static int conf_set_wgameres_addrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.wgameresaddrs, valstr, NULL); } static int conf_setdef_wgameres_addrs(void) { return conf_set_str(&prefs_runtime_config.wgameresaddrs, NULL, BNETD_WGAMERES_ADDRS); } static const char* conf_get_wgameres_addrs(void) { return prefs_runtime_config.wgameresaddrs; } extern char const * prefs_get_wolv1_addrs(void) { return prefs_runtime_config.wolv1addrs; } static int conf_set_wolv1_addrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.wolv1addrs, valstr, NULL); } static int conf_setdef_wolv1_addrs(void) { return conf_set_str(&prefs_runtime_config.wolv1addrs, NULL, BNETD_WOLV1_ADDRS); } static const char* conf_get_wolv1_addrs(void) { return prefs_runtime_config.wolv1addrs; } extern char const * prefs_get_wolv2_addrs(void) { return prefs_runtime_config.wolv2addrs; } static int conf_set_wolv2_addrs(const char *valstr) { return conf_set_str(&prefs_runtime_config.wolv2addrs, valstr, NULL); } static int conf_setdef_wolv2_addrs(void) { return conf_set_str(&prefs_runtime_config.wolv2addrs, NULL, BNETD_WOLV2_ADDRS); } static const char* conf_get_wolv2_addrs(void) { return prefs_runtime_config.wolv2addrs; } static int conf_set_wol_timezone(const char *valstr) { return conf_set_str(&prefs_runtime_config.woltimezone, valstr, NULL); } extern char const * prefs_get_wol_timezone(void) { return prefs_runtime_config.woltimezone; } static char const * conf_get_wol_timezone(void) { return prefs_runtime_config.woltimezone; } static int conf_setdef_wol_timezone(void) { return conf_set_str(&prefs_runtime_config.woltimezone, NULL, 0); } static int conf_set_wol_longitude(const char *valstr) { return conf_set_str(&prefs_runtime_config.wollongitude, valstr, NULL); } extern char const * prefs_get_wol_longitude(void) { return prefs_runtime_config.wollongitude; } static char const * conf_get_wol_longitude(void) { return prefs_runtime_config.wollongitude; } static int conf_setdef_wol_longitude(void) { return conf_set_str(&prefs_runtime_config.wollongitude, NULL, 0); } static int conf_set_wol_latitude(const char *valstr) { return conf_set_str(&prefs_runtime_config.wollatitude, valstr, NULL); } extern char const * prefs_get_wol_latitude(void) { return prefs_runtime_config.wollatitude; } static char const * conf_get_wol_latitude(void) { return prefs_runtime_config.wollatitude; } static int conf_setdef_wol_latitude(void) { return conf_set_str(&prefs_runtime_config.wollatitude, NULL, 0); } static int conf_set_wol_autoupdate_serverhost(const char *valstr) { return conf_set_str(&prefs_runtime_config.wol_autoupdate_serverhost, valstr, NULL); } extern char const * prefs_get_wol_autoupdate_serverhost(void) { return prefs_runtime_config.wol_autoupdate_serverhost; } static char const * conf_get_wol_autoupdate_serverhost(void) { return prefs_runtime_config.wol_autoupdate_serverhost; } static int conf_setdef_wol_autoupdate_serverhost(void) { return conf_set_str(&prefs_runtime_config.wol_autoupdate_serverhost, NULL, 0); } static int conf_set_wol_autoupdate_username(const char *valstr) { return conf_set_str(&prefs_runtime_config.wol_autoupdate_username, valstr, NULL); } extern char const * prefs_get_wol_autoupdate_username(void) { return prefs_runtime_config.wol_autoupdate_username; } static char const * conf_get_wol_autoupdate_username(void) { return prefs_runtime_config.wol_autoupdate_username; } static int conf_setdef_wol_autoupdate_username(void) { return conf_set_str(&prefs_runtime_config.wol_autoupdate_username, NULL, 0); } static int conf_set_wol_autoupdate_password(const char *valstr) { return conf_set_str(&prefs_runtime_config.wol_autoupdate_password, valstr, NULL); } extern char const * prefs_get_wol_autoupdate_password(void) { return prefs_runtime_config.wol_autoupdate_password; } static char const * conf_get_wol_autoupdate_password(void) { return prefs_runtime_config.wol_autoupdate_password; } static int conf_setdef_wol_autoupdate_password(void) { return conf_set_str(&prefs_runtime_config.wol_autoupdate_password, NULL, 0); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/prefs.h000066400000000000000000000210621462640461400173540ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999 Mark Baysinger (mbaysing@ucsd.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_PREFS_TYPES #define INCLUDED_PREFS_TYPES #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_PREFS_PROTOS #define INCLUDED_PREFS_PROTOS namespace pvpgn { namespace bnetd { extern int prefs_load(char const * filename); extern void prefs_unload(void); extern char const * prefs_get_storage_path(void); extern char const * prefs_get_filedir(void); extern char const * prefs_get_i18ndir(void); extern char const * prefs_get_logfile(void); extern char const * prefs_get_loglevels(void); extern char const * prefs_get_localizefile(void); extern char const * prefs_get_motdfile(void); extern char const * prefs_get_motdw3file(void); extern char const * prefs_get_newsfile(void); extern char const * prefs_get_adfile(void); extern char const * prefs_get_topicfile(void); extern char const * prefs_get_DBlayoutfile(void); extern unsigned int prefs_get_user_sync_timer(void); extern unsigned int prefs_get_user_flush_timer(void); extern unsigned int prefs_get_user_flush_connected(void); extern unsigned int prefs_get_user_step(void); extern char const * prefs_get_hostname(void); extern char const * prefs_get_servername(void); extern unsigned int prefs_get_track(void); extern char const * prefs_get_location(void); extern char const * prefs_get_description(void); extern char const * prefs_get_url(void); extern char const * prefs_get_contact_name(void); extern char const * prefs_get_contact_email(void); extern unsigned int prefs_get_latency(void); extern unsigned int prefs_get_irc_latency(void); extern unsigned int prefs_get_shutdown_delay(void); extern unsigned int prefs_get_shutdown_decr(void); extern unsigned int prefs_get_allow_new_accounts(void); extern unsigned int prefs_get_max_accounts(void); extern unsigned int prefs_get_kick_old_login(void); extern char const * prefs_get_channelfile(void); extern unsigned int prefs_get_ask_new_channel(void); extern unsigned int prefs_get_hide_pass_games(void); extern unsigned int prefs_get_hide_started_games(void); extern unsigned int prefs_get_hide_temp_channels(void); extern unsigned int prefs_get_hide_addr(void); extern unsigned int prefs_get_enable_conn_all(void); extern unsigned int prefs_get_udptest_port(void); extern char const * prefs_get_reportdir(void); extern unsigned int prefs_get_report_all_games(void); extern unsigned int prefs_get_report_diablo_games(void); extern char const * prefs_get_pidfile(void); extern char const * prefs_get_iconfile(void); extern char const * prefs_get_war3_iconfile(void); extern char const * prefs_get_star_iconfile(void); extern char const * prefs_get_tosfile(void); extern char const * prefs_get_mpqauthfile(void); extern char const * prefs_get_mpqfile(void); extern char const * prefs_get_trackserv_addrs(void); extern char const * prefs_get_bnetdserv_addrs(void); extern char const * prefs_get_irc_addrs(void); extern char const * prefs_get_w3route_addr(void); extern unsigned int prefs_get_use_keepalive(void); extern char const * prefs_get_ipbanfile(void); extern unsigned int prefs_get_discisloss(void); extern char const * prefs_get_helpfile(void); extern char const * prefs_get_transfile(void); extern unsigned int prefs_get_chanlog(void); extern char const * prefs_get_chanlogdir(void); extern char const * prefs_get_userlogdir(void); extern unsigned int prefs_get_quota(void); extern unsigned int prefs_get_quota_lines(void); extern unsigned int prefs_get_quota_time(void); extern unsigned int prefs_get_quota_wrapline(void); extern unsigned int prefs_get_quota_maxline(void); extern unsigned int prefs_get_ladder_init_rating(void); extern unsigned int prefs_get_quota_dobae(void); extern char const * prefs_get_realmfile(void); extern char const * prefs_get_issuefile(void); extern char const * prefs_get_effective_user(void); extern char const * prefs_get_effective_group(void); extern unsigned int prefs_get_nullmsg(void); extern unsigned int prefs_get_mail_support(void); extern unsigned int prefs_get_mail_quota(void); extern char const * prefs_get_maildir(void); extern char const * prefs_get_log_notice(void); extern unsigned int prefs_get_savebyname(void); extern unsigned int prefs_get_allow_bad_version(void); extern unsigned int prefs_get_allow_unknown_version(void); extern char const * prefs_get_versioncheck_file(void); extern unsigned int prefs_allow_d2cs_setname(void); extern unsigned int prefs_get_d2cs_version(void); extern unsigned int prefs_get_hashtable_size(void); extern char const * prefs_get_telnet_addrs(void); extern unsigned int prefs_get_ipban_check_int(void); extern unsigned int prefs_get_max_concurrent_logins(void); /* [zap-zero] 20020616 */ extern char const * prefs_get_mysql_host(void); extern char const * prefs_get_mysql_account(void); extern char const * prefs_get_mysql_password(void); extern char const * prefs_get_mysql_sock(void); extern char const * prefs_get_mysql_dbname(void); extern unsigned int prefs_get_mysql_persistent(void); extern char const * prefs_get_mapsfile(void); extern char const * prefs_get_xplevel_file(void); extern char const * prefs_get_xpcalc_file(void); extern int prefs_get_initkill_timer(void); //aaron extern int prefs_get_war3_ladder_update_secs(void); extern int prefs_get_output_update_secs(void); extern char const * prefs_get_ladderdir(void); extern char const * prefs_get_outputdir(void); extern int prefs_get_XML_output_ladder(void); extern int prefs_get_XML_status_output(void); extern char const * prefs_get_account_allowed_symbols(void); extern unsigned int prefs_get_account_force_username(void); extern char const * prefs_get_command_groups_file(void); extern char const * prefs_get_tournament_file(void); extern char const * prefs_get_customicons_file(void); extern char const * prefs_get_scriptdir(void); extern char const * prefs_get_aliasfile(void); extern char const * prefs_get_anongame_infos_file(void); extern unsigned int prefs_get_max_conns_per_IP(void); extern int prefs_get_max_friends(void); extern unsigned int prefs_get_clan_newer_time(void); extern unsigned int prefs_get_clan_max_members(void); extern unsigned int prefs_get_clan_channel_default_private(void); extern unsigned int prefs_get_clan_min_invites(void); extern unsigned int prefs_get_passfail_count(void); extern unsigned int prefs_get_passfail_bantime(void); extern unsigned int prefs_get_maxusers_per_channel(void); extern char const * prefs_get_supportfile(void); extern char const * prefs_get_allowed_clients(void); extern char const * prefs_get_ladder_games(void); extern char const * prefs_get_ladder_prefix(void); extern unsigned int prefs_get_max_connections(void); extern unsigned int prefs_get_packet_limit(void); extern unsigned int prefs_get_sync_on_logoff(void); extern char const * prefs_get_irc_network_name(void); extern unsigned int prefs_get_localize_by_country(void); extern unsigned int prefs_get_log_commands(void); extern char const * prefs_get_log_command_groups(void); extern char const * prefs_get_log_command_list(void); /** * Westwood Online Extensions */ extern char const * prefs_get_apireg_addrs(void); extern char const * prefs_get_wgameres_addrs(void); extern char const * prefs_get_wolv1_addrs(void); extern char const * prefs_get_wolv2_addrs(void); extern char const * prefs_get_wol_timezone(void); extern char const * prefs_get_wol_longitude(void); extern char const * prefs_get_wol_latitude(void); extern char const * prefs_get_wol_autoupdate_serverhost(void); extern char const * prefs_get_wol_autoupdate_username(void); extern char const * prefs_get_wol_autoupdate_password(void); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/quota.h000066400000000000000000000022411462640461400173640ustar00rootroot00000000000000/* * Copyright (C) 2000 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_QUOTA_TYPES #define INCLUDED_QUOTA_TYPES #include #ifdef JUST_NEED_TYPES # include "common/list.h" #else # define JUST_NEED_TYPES # include "common/list.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { namespace bnetd { typedef struct { std::time_t inf; unsigned int count; } t_qline; typedef struct { unsigned int totcount; t_list * list; } t_quota; } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/realm.cpp000066400000000000000000000327001462640461400176710ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define REALM_INTERNAL_ACCESS #include "realm.h" #include #include #include #include #include "common/list.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/addr.h" #include "common/util.h" #include "compat/strcasecmp.h" #include "connection.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_list * realmlist_head = NULL; static t_realm * realm_create(char const * name, char const * description, unsigned int ip, unsigned int port); static int realm_destroy(t_realm * realm); static t_realm * realm_create(char const * name, char const * description, unsigned int ip, unsigned int port) { t_realm * realm; if (!name) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL name"); return NULL; } if (!description) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL description"); return NULL; } realm = (t_realm*)xmalloc(sizeof(t_realm)); realm->name = NULL; realm->description = NULL; if (realm_set_name(realm, name) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to set name for realm"); xfree(realm); return NULL; } if (realm->description != NULL) xfree((void *)realm->description); realm->description = xstrdup(description); realm->ip = ip; realm->port = port; realm->conn = NULL; realm->active = 0; realm->player_number = 0; realm->game_number = 0; realm->sessionnum = 0; realm->tcp_sock = 0; rcm_init(&realm->rcm); eventlog(eventlog_level_info, __FUNCTION__, "created realm \"{}\"", name); return realm; } static int realm_destroy(t_realm * realm) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return -1; } if (realm->active) realm_deactive(realm); xfree((void *)realm->name); /* avoid warning */ xfree((void *)realm->description); /* avoid warning */ xfree((void *)realm); /* avoid warning */ return 0; } extern char const * realm_get_name(t_realm const * realm) { if (!realm) { return NULL; } return realm->name; } extern int realm_set_name(t_realm * realm, char const * name) { char const * temp; t_realm const * temprealm; if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return -1; } if (name && (temprealm = realmlist_find_realm(name))) { if (temprealm == realm) return 0; else { eventlog(eventlog_level_error, __FUNCTION__, "realm {} does already exist in list", name); return -1; } } if (name) temp = xstrdup(name); else temp = NULL; if (realm->name) xfree((void *)realm->name); /* avoid warning */ realm->name = temp; return 0; } extern char const * realm_get_description(t_realm const * realm) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return NULL; } return realm->description; } extern unsigned short realm_get_port(t_realm const * realm) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return 0; } return realm->port; } extern unsigned int realm_get_ip(t_realm const * realm) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return 0; } return realm->ip; } extern unsigned int realm_get_active(t_realm const * realm) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return 0; } return realm->active; } extern int realm_set_active(t_realm * realm, unsigned int active) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return -1; } realm->active = active; return 0; } extern unsigned int realm_get_player_number(t_realm const * realm) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return 0; } return realm->player_number; } extern int realm_add_player_number(t_realm * realm, int number) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return -1; } realm->player_number += number; return 0; } extern unsigned int realm_get_game_number(t_realm const * realm) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return 0; } return realm->game_number; } extern int realm_add_game_number(t_realm * realm, int number) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return -1; } realm->game_number += number; return 0; } extern int realm_active(t_realm * realm, t_connection * c) { if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return -1; } if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (realm->active) { eventlog(eventlog_level_debug, __FUNCTION__, "realm {} is already actived,destroy previous one", realm->name); realm_deactive(realm); } realm->active = 1; realm->conn = c; conn_set_realm(c, realm); realm->sessionnum = conn_get_sessionnum(c); realm->tcp_sock = conn_get_socket(c); eventlog(eventlog_level_info, __FUNCTION__, "realm {} actived", realm->name); return 0; } extern int realm_deactive(t_realm * realm) { t_connection * c; if (!realm) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realm"); return -1; } if (!realm->active) { eventlog(eventlog_level_error, __FUNCTION__, "realm {} is not actived", realm->name); return -1; } if ((c = realm_get_conn(realm))) conn_set_state(c, conn_state_destroy); realm->active = 0; realm->sessionnum = 0; realm->tcp_sock = 0; /* realm->player_number=0; realm->game_number=0; */ eventlog(eventlog_level_info, __FUNCTION__, "realm {} deactived", realm->name); return 0; } t_list * realmlist_load(char const * filename) { std::FILE * fp; unsigned int line; unsigned int pos; unsigned int len; t_addr * raddr; char * temp, *temp2; char * buff; char * name; char * desc; t_realm * realm; t_list * list_head = NULL; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return NULL; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open realm file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return NULL; } list_head = list_create(); for (line = 1; (buff = file_get_line(fp)); line++) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if ((temp = std::strrchr(buff, '#'))) { unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } /* skip any separators */ for (temp = buff; *temp && (*temp == ' ' || *temp == '\t'); temp++); if (*temp != '"') { eventlog(eventlog_level_error, __FUNCTION__, "malformed line {} in file \"{}\" (no realmname)", line, filename); continue; } temp2 = temp + 1; /* find the next " */ for (temp = temp2; *temp && *temp != '"'; temp++); if (*temp != '"' || temp == temp2) { eventlog(eventlog_level_error, __FUNCTION__, "malformed line {} in file \"{}\" (no realmname)", line, filename); continue; } /* save the realmname */ *temp = '\0'; name = xstrdup(temp2); /* eventlog(eventlog_level_trace, __FUNCTION__,"found realmname: {}",name); */ /* skip any separators */ for (temp = temp + 1; *temp && (*temp == '\t' || *temp == ' '); temp++); if (*temp == '"') { /* we have realm description */ temp2 = temp + 1; /* find the next " */ for (temp = temp2; *temp && *temp != '"'; temp++); if (*temp != '"' || temp == temp2) { eventlog(eventlog_level_error, __FUNCTION__, "malformed line {} in file \"{}\" (no valid description)", line, filename); xfree(name); continue; } /* save the description */ *temp = '\0'; desc = xstrdup(temp2); /* eventlog(eventlog_level_trace, __FUNCTION__,"found realm desc: {}",desc); */ /* skip any separators */ for (temp = temp + 1; *temp && (*temp == ' ' || *temp == '\t'); temp++); } else desc = xstrdup("\0"); temp2 = temp; /* find out where address ends */ for (temp = temp2 + 1; *temp && *temp != ' ' && *temp != '\t'; temp++); if (*temp) *temp++ = '\0'; /* if is not the end of the file, end addr and move forward */ /* eventlog(eventlog_level_trace, __FUNCTION__,"found realm ip: {}",temp2); */ if (!(raddr = addr_create_str(temp2, 0, BNETD_REALM_PORT))) /* 0 means "this computer" */ { eventlog(eventlog_level_error, __FUNCTION__, "invalid address value for field 3 on line {} in file \"{}\"", line, filename); xfree(name); xfree(desc); continue; } if (!(realm = realm_create(name, desc, addr_get_ip(raddr), addr_get_port(raddr)))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create realm"); addr_destroy(raddr); xfree(name); xfree(desc); continue; } addr_destroy(raddr); xfree(name); xfree(desc); list_prepend_data(list_head, realm); } file_get_line(NULL); // clear file_get_line buffer if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close realm file \"{}\" after reading (std::fclose: {})", filename, std::strerror(errno)); return list_head; } extern int realmlist_reload(char const * filename) { t_elem * new_curr; t_elem * old_curr; t_realm * new_realm; t_realm * old_realm; int match; t_list * newlist = NULL; t_list * oldlist = realmlist_head; realmlist_head = NULL; if (!(newlist = realmlist_load(filename))) return -1; LIST_TRAVERSE(oldlist, old_curr) { if (!(old_realm = (t_realm*)elem_get_data(old_curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL elem in list"); continue; } match = 0; LIST_TRAVERSE(newlist, new_curr) { if (!(new_realm = (t_realm*)elem_get_data(new_curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL elem in list"); continue; } if (!std::strcmp(old_realm->name, new_realm->name)) { match = 1; rcm_chref(&old_realm->rcm, new_realm); break; } } if (!match) rcm_chref(&old_realm->rcm, NULL); realm_destroy(old_realm); list_remove_elem(oldlist, &old_curr); } list_destroy(oldlist); realmlist_head = newlist; return 0; } extern int realmlist_create(char const * filename) { if (!(realmlist_head = realmlist_load(filename))) return -1; return 0; } extern int realmlist_unload(t_list * list_head) { t_elem * curr; t_realm * realm; if (list_head) { LIST_TRAVERSE(list_head, curr) { if (!(realm = (t_realm*)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL realm in list"); else realm_destroy(realm); list_remove_elem(list_head, &curr); } list_destroy(list_head); } return 0; } extern int realmlist_destroy() { int res; res = realmlist_unload(realmlist_head); realmlist_head = NULL; return res; } extern t_list * realmlist(void) { return realmlist_head; } extern t_realm * realmlist_find_realm(char const * realmname) { t_elem const * curr; t_realm * realm; if (!realmname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL realmname"); return NULL; } LIST_TRAVERSE_CONST(realmlist_head, curr) { realm = (t_realm*)elem_get_data(curr); if (strcasecmp(realm->name, realmname) == 0) return realm; } return NULL; } extern t_realm * realmlist_find_realm_by_ip(unsigned long ip) { t_elem const * curr; t_realm * realm; LIST_TRAVERSE_CONST(realmlist_head, curr) { realm = (t_realm*)elem_get_data(curr); if (realm->ip == ip) return realm; } return NULL; } extern t_connection * realm_get_conn(t_realm * realm) { assert(realm); return realm->conn; } extern t_realm * realm_get(t_realm * realm, t_rcm_regref * regref) { rcm_get(&realm->rcm, regref); return realm; } extern void realm_put(t_realm * realm, t_rcm_regref * regref) { rcm_put(&realm->rcm, regref); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/realm.h000066400000000000000000000060371462640461400173420ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_REALM_TYPES #define INCLUDED_REALM_TYPES #ifdef JUST_NEED_TYPES # include "connection.h" # include "common/rcm.h" #else #define JUST_NEED_TYPES # include "connection.h" # include "common/rcm.h" #undef JUST_NEED_TYPES #endif namespace pvpgn { namespace bnetd { struct connection; typedef struct realm #ifdef REALM_INTERNAL_ACCESS { char const * name; char const * description; unsigned int sessionnum; unsigned int active; unsigned int ip; unsigned short port; unsigned int player_number; unsigned int game_number; int tcp_sock; struct connection * conn; t_rcm rcm; } #endif t_realm; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_REALM_PROTOS #define INCLUDED_REALM_PROTOS #define JUST_NEED_TYPES # include "common/list.h" # include "connection.h" # include "common/rcm.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern char const * realm_get_name(t_realm const * realm); extern char const * realm_get_description(t_realm const * realm); extern unsigned short realm_get_port(t_realm const * realm); extern unsigned int realm_get_ip(t_realm const * realm); extern int realm_set_name(t_realm * realm, char const * name); extern unsigned int realm_get_active(t_realm const * realm); extern unsigned int realm_get_player_number(t_realm const * realm); extern int realm_add_player_number(t_realm * realm, int number); extern unsigned int realm_get_game_number(t_realm const * realm); extern int realm_add_game_number(t_realm * realm, int number); extern int realm_set_active(t_realm * realm, unsigned int active); extern int realm_active(t_realm * realm, struct connection * c); extern int realm_deactive(t_realm * realm); extern int realmlist_create(char const * filename); extern int realmlist_destroy(void); extern int realmlist_reload(char const * filename); extern t_realm * realmlist_find_realm(char const * realmname); extern t_realm * realmlist_find_realm_by_ip(unsigned long ip); /* ??? */ extern t_list * realmlist(void); extern struct connection * realm_get_conn(t_realm * realm); extern t_realm * realm_get(t_realm * realm, t_rcm_regref * regref); extern void realm_put(t_realm * realm, t_rcm_regref * regref); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/runprog.cpp000066400000000000000000000072131462640461400202660ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "runprog.h" #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_WAIT_H # include #endif #include "compat/stdfileno.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { #ifdef DO_SUBPROC static pid_t currpid = 0; #endif extern std::FILE * runprog_open(char const * command) { #ifndef DO_SUBPROC return NULL; /* always fail */ #else int fds[2]; std::FILE * pp; if (!command) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL command"); return NULL; } if (pipe(fds) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create pipe (pipe: {})", std::strerror(errno)); return NULL; } switch ((currpid = fork())) { case 0: close(fds[0]); close(STDINFD); close(STDOUTFD); close(STDERRFD); /* FIXME: we should close all other fds to make sure the program doesn't use them. For now, leave it alone because we would either have to keep track of them all or do a for for (fd=0; fd namespace pvpgn { namespace bnetd { extern std::FILE * runprog_open(char const * command); extern int runprog_close(std::FILE * pp); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/server.cpp000066400000000000000000001455351462640461400201120ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define SERVER_INTERNAL_ACCESS #include "server.h" #include #include #include #include #ifdef DO_POSIXSIG # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include "compat/strerror.h" #include "compat/psock.h" #include "common/fdwatch.h" #include "common/addr.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/packet.h" #include "common/hexdump.h" #include "common/network.h" #include "common/list.h" #include "common/trans.h" #ifdef WIN32 # include "win32/service.h" # include "windows.h" #endif #include "common/util.h" #include "prefs.h" #include "connection.h" #include "ipban.h" #include "timer.h" #include "handle_bnet.h" #include "handle_bot.h" #include "handle_telnet.h" #include "handle_file.h" #include "handle_init.h" #include "handle_d2cs.h" #include "handle_irc_common.h" #include "handle_udp.h" #include "handle_apireg.h" #include "handle_wol_gameres.h" #include "anongame.h" #include "clan.h" #include "attrlayer.h" #include "account.h" #include "message.h" #include "game.h" #include "cmdline.h" #include "tracker.h" #include "ladder.h" #include "output.h" #include "channel.h" #include "realm.h" #include "autoupdate.h" #include "news.h" #include "versioncheck.h" #include "helpfile.h" #include "adbanner.h" #include "command_groups.h" #include "alias_command.h" #include "tournament.h" #include "icons.h" #include "anongame_infos.h" #include "topic.h" #include "i18n.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" #ifdef WITH_LUA #include "luainterface.h" #endif extern std::FILE * hexstrm; /* from main.c */ extern int g_ServiceStatus; namespace pvpgn { namespace bnetd { #ifdef DO_POSIXSIG static void quit_sig_handle(int unused); static void restart_sig_handle(int unused); static void save_sig_handle(int unused); #ifdef HAVE_SETITIMER static void timer_sig_handle(int unused); #endif #endif time_t now; static std::time_t starttime; static std::time_t curr_exittime; static volatile std::time_t sigexittime = 0; static volatile int do_restart = 0; static volatile int do_save = 0; static volatile int got_epipe = 0; static char const * server_hostname = NULL; extern void server_quit_delay(int delay) { /* getting negative delay is ok too because it will force immediate * shutdown */ if (delay) sigexittime = std::time(NULL) + delay; else sigexittime = 0; } extern void server_quit_wraper(void) { if (sigexittime) sigexittime -= prefs_get_shutdown_decr(); else sigexittime = std::time(NULL) + (std::time_t)prefs_get_shutdown_delay(); } extern void server_restart_wraper(int mode){ do_restart = mode; } extern void server_save_wraper(void){ do_save = 1; } #ifdef DO_POSIXSIG static void quit_sig_handle(int unused) { server_quit_wraper(); } static void restart_sig_handle(int unused) { do_restart = 1; } static void save_sig_handle(int unused) { do_save = 1; } static void pipe_sig_handle(int unused) { got_epipe = 1; } #ifdef HAVE_SETITIMER static void timer_sig_handle(int unused) { std::time(&now); } #endif static void forced_quit_sig_handle(int unused) { server_quit_delay(-1); /* programs shutdown 1 second before now */ } #endif extern unsigned int server_get_uptime(void) { return (unsigned int)std::difftime(std::time(NULL), starttime); } extern unsigned int server_get_starttime(void) { return (unsigned int)starttime; } static char const * laddr_type_get_str(t_laddr_type laddr_type) { switch (laddr_type) { case laddr_type_bnet: return "bnet"; case laddr_type_w3route: return "w3route"; case laddr_type_irc: return "irc"; case laddr_type_wolv1: return "wolv1"; case laddr_type_wolv2: return "wolv2"; case laddr_type_apireg: return "apireg"; case laddr_type_wgameres: return "wgameres"; case laddr_type_telnet: return "telnet"; default: return "UNKNOWN"; } } static int handle_accept(void *data, t_fdwatch_type rw); static int handle_tcp(void *data, t_fdwatch_type rw); static int handle_udp(void *data, t_fdwatch_type rw); static int sd_accept(t_addr const * curr_laddr, t_laddr_info const * laddr_info, int ssocket, int usocket) { char tempa[32]; int csocket; struct sockaddr_in caddr; psock_t_socklen caddr_len; unsigned int raddr; unsigned short rport; if (!addr_get_addr_str(curr_laddr, tempa, sizeof(tempa))) std::strcpy(tempa, "x.x.x.x:x"); /* accept the connection */ std::memset(&caddr, 0, sizeof(caddr)); /* not sure if this is needed... modern systems are ok anyway */ caddr_len = sizeof(caddr); if ((csocket = psock_accept(ssocket, (struct sockaddr *)&caddr, &caddr_len)) < 0) { /* BSD, POSIX error for aborted connections, SYSV often uses EAGAIN or EPROTO */ if ( #ifdef PSOCK_EWOULDBLOCK psock_errno() == PSOCK_EWOULDBLOCK || #endif #ifdef PSOCK_ECONNABORTED psock_errno() == PSOCK_ECONNABORTED || #endif #ifdef PSOCK_EPROTO psock_errno() == PSOCK_EPROTO || #endif 0) eventlog(eventlog_level_error, __FUNCTION__, "client aborted connection on {} (psock_accept: {})", tempa, pstrerror(psock_errno())); else /* EAGAIN can mean out of resources _or_ connection aborted :( */ if ( #ifdef PSOCK_EINTR psock_errno() != PSOCK_EINTR && #endif 1) eventlog(eventlog_level_error, __FUNCTION__, "could not accept new connection on {} (psock_accept: {})", tempa, pstrerror(psock_errno())); return -1; } /* dont accept new connections while shutting down */ if (curr_exittime) { psock_shutdown(csocket, PSOCK_SHUT_RDWR); psock_close(csocket); return 0; } char addrstr[INET_ADDRSTRLEN] = { 0 }; if (ipbanlist_check(inet_ntop(AF_INET, &(caddr.sin_addr), addrstr, sizeof(addrstr))) != 0) { eventlog(eventlog_level_info, __FUNCTION__, "[{}] connection from banned address {} denied (closing connection)", csocket, addrstr); psock_close(csocket); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "[{}] accepted connection from {} on {}", csocket, addr_num_to_addr_str(ntohl(caddr.sin_addr.s_addr), ntohs(caddr.sin_port)), tempa); if (prefs_get_use_keepalive()) { int val = 1; if (psock_setsockopt(csocket, PSOCK_SOL_SOCKET, PSOCK_SO_KEEPALIVE, &val, (psock_t_socklen)sizeof(val)) < 0) eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not set socket option SO_KEEPALIVE (psock_setsockopt: {})", csocket, pstrerror(psock_errno())); /* not a fatal error */ } { struct sockaddr_in rsaddr; psock_t_socklen rlen; std::memset(&rsaddr, 0, sizeof(rsaddr)); /* not sure if this is needed... modern systems are ok anyway */ rlen = sizeof(rsaddr); if (psock_getsockname(csocket, (struct sockaddr *)&rsaddr, &rlen) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] unable to determine real local port (psock_getsockname: {})", csocket, pstrerror(psock_errno())); /* not a fatal error */ raddr = addr_get_ip(curr_laddr); rport = addr_get_port(curr_laddr); } else { if (rsaddr.sin_family != PSOCK_AF_INET) { eventlog(eventlog_level_error, __FUNCTION__, "local address returned with bad address family {}", (int)rsaddr.sin_family); /* not a fatal error */ raddr = addr_get_ip(curr_laddr); rport = addr_get_port(curr_laddr); } else { raddr = ntohl(rsaddr.sin_addr.s_addr); rport = ntohs(rsaddr.sin_port); } } } if (psock_ctl(csocket, PSOCK_NONBLOCK) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not set TCP socket to non-blocking mode (closing connection) (psock_ctl: {})", csocket, pstrerror(psock_errno())); psock_close(csocket); return -1; } { t_connection * c; if (!(c = conn_create(csocket, usocket, raddr, rport, addr_get_ip(curr_laddr), addr_get_port(curr_laddr), ntohl(caddr.sin_addr.s_addr), ntohs(caddr.sin_port)))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] unable to create new connection (closing connection)", csocket); psock_close(csocket); return -1; } if (conn_add_fdwatch(c, handle_tcp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] unable to add socket to fdwatch pool (max connections?)", csocket); conn_set_state(c, conn_state_destroy); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] client connected to a {} listening address", csocket, laddr_type_get_str(laddr_info->type)); switch (laddr_info->type) { case laddr_type_irc: case laddr_type_wolv1: case laddr_type_wolv2: conn_set_class(c, conn_class_ircinit); conn_set_state(c, conn_state_connected); break; case laddr_type_apireg: conn_set_class(c, conn_class_apireg); conn_set_state(c, conn_state_connected); break; case laddr_type_wgameres: conn_set_class(c, conn_class_wgameres); conn_set_state(c, conn_state_connected); break; case laddr_type_w3route: conn_set_class(c, conn_class_w3route); conn_set_state(c, conn_state_connected); break; case laddr_type_telnet: conn_set_class(c, conn_class_telnet); conn_set_state(c, conn_state_connected); break; case laddr_type_bnet: { /* add a timer to close stale connections */ int delay; t_timer_data data; data.p = NULL; delay = prefs_get_initkill_timer(); if (delay) timerlist_add_timer(c, std::time(NULL) + delay, conn_shutdown, data); } default: /* We have to wait for an initial "magic" byte on bnet connections to * tell us exactly what connection class we are dealing with. */ break; } } return 0; } static int sd_udpinput(t_addr * const curr_laddr, t_laddr_info const * laddr_info, int ssocket, int usocket) { int err; psock_t_socklen errlen; t_packet * upacket; err = 0; errlen = sizeof(err); if (psock_getsockopt(usocket, PSOCK_SOL_SOCKET, PSOCK_SO_ERROR, &err, &errlen) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] unable to read socket error (psock_getsockopt: {})", usocket, pstrerror(psock_errno())); return -1; } if (errlen && err) /* if it was an error, there is no packet to read */ { eventlog(eventlog_level_error, __FUNCTION__, "[{}] async UDP socket error notification (psock_getsockopt: {})", usocket, pstrerror(err)); return -1; } if (!(upacket = packet_create(packet_class_udp))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate raw packet for input"); return -1; } { struct sockaddr_in fromaddr; psock_t_socklen fromlen; int len; fromlen = sizeof(fromaddr); if ((len = psock_recvfrom(usocket, packet_get_raw_data_build(upacket, 0), MAX_PACKET_SIZE, 0, (struct sockaddr *)&fromaddr, &fromlen)) < 0) { if ( #ifdef PSOCK_EINTR psock_errno() != PSOCK_EINTR && #endif #ifdef PSOCK_EAGAIN psock_errno() != PSOCK_EAGAIN && #endif #ifdef PSOCK_EWOULDBLOCK psock_errno() != PSOCK_EWOULDBLOCK && #endif #ifdef PSOCK_ECONNRESET psock_errno() != PSOCK_ECONNRESET && /* this is a win2k/winxp issue * their socket implementation returns this value * although it shouldn't */ #endif 1) eventlog(eventlog_level_error, __FUNCTION__, "could not recv UDP datagram (psock_recvfrom: {})", pstrerror(psock_errno())); packet_del_ref(upacket); return -1; } if (fromaddr.sin_family != PSOCK_AF_INET) { eventlog(eventlog_level_error, __FUNCTION__, "got UDP datagram with bad address family {}", (int)fromaddr.sin_family); packet_del_ref(upacket); return -1; } packet_set_size(upacket, len); if (hexstrm) { char tempa[32]; if (!addr_get_addr_str(curr_laddr, tempa, sizeof(tempa))) std::strcpy(tempa, "x.x.x.x:x"); std::fprintf(hexstrm, "%d: recv class=%s[0x%02x] type=%s[0x%04x] from=%s to=%s length=%u\n", usocket, packet_get_class_str(upacket), (unsigned int)packet_get_class(upacket), packet_get_type_str(upacket, packet_dir_from_client), packet_get_type(upacket), addr_num_to_addr_str(ntohl(fromaddr.sin_addr.s_addr), ntohs(fromaddr.sin_port)), tempa, packet_get_size(upacket)); hexdump(hexstrm, packet_get_raw_data(upacket, 0), packet_get_size(upacket)); } handle_udp_packet(usocket, ntohl(fromaddr.sin_addr.s_addr), ntohs(fromaddr.sin_port), upacket); packet_del_ref(upacket); } return 0; } static int sd_tcpinput(t_connection * c) { unsigned int currsize; t_packet * packet; int csocket = conn_get_socket(c); bool skip; currsize = conn_get_in_size(c); if (!conn_get_in_queue(c)) { switch (conn_get_class(c)) { case conn_class_init: if (!(packet = packet_create(packet_class_init))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate init packet for input"); return -1; } break; case conn_class_d2cs_bnetd: if (!(packet = packet_create(packet_class_d2cs_bnetd))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate d2cs_bnetd packet"); return -1; } break; case conn_class_bnet: if (!(packet = packet_create(packet_class_bnet))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate bnet packet for input"); return -1; } break; case conn_class_file: switch (conn_get_state(c)) { case conn_state_pending_raw: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate raw packet for input"); return -1; } packet_set_size(packet, sizeof(t_client_file_req3)); break; default: if (!(packet = packet_create(packet_class_file))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate file packet for input"); return -1; } break; } break; case conn_class_bot: case conn_class_ircinit: case conn_class_irc: case conn_class_wol: case conn_class_wserv: case conn_class_apireg: case conn_class_wladder: case conn_class_telnet: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate raw packet for input"); return -1; } packet_set_size(packet, 1); /* start by only reading one char */ break; case conn_class_w3route: if (!(packet = packet_create(packet_class_w3route))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate w3route packet for input"); return -1; } break; case conn_class_wgameres: if (!(packet = packet_create(packet_class_wolgameres))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate wolgameres packet for input"); return -1; } break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] connection has bad class (closing connection)", conn_get_socket(c)); conn_close_read(c); return -2; } conn_put_in_queue(c, packet); currsize = 0; } packet = conn_get_in_queue(c); switch (net_recv_packet(csocket, packet, &currsize)) { case -1: eventlog(eventlog_level_debug, __FUNCTION__, "[{}] read returned -1 (closing connection)", conn_get_socket(c)); conn_close_read(c); return -2; case 0: /* still working on it */ /* eventlog(eventlog_level_debug,__FUNCTION__,"[{}] still reading \"{}\" packet ({} of {} bytes so far)",conn_get_socket(c),packet_get_class_str(packet),conn_get_in_size(c),packet_get_size(packet)); */ conn_set_in_size(c, currsize); break; case 1: /* done reading */ switch (conn_get_class(c)) { case conn_class_bot: case conn_class_telnet: if (currsize < MAX_PACKET_SIZE) /* if we overflow, we can't wait for the end of the line. handle_*_packet() should take care of it */ { char const * const temp = (char const * const)packet_get_raw_data_const(packet, 0); if ((temp[currsize - 1] == '\003') || (temp[currsize - 1] == '\004')) { /* we have to ignore these special characters, since * some bots even send them after login (eg. UltimateBot) */ currsize--; skip = true; break; } if (temp[currsize - 1] != '\r' && temp[currsize - 1] != '\n') { conn_set_in_size(c, currsize); packet_set_size(packet, currsize + 1); skip = true; break; /* no end of line, get another char */ } } skip = false; break; case conn_class_ircinit: case conn_class_irc: case conn_class_wol: case conn_class_wserv: case conn_class_wladder: if (currsize < MAX_PACKET_SIZE) /* if we overflow, we can't wait for the end of the line. handle_*_packet() should take care of it */ { char const * const temp = (char const * const)packet_get_raw_data_const(packet, 0); if ((temp[currsize - 1] == '\r') || (temp[currsize - 1] == '\0')) { /* kindly ignore \r and NUL ... */ currsize--; skip = true; break; } if (temp[currsize - 1] != '\n') { conn_set_in_size(c, currsize); packet_set_size(packet, currsize + 1); skip = true; break; /* no end of line, get another char */ } } skip = false; break; default: skip = false; } if (!skip) { conn_put_in_queue(c, NULL); if (hexstrm) { std::fprintf(hexstrm, "%d: recv class=%s[0x%02x] type=%s[0x%04x] length=%u\n", csocket, packet_get_class_str(packet), (unsigned int)packet_get_class(packet), packet_get_type_str(packet, packet_dir_from_client), packet_get_type(packet), packet_get_size(packet)); hexdump(hexstrm, packet_get_raw_data_const(packet, 0), packet_get_size(packet)); } if (conn_get_class(c) == conn_class_bot || conn_get_class(c) == conn_class_telnet) /* NUL terminate the line to make life easier */ { char * const temp = (char*)packet_get_raw_data(packet, 0); if (temp[currsize - 1] == '\r' || temp[currsize - 1] == '\n') temp[currsize - 1] = '\0'; /* have to do it here instead of above so everything is intact for the hexdump */ } { int ret; switch (conn_get_class(c)) { case conn_class_init: ret = handle_init_packet(c, packet); break; case conn_class_bnet: ret = handle_bnet_packet(c, packet); break; case conn_class_d2cs_bnetd: ret = handle_d2cs_packet(c, packet); break; case conn_class_bot: ret = handle_bot_packet(c, packet); break; case conn_class_telnet: ret = handle_telnet_packet(c, packet); break; case conn_class_file: ret = handle_file_packet(c, packet); break; case conn_class_ircinit: case conn_class_irc: case conn_class_wol: case conn_class_wserv: case conn_class_wladder: ret = handle_irc_common_packet(c, packet); break; case conn_class_apireg: ret = handle_apireg_packet(c, packet); break; case conn_class_w3route: ret = handle_w3route_packet(c, packet); break; case conn_class_wgameres: ret = handle_wol_gameres_packet(c, packet); break; default: eventlog(eventlog_level_error, __FUNCTION__, "[{}] bad packet class {} (closing connection)", conn_get_socket(c), (int)packet_get_class(packet)); ret = -1; } packet_del_ref(packet); if (ret < 0) { conn_close_read(c); return -2; } } conn_set_in_size(c, 0); } } return 0; } static int sd_tcpoutput(t_connection * c) { unsigned int currsize; unsigned int totsize; t_packet * packet; int csocket = conn_get_socket(c); totsize = 0; for (;;) { currsize = conn_get_out_size(c); if ((packet = conn_peek_outqueue(c)) == NULL) return -2; switch (net_send_packet(csocket, packet, &currsize)) /* avoid warning */ { case -1: /* marking connection as "destroyed", memory will be freed later */ conn_clear_outqueue(c); conn_set_state(c, conn_state_destroy); return -2; case 0: /* still working on it */ conn_set_out_size(c, currsize); return 0; /* bail out */ case 1: /* done sending */ if (hexstrm) { std::fprintf(hexstrm, "%d: send class=%s[0x%02x] type=%s[0x%04x] length=%u\n", csocket, packet_get_class_str(packet), (unsigned int)packet_get_class(packet), packet_get_type_str(packet, packet_dir_from_server), packet_get_type(packet), packet_get_size(packet)); hexdump(hexstrm, packet_get_raw_data(packet, 0), packet_get_size(packet)); } packet = conn_pull_outqueue(c); packet_del_ref(packet); conn_set_out_size(c, 0); /* stop at about BNETD_MAX_OUTBURST (or until out of packets or EWOULDBLOCK) */ if (totsize > BNETD_MAX_OUTBURST || !conn_peek_outqueue(c)) return 0; totsize += currsize; break; } } /* not reached */ } void server_check_and_fix_hostname(char const * sname) { int ok = 1; char * tn = (char *)sname; char * sn = (char *)sname; if (!std::isalnum((int)*sn)) { eventlog(eventlog_level_error, __FUNCTION__, "hostname contains invalid first symbol (must be alphanumeric)"); *tn = 'a'; } tn++; sn++; for (; *sn != '\0'; sn++) { if (std::isalnum((int)*sn) || *sn == '-' || *sn == '.') { *tn = *sn; tn++; } else ok = 0; } *tn = '\0'; if (!ok) eventlog(eventlog_level_error, __FUNCTION__, "hostname contains invalid symbol(s) (must be alphanumeric, '-' or '.') - skipped those symbols"); } extern void server_set_hostname(void) { char temp[250]; char const * hn; #ifdef HAVE_GETHOSTBYNAME struct hostent * hp; #endif if (server_hostname) { xfree((void *)server_hostname); /* avoid warning */ server_hostname = NULL; } hn = prefs_get_hostname(); if ((!hn) || (hn[0] == '\0')) { if (gethostname(temp, sizeof(temp)) < 0) { #ifdef WIN32 std::sprintf(temp, "localhost"); #else eventlog(eventlog_level_error, __FUNCTION__, "could not get hostname: {}", pstrerror(errno)); return; #endif } #ifdef HAVE_GETHOSTBYNAME hp = gethostbyname(temp); if (!hp || !hp->h_name) { #endif server_hostname = xstrdup(temp); #ifdef HAVE_GETHOSTBYNAME } else { int i = 0; if (std::strchr(hp->h_name, '.')) /* Default name is already a FQDN */ server_hostname = xstrdup(hp->h_name); /* ... if not we have to examine the aliases */ while (!server_hostname && hp->h_aliases && hp->h_aliases[i]) { if (std::strchr(hp->h_aliases[i], '.')) server_hostname = xstrdup(hp->h_aliases[i]); i++; } if (!server_hostname) /* Fall back to default name which might not be a FQDN */ server_hostname = xstrdup(hp->h_name); } #endif } else { server_hostname = xstrdup(hn); } server_check_and_fix_hostname(server_hostname); eventlog(eventlog_level_info, __FUNCTION__, "set hostname to \"{}\"", server_hostname); } extern char const * server_get_hostname(void) { if (!server_hostname) return "(null)"; /* avoid crashes */ else return server_hostname; } extern void server_clear_hostname(void) { if (server_hostname) { xfree((void *)server_hostname); /* avoid warning */ server_hostname = NULL; } } static int handle_accept(void *data, t_fdwatch_type rw) { t_laddr_info *laddr_info = (t_laddr_info*)addr_get_data((t_addr *)data).p; return sd_accept((t_addr *)data, laddr_info, laddr_info->ssocket, laddr_info->usocket); } static int handle_udp(void *data, t_fdwatch_type rw) { t_laddr_info *laddr_info = (t_laddr_info*)addr_get_data((t_addr *)data).p; return sd_udpinput((t_addr *)data, laddr_info, laddr_info->ssocket, laddr_info->usocket); } static int handle_tcp(void *data, t_fdwatch_type rw) { switch (rw) { case fdwatch_type_read: return sd_tcpinput((t_connection *)data); case fdwatch_type_write: return sd_tcpoutput((t_connection *)data); default: return -1; } } static int _setup_add_addrs(t_addrlist **pladdrs, const char *str, unsigned int defaddr, unsigned short defport, t_laddr_type type) { t_addr * curr_laddr; t_addr_data laddr_data; t_laddr_info * laddr_info; t_elem const * acurr; if (*pladdrs == NULL) { *pladdrs = addrlist_create(str, defaddr, defport); if (*pladdrs == NULL) return -1; } else if (addrlist_append(*pladdrs, str, defaddr, defport)) return -1; /* Mark all these laddrs for being classic Battle.net service */ LIST_TRAVERSE_CONST(*pladdrs, acurr) { curr_laddr = (t_addr*)elem_get_data(acurr); if (addr_get_data(curr_laddr).p) continue; laddr_info = (t_laddr_info*)xmalloc(sizeof(t_laddr_info)); laddr_info->usocket = -1; laddr_info->ssocket = -1; laddr_info->type = type; laddr_data.p = laddr_info; if (addr_set_data(curr_laddr, laddr_data) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not set address data"); if (laddr_info->usocket != -1) { psock_close(laddr_info->usocket); laddr_info->usocket = -1; } if (laddr_info->ssocket != -1) { psock_close(laddr_info->ssocket); laddr_info->ssocket = -1; } return -1; } } return 0; } static int _set_reuseaddr(int sock) { int val = 1; return psock_setsockopt(sock, PSOCK_SOL_SOCKET, PSOCK_SO_REUSEADDR, &val, (psock_t_socklen)sizeof(int)); } static int _bind_socket(int sock, unsigned addr, short port) { struct sockaddr_in saddr; std::memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = PSOCK_AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = htonl(addr); return psock_bind(sock, (struct sockaddr *)&saddr, (psock_t_socklen)sizeof(saddr)); } static int _setup_listensock(t_addrlist *laddrs) { t_addr * curr_laddr; t_laddr_info * laddr_info; t_elem const * acurr; char tempa[32]; int fidx; LIST_TRAVERSE_CONST(laddrs, acurr) { curr_laddr = (t_addr*)elem_get_data(acurr); if (!(laddr_info = (t_laddr_info*)addr_get_data(curr_laddr).p)) { eventlog(eventlog_level_error, __FUNCTION__, "NULL address info"); goto err; } if (!addr_get_addr_str(curr_laddr, tempa, sizeof(tempa))) std::strcpy(tempa, "x.x.x.x:x"); laddr_info->ssocket = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP); if (laddr_info->ssocket < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create a {} listening socket (psock_socket: {})", laddr_type_get_str(laddr_info->type), pstrerror(psock_errno())); goto err; } if (_set_reuseaddr(laddr_info->ssocket) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set option SO_REUSEADDR on {} socket {} (psock_setsockopt: {})", laddr_type_get_str(laddr_info->type), laddr_info->ssocket, pstrerror(psock_errno())); /* not a fatal error... */ if (_bind_socket(laddr_info->ssocket, addr_get_ip(curr_laddr), addr_get_port(curr_laddr)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not bind {} socket to address {} TCP (psock_bind: {})", laddr_type_get_str(laddr_info->type), tempa, pstrerror(psock_errno())); goto errsock; } /* tell socket to listen for connections */ if (psock_listen(laddr_info->ssocket, LISTEN_QUEUE) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not set {} socket {} to listen (psock_listen: {})", laddr_type_get_str(laddr_info->type), laddr_info->ssocket, pstrerror(psock_errno())); goto errsock; } if (psock_ctl(laddr_info->ssocket, PSOCK_NONBLOCK) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set {} TCP listen socket to non-blocking mode (psock_ctl: {})", laddr_type_get_str(laddr_info->type), pstrerror(psock_errno())); /* index not stored persisently because we dont need to refer to it later */ fidx = fdwatch_add_fd(laddr_info->ssocket, fdwatch_type_read, handle_accept, curr_laddr); if (fidx < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add listening socket {} to fdwatch pool (max sockets?)", laddr_info->ssocket); goto errsock; } eventlog(eventlog_level_info, __FUNCTION__, "listening for {} connections on {} TCP", laddr_type_get_str(laddr_info->type), tempa); if (laddr_info->type == laddr_type_bnet) { laddr_info->usocket = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_DGRAM, PSOCK_IPPROTO_UDP); if (laddr_info->usocket < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create UDP socket (psock_socket: {})", pstrerror(psock_errno())); goto errfdw; } if (_set_reuseaddr(laddr_info->usocket) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set option SO_REUSEADDR on {} socket {} (psock_setsockopt: {})", laddr_type_get_str(laddr_info->type), laddr_info->usocket, pstrerror(psock_errno())); /* not a fatal error... */ if (_bind_socket(laddr_info->usocket, addr_get_ip(curr_laddr), addr_get_port(curr_laddr)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not bind {} socket to address {} UDP (psock_bind: {})", laddr_type_get_str(laddr_info->type), tempa, pstrerror(psock_errno())); goto errusock; } if (psock_ctl(laddr_info->usocket, PSOCK_NONBLOCK) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set {} UDP socket to non-blocking mode (psock_ctl: {})", laddr_type_get_str(laddr_info->type), pstrerror(psock_errno())); /* index ignored because we never need it after this */ if (fdwatch_add_fd(laddr_info->usocket, fdwatch_type_read, handle_udp, curr_laddr) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add listening socket {} to fdwatch pool (max sockets?)", laddr_info->usocket); goto errusock; } } } return 0; errusock: psock_close(laddr_info->usocket); laddr_info->usocket = -1; errfdw: fdwatch_del_fd(fidx); errsock: psock_close(laddr_info->ssocket); laddr_info->ssocket = -1; err: return -1; } #ifdef DO_POSIXSIG static sigset_t block_set; static sigset_t save_set; static int _setup_posixsig(void) { if (sigemptyset(&save_set) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); return -1; } if (sigemptyset(&block_set) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); return -1; } if (sigaddset(&block_set, SIGINT) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add std::signal to set (sigemptyset: {})", pstrerror(errno)); return -1; } if (sigaddset(&block_set, SIGHUP) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add std::signal to set (sigemptyset: {})", pstrerror(errno)); return -1; } if (sigaddset(&block_set, SIGTERM) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add std::signal to set (sigemptyset: {})", pstrerror(errno)); return -1; } if (sigaddset(&block_set, SIGUSR1) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add std::signal to set (sigemptyset: {})", pstrerror(errno)); return -1; } if (sigaddset(&block_set, SIGUSR2) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add std::signal to set (sigemptyset: {})", pstrerror(errno)); return -1; } if (sigaddset(&block_set, SIGALRM) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not add std::signal to set (sigemptyset: {})", pstrerror(errno)); return -1; } { struct sigaction quit_action; struct sigaction restart_action; struct sigaction save_action; struct sigaction pipe_action; #ifdef HAVE_SETITIMER struct sigaction timer_action; #endif struct sigaction forced_quit_action; quit_action.sa_handler = quit_sig_handle; if (sigemptyset(&quit_action.sa_mask) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); quit_action.sa_flags = SA_RESTART; restart_action.sa_handler = restart_sig_handle; if (sigemptyset(&restart_action.sa_mask) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); restart_action.sa_flags = SA_RESTART; save_action.sa_handler = save_sig_handle; if (sigemptyset(&save_action.sa_mask) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); save_action.sa_flags = SA_RESTART; pipe_action.sa_handler = pipe_sig_handle; if (sigemptyset(&pipe_action.sa_mask) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); pipe_action.sa_flags = SA_RESTART; #ifdef HAVE_SETITIMER timer_action.sa_handler = timer_sig_handle; if (sigemptyset(&timer_action.sa_mask) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); timer_action.sa_flags = SA_RESTART; #endif /* HAVE_SETITIMER */ forced_quit_action.sa_handler = forced_quit_sig_handle; if (sigemptyset(&forced_quit_action.sa_mask) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not initialize std::signal set (sigemptyset: {})", pstrerror(errno)); forced_quit_action.sa_flags = SA_RESTART; if (sigaction(SIGINT, &quit_action, NULL) < 0) /* control-c */ eventlog(eventlog_level_error, __FUNCTION__, "could not set SIGINT std::signal handler (sigaction: {})", pstrerror(errno)); if (sigaction(SIGHUP, &restart_action, NULL) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set SIGHUP std::signal handler (sigaction: {})", pstrerror(errno)); if (sigaction(SIGTERM, &quit_action, NULL) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set SIGTERM std::signal handler (sigaction: {})", pstrerror(errno)); if (sigaction(SIGUSR1, &save_action, NULL) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set SIGUSR1 std::signal handler (sigaction: {})", pstrerror(errno)); if (sigaction(SIGPIPE, &pipe_action, NULL) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set SIGPIPE std::signal handler (sigaction: {})", pstrerror(errno)); #ifdef HAVE_SETITIMER /* setup asynchronus timestamp update timer */ if (sigaction(SIGALRM, &timer_action, NULL) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set SIGALRM std::signal handler (sigaction: {})", pstrerror(errno)); { struct itimerval it; it.it_interval.tv_sec = BNETD_JIFFIES / 1000; it.it_interval.tv_usec = BNETD_JIFFIES % 1000; it.it_value.tv_sec = BNETD_JIFFIES / 1000; it.it_value.tv_usec = BNETD_JIFFIES % 1000; if (setitimer(ITIMER_REAL, &it, NULL)) { eventlog(eventlog_level_fatal, __FUNCTION__, "failed to set timers (setitimer(): {})", pstrerror(errno)); return -1; } } #endif if (sigaction(SIGQUIT, &forced_quit_action, NULL) < 0) /* immediate shutdown */ eventlog(eventlog_level_error, __FUNCTION__, "could not set SIGQUIT std::signal handler (sigaction: {})", pstrerror(errno)); } return 0; } #endif #ifdef WIN32 # ifndef WIN32_GUI BOOL CtrlHandler(DWORD fdwCtrlType) { switch (fdwCtrlType) { // Handle the CTRL-C signal. case CTRL_C_EVENT: server_quit_wraper(); return(TRUE); // CTRL-CLOSE: confirm that the user wants to exit. server_quit_wraper(); return(TRUE); // Pass other signals to the next handler. case CTRL_BREAK_EVENT: server_quit_wraper(); return FALSE; case CTRL_LOGOFF_EVENT: server_quit_wraper(); return FALSE; case CTRL_SHUTDOWN_EVENT: server_quit_wraper(); return FALSE; default: return FALSE; } } # endif #endif static std::time_t prev_exittime; static void _server_mainloop(t_addrlist *laddrs) { std::time_t next_savetime, track_time; std::time_t war3_ladder_updatetime; std::time_t output_updatetime; std::time_t prev_time = 0; starttime = std::time(NULL); track_time = starttime - prefs_get_track(); next_savetime = starttime + prefs_get_user_sync_timer(); war3_ladder_updatetime = starttime - prefs_get_war3_ladder_update_secs(); output_updatetime = starttime - prefs_get_output_update_secs(); for (;;) { #ifdef WIN32 if (g_ServiceStatus == 0) server_quit_wraper(); while (g_ServiceStatus == 2) Sleep(1000); #endif #ifdef DO_POSIXSIG if (sigprocmask(SIG_SETMASK, &save_set, NULL) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not unblock signals"); /* receive signals here */ if (sigprocmask(SIG_SETMASK, &block_set, NULL) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not block signals"); #endif if (got_epipe) { got_epipe = 0; eventlog(eventlog_level_info, __FUNCTION__, "handled SIGPIPE"); } #if !defined(DO_POSIXSIG) || !defined(HAVE_SETITIMER) /* if no DO_POSIXSIG or no HAVE_SETITIMER so we must read timestamp each loop */ std::time(&now); #endif curr_exittime = sigexittime; if (curr_exittime && (curr_exittime <= now || connlist_login_get_length() < 1)) { eventlog(eventlog_level_info, __FUNCTION__, "the server is shutting down ({} connections left)", connlist_get_length()); clanlist_save(); /* no need for accountlist_save() when using "force" */ accountlist_save(FS_FORCE | FS_ALL); accountlist_flush(FS_FORCE | FS_ALL); break; } if (prev_exittime != curr_exittime) { t_message * message; char const * tstr; if (curr_exittime != 0) { char text[29 + 256 + 2 + 32 + 24 + 1]; /* "The ... in " + std::time + " (" num + " connection ...)." + NUL */ tstr = seconds_to_timestr(curr_exittime - now); std::sprintf(text, "The server will shut down in %s (%d connections remaining).", tstr, connlist_get_length()); if ((message = message_create(message_type_error, NULL, text))) { message_send_all(message); message_destroy(message); } eventlog(eventlog_level_info, __FUNCTION__, "the server will shut down in {} ({} connections remaining)", tstr, connlist_get_length()); } else { if ((message = message_create(message_type_error, NULL, "Server shutdown has been canceled."))) { message_send_all(message); message_destroy(message); } eventlog(eventlog_level_info, __FUNCTION__, "server shutdown canceled"); } } prev_exittime = curr_exittime; if (next_savetime <= now) { /* do this stuff in usersync periods */ clanlist_save(); gamelist_check_voidgame(); ladders.save(); next_savetime += prefs_get_user_sync_timer(); } accountlist_save(FS_NONE); accountlist_flush(FS_NONE); if (prefs_get_track() && track_time + (std::time_t)prefs_get_track() <= now) { track_time = now; tracker_send_report(laddrs); } if (prefs_get_war3_ladder_update_secs() && war3_ladder_updatetime + (std::time_t)prefs_get_war3_ladder_update_secs() <= now) { war3_ladder_updatetime = now; ladders.status(); } if (prefs_get_output_update_secs() && output_updatetime + (std::time_t)prefs_get_output_update_secs() <= now) { output_updatetime = now; output_write_to_file(); } if (do_save) { eventlog(eventlog_level_info, __FUNCTION__, "saving accounts due to std::signal"); clanlist_save(); do_save = 0; } if (do_restart) { if (do_restart == restart_mode_all) { eventlog(eventlog_level_info, __FUNCTION__, "reading configuration files"); if (cmdline_get_preffile()) { if (prefs_load(cmdline_get_preffile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not parse configuration file"); } else if (prefs_load(BNETD_DEFAULT_CONF_FILE) < 0) eventlog(eventlog_level_error, __FUNCTION__, "using default configuration"); if (eventlog_open(prefs_get_logfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not use the file \"{}\" for the eventlog", prefs_get_logfile()); /* FIXME: load new network settings */ /* reload server name */ server_set_hostname(); attrlayer_load_default(); } if (do_restart == restart_mode_all || do_restart == restart_mode_i18n) { i18n_reload(); } if (do_restart == restart_mode_all || do_restart == restart_mode_channels) { channellist_reload(); } if (do_restart == restart_mode_all || do_restart == restart_mode_realms) { if (realmlist_reload(prefs_get_realmfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not reload realm list"); } if (do_restart == restart_mode_all || do_restart == restart_mode_autoupdate) { autoupdate_unload(); if (autoupdate_load(prefs_get_mpqfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load autoupdate list"); } if (do_restart == restart_mode_all || do_restart == restart_mode_news) { news_unload(); if (news_load(prefs_get_newsfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load news list"); } if (do_restart == restart_mode_all || do_restart == restart_mode_versioncheck) { unload_versioncheck_conf(); if (!load_versioncheck_conf(prefs_get_versioncheck_file())) { eventlog(eventlog_level_error, __FUNCTION__, "could not load versioncheck list"); } } if (do_restart == restart_mode_all || do_restart == restart_mode_ipbans) { if (ipbanlist_destroy() < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not unload old IP ban list"); ipbanlist_create(); if (ipbanlist_load(prefs_get_ipbanfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load new IP ban list"); } if (do_restart == restart_mode_all || do_restart == restart_mode_helpfile) { helpfile_unload(); if (helpfile_init(prefs_get_helpfile()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load the helpfile"); } if (do_restart == restart_mode_all || do_restart == restart_mode_banners) { try { if (AdBannerList.is_loaded()) { AdBannerList.unload(); } AdBannerList.load(prefs_get_adfile()); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what()); } } if (do_restart == restart_mode_all || do_restart == restart_mode_tracker) { if (prefs_get_track()) tracker_set_servers(prefs_get_trackserv_addrs()); } if (do_restart == restart_mode_all || do_restart == restart_mode_commandgroups) { if (command_groups_reload(prefs_get_command_groups_file()) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not load new command_groups list"); } if (do_restart == restart_mode_all || do_restart == restart_mode_aliasfile) { aliasfile_unload(); aliasfile_load(prefs_get_aliasfile()); } if (do_restart == restart_mode_all || do_restart == restart_mode_transfile) { if (trans_reload(prefs_get_transfile(), TRANS_BNETD) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not reload trans list"); } if (do_restart == restart_mode_all || do_restart == restart_mode_tournament) { tournament_reload(prefs_get_tournament_file()); } if (do_restart == restart_mode_all || do_restart == restart_mode_icons) { customicons_unload(); customicons_load(prefs_get_customicons_file()); } if (do_restart == restart_mode_all || do_restart == restart_mode_anongame) { anongame_infos_unload(); anongame_infos_load(prefs_get_anongame_infos_file()); } #ifdef WITH_LUA if (do_restart == restart_mode_all || do_restart == restart_mode_lua) { lua_handle_server(luaevent_server_rehash); lua_unload(); lua_load(prefs_get_scriptdir()); } #endif eventlog(eventlog_level_info, __FUNCTION__, "done reconfiguring"); do_restart = 0; } /* only check timers once a second */ if (now > prev_time) { prev_time = now; timerlist_check_timers(now); #ifdef WITH_LUA lua_handle_server(luaevent_server_mainloop); #endif } /* no need to populate the fdwatch structures as they are populated on the fly * by sd_accept, conn_push_outqueue, conn_pull_outqueue, conn_destory */ /* find which sockets need servicing */ switch (fdwatch(BNETD_POLL_INTERVAL)) { case -1: /* error */ if ( #ifdef PSOCK_EINTR psock_errno() != PSOCK_EINTR && #endif 1) eventlog(eventlog_level_error, __FUNCTION__, "fdwatch() failed (errno: {})", pstrerror(psock_errno())); case 0: /* timeout... no sockets need checking */ continue; } /* cycle through the ready sockets and handle them */ fdwatch_handle(); /* reap dead connections */ connlist_reap(); } } static void _shutdown_conns(void) { t_elem *ccurr; t_connection *c; LIST_TRAVERSE(connlist(), ccurr) { c = (t_connection *)elem_get_data(ccurr); conn_destroy(c, &ccurr, DESTROY_FROM_CONNLIST); } } static void _shutdown_addrs(t_addrlist *laddrs) { t_addr * curr_laddr; t_laddr_info * laddr_info; t_elem const * acurr; LIST_TRAVERSE_CONST(laddrs, acurr) { curr_laddr = (t_addr*)elem_get_data(acurr); if ((laddr_info = (t_laddr_info*)addr_get_data(curr_laddr).p)) { if (laddr_info->usocket != -1) psock_close(laddr_info->usocket); if (laddr_info->ssocket != -1) psock_close(laddr_info->ssocket); xfree(laddr_info); } } addrlist_destroy(laddrs); } extern int server_process(void) { t_addrlist * laddrs; laddrs = NULL; /* Start with the Battle.net address list */ if (_setup_add_addrs(&laddrs, prefs_get_bnetdserv_addrs(), INADDR_ANY, BNETD_SERV_PORT, laddr_type_bnet)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_bnet), prefs_get_bnetdserv_addrs()); return -1; } /* Append list of addresses to listen for IRC connections */ if (_setup_add_addrs(&laddrs, prefs_get_irc_addrs(), INADDR_ANY, BNETD_IRC_PORT, laddr_type_irc)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_irc), prefs_get_irc_addrs()); _shutdown_addrs(laddrs); return -1; } /* Append list of addresses to listen for WOLv1 connections */ if (_setup_add_addrs(&laddrs, prefs_get_wolv1_addrs(), INADDR_ANY, BNETD_WOLV1_PORT, laddr_type_wolv1)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_wolv1), prefs_get_wolv1_addrs()); _shutdown_addrs(laddrs); return -1; } /* Append list of addresses to listen for WOLv2 connections */ if (_setup_add_addrs(&laddrs, prefs_get_wolv2_addrs(), INADDR_ANY, BNETD_WOLV2_PORT, laddr_type_wolv2)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_wolv2), prefs_get_wolv2_addrs()); _shutdown_addrs(laddrs); return -1; } /* Append list of addresses to listen for APIREGISER connections */ if (_setup_add_addrs(&laddrs, prefs_get_apireg_addrs(), INADDR_ANY, BNETD_APIREG_PORT, laddr_type_apireg)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_apireg), prefs_get_apireg_addrs()); _shutdown_addrs(laddrs); return -1; } /* Append list of addresses to listen for WGAMERES connections */ if (_setup_add_addrs(&laddrs, prefs_get_wgameres_addrs(), INADDR_ANY, BNETD_WGAMERES_PORT, laddr_type_wgameres)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_wgameres), prefs_get_wgameres_addrs()); _shutdown_addrs(laddrs); return -1; } /* Append list of addresses to listen for W3ROUTE connections */ if (_setup_add_addrs(&laddrs, prefs_get_w3route_addr(), INADDR_ANY, BNETD_W3ROUTE_PORT, laddr_type_w3route)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_w3route), prefs_get_w3route_addr()); _shutdown_addrs(laddrs); return -1; } /* Append list of addresses to listen for telnet connections */ if (_setup_add_addrs(&laddrs, prefs_get_telnet_addrs(), INADDR_ANY, BNETD_TELNET_PORT, laddr_type_telnet)) { eventlog(eventlog_level_error, __FUNCTION__, "could not create {} server address list from \"{}\"", laddr_type_get_str(laddr_type_telnet), prefs_get_telnet_addrs()); _shutdown_addrs(laddrs); return -1; } if (_setup_listensock(laddrs)) { _shutdown_addrs(laddrs); return -1; } /* setup std::signal handlers */ prev_exittime = sigexittime; #ifdef DO_POSIXSIG _setup_posixsig(); #endif #ifdef WIN32 # ifndef WIN32_GUI SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); # endif #endif _server_mainloop(laddrs); #ifdef WIN32 # ifndef WIN32_GUI SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, FALSE); # endif #endif /* cleanup for server shutdown */ _shutdown_conns(); _shutdown_addrs(laddrs); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/server.h000066400000000000000000000057011462640461400175450ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SERVER_TYPES #define INCLUDED_SERVER_TYPES #include namespace pvpgn { namespace bnetd { #ifdef SERVER_INTERNAL_ACCESS typedef enum { laddr_type_bnet, /* classic battle.net service (usually on port 6112) */ laddr_type_w3route, /* warcraft 3 playgame routing (def. port 6200) */ laddr_type_irc, /* Internet Relay Chat service (port is varying; mostly on port 6667 or 7000) */ laddr_type_wolv1, /* Westwood Online v1 (IRC) Services (port is 4000) */ laddr_type_wolv2, /* Westwood Online v2 (IRC) Services (port is 4005) */ laddr_type_apireg, /* Westwood API Register Services (port is 5700) */ laddr_type_wgameres,/* Westwood gameres Services (port is 4807) */ laddr_type_telnet /* telnet service (usually on port 23) */ } t_laddr_type; /* listen address structure */ typedef struct { int ssocket; /* TCP listen socket */ int usocket; /* UDP socket */ t_laddr_type type; } t_laddr_info; #endif extern std::time_t now; } } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_SERVER_PROTOS #define INCLUDED_SERVER_PROTOS namespace pvpgn { namespace bnetd { enum restart_mode { restart_mode_none, // always first (0) restart_mode_all, restart_mode_i18n, restart_mode_channels, restart_mode_realms, restart_mode_autoupdate, restart_mode_news, restart_mode_versioncheck, restart_mode_ipbans, restart_mode_helpfile, restart_mode_banners, restart_mode_tracker, restart_mode_commandgroups, restart_mode_aliasfile, restart_mode_transfile, restart_mode_tournament, restart_mode_icons, restart_mode_anongame, restart_mode_lua }; extern unsigned int server_get_uptime(void); extern unsigned int server_get_starttime(void); extern void server_quit_delay(int delay); extern void server_set_hostname(void); extern char const * server_get_hostname(void); extern void server_clear_hostname(void); extern int server_process(void); extern void server_quit_wraper(void); extern void server_restart_wraper(int mode); extern void server_save_wraper(void); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_common.cpp000066400000000000000000000540761462640461400207520ustar00rootroot00000000000000/* * Copyright (C) 2002 TheUndying * Copyright (C) 2002 zap-zero * Copyright (C) 2002,2003,2005 Dizzy * Copyright (C) 2002 Zzzoom * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifdef WITH_SQL #define SQL_INTERNAL # include "sql_common.h" #undef SQL_INTERNAL #include #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/flags.h" #include "common/list.h" #include "common/tag.h" #define CLAN_INTERNAL_ACCESS #define TEAM_INTERNAL_ACCESS #include "team.h" #include "account.h" #include "sql_dbcreator.h" #ifdef WITH_SQL_MYSQL #include "sql_mysql.h" #endif #ifdef WITH_SQL_PGSQL #include "sql_pgsql.h" #endif #ifdef WITH_SQL_SQLITE3 #include "sql_sqlite3.h" #endif #ifdef WITH_SQL_ODBC #include "sql_odbc.h" #endif #include "clan.h" #include "prefs.h" #undef CLAN_INTERNAL_ACCESS #undef TEAM_INTERNAL_ACCESS #include "common/setup_after.h" namespace pvpgn { namespace bnetd { unsigned int sql_defacct; t_sql_engine *sql = NULL; #ifndef SQL_ON_DEMAND char const *sql_tables[] = { "BNET", "Record", "profile", "friend", "Team", NULL }; #endif /* SQL_ON_DEMAND */ const char* tab_prefix = SQL_DEFAULT_PREFIX; static char query[1024]; extern int sql_init(const char *dbpath) { char *tok, *path, *tmp, *p; const char *dbhost = NULL; const char *dbname = NULL; const char *dbuser = NULL; const char *dbpass = NULL; const char *driver = NULL; const char *dbport = NULL; const char *dbsocket = NULL; const char *def = NULL; const char *pref = NULL; path = xstrdup(dbpath); tmp = path; while ((tok = std::strtok(tmp, ";")) != NULL) { tmp = NULL; if ((p = std::strchr(tok, '=')) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "invalid storage_path, no '=' present in token"); xfree((void *)path); return -1; } *p = '\0'; if (strcasecmp(tok, "host") == 0) dbhost = p + 1; else if (strcasecmp(tok, "mode") == 0) driver = p + 1; else if (strcasecmp(tok, "name") == 0) dbname = p + 1; else if (strcasecmp(tok, "port") == 0) dbport = p + 1; else if (strcasecmp(tok, "socket") == 0) dbsocket = p + 1; else if (strcasecmp(tok, "user") == 0) dbuser = p + 1; else if (strcasecmp(tok, "pass") == 0) dbpass = p + 1; else if (strcasecmp(tok, "default") == 0) def = p + 1; else if (strcasecmp(tok, "prefix") == 0) pref = p + 1; else eventlog(eventlog_level_warn, __FUNCTION__, "unknown token in storage_path : '{}'", tok); } if (driver == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "no mode specified"); xfree((void *)path); return -1; } if (def == NULL) sql_defacct = STORAGE_SQL_DEFAULT_UID; else sql_defacct = std::atoi(def); if (pref == NULL) tab_prefix = SQL_DEFAULT_PREFIX; else tab_prefix = xstrdup(pref); do { #ifdef WITH_SQL_MYSQL if (strcasecmp(driver, "mysql") == 0) { sql = &sql_mysql; if (sql->init(dbhost, dbport, dbsocket, dbname, dbuser, dbpass)) { eventlog(eventlog_level_error, __FUNCTION__, "got error init db"); sql = NULL; xfree((void *)path); return -1; } break; } #endif /* WITH_SQL_MYSQL */ #ifdef WITH_SQL_PGSQL if (strcasecmp(driver, "pgsql") == 0) { sql = &sql_pgsql; if (sql->init(dbhost, dbport, dbsocket, dbname, dbuser, dbpass)) { eventlog(eventlog_level_error, __FUNCTION__, "got error init db"); sql = NULL; xfree((void *)path); return -1; } break; } #endif /* WITH_SQL_PGSQL */ #ifdef WITH_SQL_SQLITE3 if (strcasecmp(driver, "sqlite3") == 0) { sql = &sql_sqlite3; if (sql->init(NULL, 0, NULL, dbname, NULL, NULL)) { eventlog(eventlog_level_error, __FUNCTION__, "got error init db"); sql = NULL; xfree((void *)path); return -1; } break; } #endif /* WITH_SQL_SQLITE3 */ #ifdef WITH_SQL_ODBC if (strcasecmp(driver, "odbc") == 0) { sql = &sql_odbc; if (sql->init(dbhost, dbport, dbsocket, dbname, dbuser, dbpass)) { eventlog(eventlog_level_error, __FUNCTION__, "got error init db"); sql = NULL; free((void *)path); return -1; } break; } #endif /* WITH_SQL_ODBC */ eventlog(eventlog_level_error, __FUNCTION__, "no driver found for '{}'", driver); xfree((void *)path); return -1; } while (0); xfree((void *)path); sql_dbcreator(sql); return 0; } extern int sql_close(void) { if (sql == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "sql not initilized"); return -1; } sql->close(); sql = NULL; if (strcmp(tab_prefix, SQL_DEFAULT_PREFIX) != 0) { xfree((void*)tab_prefix); tab_prefix = NULL; } return 0; } extern unsigned sql_read_maxuserid(void) { t_sql_res *result; t_sql_row *row; long maxuid; if (sql == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "sql not initilized"); return 0; } std::snprintf(query, sizeof(query), "SELECT max(" SQL_UID_FIELD ") FROM %sBNET", tab_prefix); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"SELECT max(" SQL_UID_FIELD ") FROM {}BNET\"", tab_prefix); return 0; } row = sql->fetch_row(result); if (row == NULL || row[0] == NULL) { sql->free_result(result); eventlog(eventlog_level_error, __FUNCTION__, "got NULL max"); return 0; } maxuid = std::atol(row[0]); sql->free_result(result); if (maxuid < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid maxuserid"); return 0; } return maxuid; } extern int sql_read_accounts(int flag, t_read_accounts_func cb, void *data) { t_sql_res *result = NULL; t_sql_row *row; t_storage_info *info; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } if (cb == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL callback"); return -1; } /* don't actually load anything here if ST_FORCE is not set as SQL is indexed */ if (!FLAG_ISSET(flag, ST_FORCE)) return 1; std::snprintf(query, sizeof(query), "SELECT DISTINCT(" SQL_UID_FIELD ") FROM %sBNET", tab_prefix); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL) { if (sql->num_rows(result) <= 1) { sql->free_result(result); return 0; /* empty user list */ } while ((row = sql->fetch_row(result)) != NULL) { if (row[0] == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL uid from db"); continue; } if ((unsigned int)std::atoi(row[0]) == sql_defacct) continue; /* skip default account */ info = xmalloc(sizeof(t_sql_info)); *((unsigned int *)info) = std::atoi(row[0]); cb(info, data); } sql->free_result(result); } else { eventlog(eventlog_level_error, __FUNCTION__, "error query db (query:\"{}\")", query); return -1; } return 0; } extern int sql_cmp_info(t_storage_info * info1, t_storage_info * info2) { return *((unsigned int *)info1) != *((unsigned int *)info2); } extern int sql_free_info(t_storage_info * info) { if (info) xfree((void *)info); return 0; } extern t_storage_info *sql_get_defacct(void) { t_storage_info *info; info = xmalloc(sizeof(t_sql_info)); *((unsigned int *)info) = sql_defacct; return info; } extern int sql_load_clans(t_load_clans_func cb) { t_sql_res *result; t_sql_res *result2; t_sql_row *row; t_sql_row *row2; t_clan *clan; int member_uid; t_clanmember *member; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } if (cb == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL callback"); return -1; } std::snprintf(query, sizeof(query), "SELECT cid, short, name, motd, creation_time FROM %sclan WHERE cid > 0", tab_prefix); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL) { if (sql->num_rows(result) < 1) { sql->free_result(result); return 0; /* empty clan list */ } while ((row = sql->fetch_row(result)) != NULL) { if (row[0] == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL cid from db"); continue; } clan = (t_clan *)xmalloc(sizeof(t_clan)); if (!(clan->clanid = std::atoi(row[0]))) { eventlog(eventlog_level_error, __FUNCTION__, "got bad cid"); sql->free_result(result); return -1; } clan->tag = std::atoi(row[1]); clan->clanname = xstrdup(row[2]); clan->clan_motd = xstrdup(row[3]); clan->creation_time = std::atoi(row[4]); clan->created = 1; clan->modified = 0; clan->channel_type = prefs_get_clan_channel_default_private(); clan->members = list_create(); std::snprintf(query, sizeof(query), "SELECT " SQL_UID_FIELD ", status, join_time FROM %sclanmember WHERE cid='%u'", tab_prefix, clan->clanid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result2 = sql->query_res(query)) != NULL) { if (sql->num_rows(result2) >= 1) while ((row2 = sql->fetch_row(result2)) != NULL) { member = (t_clanmember *)xmalloc(sizeof(t_clanmember)); if (row2[0] == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL uid from db"); continue; } if (!(member_uid = std::atoi(row2[0]))) continue; if (!(member->memberacc = accountlist_find_account_by_uid(member_uid))) { eventlog(eventlog_level_error, __FUNCTION__, "cannot find uid {}", member_uid); xfree((void *)member); continue; } member->status = std::atoi(row2[1]); member->join_time = std::atoi(row2[2]); member->clan = clan; member->fullmember = 1; if ((member->status == CLAN_NEW) && (std::time(NULL) - member->join_time > prefs_get_clan_newer_time() * 3600)) { member->status = CLAN_PEON; clan->modified = 1; member->modified = 1; } list_append_data(clan->members, member); account_set_clanmember(member->memberacc, member); eventlog(eventlog_level_trace, __FUNCTION__, "added member: uid: {} status: {} join_time: {}", member_uid, member->status + '0', (unsigned)member->join_time); } sql->free_result(result2); cb(clan); } else eventlog(eventlog_level_error, __FUNCTION__, "error query db (query:\"{}\")", query); } sql->free_result(result); } else { eventlog(eventlog_level_error, __FUNCTION__, "error query db (query:\"{}\")", query); return -1; } return 0; } extern int sql_write_clan(void *data) { char esc_motd[CLAN_MOTD_MAX * 2 + 1]; t_sql_res *result; t_sql_row *row; t_elem *curr; t_clanmember *member; t_clan *clan = (t_clan *)data; int num; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } std::snprintf(query, sizeof(query), "SELECT count(*) FROM %sclan WHERE cid='%u'", tab_prefix, clan->clanid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL) { row = sql->fetch_row(result); if (row == NULL || row[0] == NULL) { sql->free_result(result); eventlog(eventlog_level_error, __FUNCTION__, "got NULL count"); return -1; } num = std::atol(row[0]); sql->free_result(result); sql->escape_string(esc_motd, clan->clan_motd, std::strlen(clan->clan_motd)); if (num < 1) std::snprintf(query, sizeof(query), "INSERT INTO %sclan (cid, short, name, motd, creation_time) VALUES('%u', '%d', '%s', '%s', '%u')", tab_prefix, clan->clanid, clan->tag, clan->clanname, esc_motd, (unsigned)clan->creation_time); else std::snprintf(query, sizeof(query), "UPDATE %sclan SET short='%d', name='%s', motd='%s', creation_time='%u' WHERE cid='%u'", tab_prefix, clan->tag, clan->clanname, esc_motd, (unsigned)clan->creation_time, clan->clanid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); return -1; } LIST_TRAVERSE(clan->members, curr) { unsigned int uid; if (!(member = (t_clanmember *)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); continue; } if (member->fullmember == 0) continue; if ((member->status == CLAN_NEW) && (std::time(NULL) - member->join_time > prefs_get_clan_newer_time() * 3600)) { member->status = CLAN_PEON; member->modified = 1; } if (member->modified) { uid = account_get_uid(member->memberacc); std::snprintf(query, sizeof(query), "SELECT count(*) FROM %sclanmember WHERE " SQL_UID_FIELD "='%u'", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL) { row = sql->fetch_row(result); if (row == NULL || row[0] == NULL) { sql->free_result(result); eventlog(eventlog_level_error, __FUNCTION__, "got NULL count"); return -1; } num = std::atol(row[0]); sql->free_result(result); if (num < 1) std::snprintf(query, sizeof(query), "INSERT INTO %sclanmember (cid, " SQL_UID_FIELD ", status, join_time) VALUES('%u', '%u', '%d', '%u')", tab_prefix, clan->clanid, uid, member->status, (unsigned)member->join_time); else std::snprintf(query, sizeof(query), "UPDATE %sclanmember SET cid='%u', status='%d', join_time='%u' WHERE " SQL_UID_FIELD "='%u'", tab_prefix, clan->clanid, member->status, (unsigned)member->join_time, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); return -1; } } else { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); return -1; } member->modified = 0; } } } else { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); return -1; } return 0; } extern int sql_remove_clan(int clantag) { t_sql_res *result; t_sql_row *row; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } std::snprintf(query, sizeof(query), "SELECT cid FROM %sclan WHERE short = '%d'", tab_prefix, clantag); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (!(result = sql->query_res(query))) { eventlog(eventlog_level_error, __FUNCTION__, "error query db (query:\"{}\")", query); return -1; } if (sql->num_rows(result) != 1) { sql->free_result(result); return -1; /*clan not found or found more than 1 */ } if ((row = sql->fetch_row(result))) { unsigned int cid = std::atoi(row[0]); std::snprintf(query, sizeof(query), "DELETE FROM %sclanmember WHERE cid='%u'", tab_prefix, cid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query) != 0) return -1; std::snprintf(query, sizeof(query), "DELETE FROM %sclan WHERE cid='%u'", tab_prefix, cid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query) != 0) return -1; } sql->free_result(result); return 0; } extern int sql_remove_clanmember(int uid) { if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } std::snprintf(query, sizeof(query), "DELETE FROM %sclanmember WHERE " SQL_UID_FIELD "='%u'", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); return -1; } return 0; } extern int sql_load_teams(t_load_teams_func cb) { t_sql_res *result; t_sql_row *row; t_team *team; int i; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } if (cb == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL callback"); return -1; } std::snprintf(query, sizeof(query), "SELECT teamid, size, clienttag, lastgame, member1, member2, member3, member4, wins,losses, xp, level, rank FROM %sarrangedteam WHERE teamid > 0", tab_prefix); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL) { if (sql->num_rows(result) < 1) { sql->free_result(result); return 0; /* empty team list */ } while ((row = sql->fetch_row(result)) != NULL) { if (row[0] == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL teamid from db"); continue; } team = (t_team *)xmalloc(sizeof(t_team)); if (!(team->teamid = std::atoi(row[0]))) { eventlog(eventlog_level_error, __FUNCTION__, "got bad teamid"); sql->free_result(result); return -1; } team->size = std::atoi(row[1]); team->clienttag = tag_str_to_uint(row[2]); team->lastgame = std::strtoul(row[3], NULL, 10); team->teammembers[0] = std::strtoul(row[4], NULL, 10); team->teammembers[1] = std::strtoul(row[5], NULL, 10); team->teammembers[2] = std::strtoul(row[6], NULL, 10); team->teammembers[3] = std::strtoul(row[7], NULL, 10); for (i = 0; i < MAX_TEAMSIZE; i++) { if (i < team->size) { if ((team->teammembers[i] == 0)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team data: too few members"); free((void *)team); goto load_team_failure; } } else { if ((team->teammembers[i] != 0)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team data: too many members"); free((void *)team); goto load_team_failure; } } team->members[i] = NULL; } team->wins = std::atoi(row[8]); team->losses = std::atoi(row[9]); team->xp = std::atoi(row[10]); team->level = std::atoi(row[11]); team->rank = std::atoi(row[12]); eventlog(eventlog_level_trace, __FUNCTION__, "succesfully loaded team {}", team->teamid); cb(team); load_team_failure: ; } sql->free_result(result); } else { eventlog(eventlog_level_error, __FUNCTION__, "error query db (query:\"{}\")", query); return -1; } return 0; } extern int sql_write_team(void *data) { t_sql_res *result; t_sql_row *row; t_team *team = (t_team *)data; int num; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } std::snprintf(query, sizeof(query), "SELECT count(*) FROM %sarrangedteam WHERE teamid='%u'", tab_prefix, team->teamid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL) { row = sql->fetch_row(result); if (row == NULL || row[0] == NULL) { sql->free_result(result); eventlog(eventlog_level_error, __FUNCTION__, "got NULL count"); return -1; } num = std::atol(row[0]); sql->free_result(result); if (num < 1) std::snprintf(query, sizeof(query), "INSERT INTO %sarrangedteam (teamid, size, clienttag, lastgame, member1, member2, member3, member4, wins,losses, xp, level, rank) VALUES('%u', '%c', '%s', '%u', '%u', '%u', '%u', '%u', '%d', '%d', '%d', '%d', '%d')", tab_prefix, team->teamid, team->size + '0', clienttag_uint_to_str(team->clienttag), (unsigned int)team->lastgame, team->teammembers[0], team->teammembers[1], team->teammembers[2], team->teammembers[3], team->wins, team->losses, team->xp, team->level, team->rank); else std::snprintf(query, sizeof(query), "UPDATE %sarrangedteam SET size='%c', clienttag='%s', lastgame='%u', member1='%u', member2='%u', member3='%u', member4='%u', wins='%d', losses='%d', xp='%d', level='%d', rank='%d' WHERE teamid='%u'", tab_prefix, team->size + '0', clienttag_uint_to_str(team->clienttag), (unsigned int)team->lastgame, team->teammembers[0], team->teammembers[1], team->teammembers[2], team->teammembers[3], team->wins, team->losses, team->xp, team->level, team->rank, team->teamid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); return -1; } } else { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); return -1; } return 0; } extern int sql_remove_team(unsigned int teamid) { if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } std::snprintf(query, sizeof(query), "DELETE FROM %sarrangedteam WHERE teamid='%u'", tab_prefix, teamid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query) != 0) return -1; return 0; } } } #endif /* WITH_SQL */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_common.h000066400000000000000000000063051462640461400204070ustar00rootroot00000000000000/* * Copyright (C) 2002,2003,2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SQL_COMMON_TYPES #define INCLUDED_SQL_COMMON_TYPES namespace pvpgn { namespace bnetd { typedef unsigned int t_sql_info; /* used as a pointer to it */ #define t_sql_res void typedef char * t_sql_row; typedef char * t_sql_field; typedef struct { int(*init)(const char *host, const char *port, const char *socket, const char *name, const char *user, const char *pass); int(*close)(void); t_sql_res * (*query_res)(const char *); int(*query)(const char *); t_sql_row * (*fetch_row)(t_sql_res *); void(*free_result)(t_sql_res *); unsigned int(*num_rows)(t_sql_res *); unsigned int(*num_fields)(t_sql_res *); unsigned int(*affected_rows)(void); t_sql_field * (*fetch_fields)(t_sql_res *); int(*free_fields)(t_sql_field *); void(*escape_string)(char *, const char *, int); } t_sql_engine; } } #endif /* INCLUDED_SQL_COMMON_TYPES */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_SQL_COMMON_PROTOS #define INCLUDED_SQL_COMMON_PROTOS #include "storage.h" namespace pvpgn { namespace bnetd { extern t_storage storage_sql; #ifdef SQL_INTERNAL #define CURRENT_DB_VERSION 150 #define DB_MAX_ATTRKEY 128 #define DB_MAX_ATTRVAL 180 #define DB_MAX_TAB 64 #define SQL_UID_FIELD "uid" #define STORAGE_SQL_DEFAULT_UID 0 #define SQL_DEFAULT_PREFIX "" /* FIXME: (HarpyWar) do not define SQL_ON_DEMAND - instead it will cause a lot of sql queries (one attribute selection = one query) https://github.com/pvpgn/pvpgn-server/issues/85 */ //#define SQL_ON_DEMAND 1 extern t_sql_engine *sql; extern unsigned int sql_defacct; extern const char* tab_prefix; #ifndef SQL_ON_DEMAND extern char const *sql_tables[]; #endif /* SQL_ON_DEMAND */ extern int sql_init(const char *); extern int sql_close(void); extern unsigned sql_read_maxuserid(void); extern int sql_read_accounts(int flag, t_read_accounts_func cb, void *data); extern int sql_cmp_info(t_storage_info * info1, t_storage_info * info2); extern int sql_free_info(t_storage_info * info); extern t_storage_info *sql_get_defacct(void); extern int sql_load_clans(t_load_clans_func cb); extern int sql_write_clan(void *data); extern int sql_remove_clan(int clantag); extern int sql_remove_clanmember(int uid); extern int sql_load_teams(t_load_teams_func cb); extern int sql_write_team(void *data); extern int sql_remove_team(unsigned int teamid); #endif /* SQL_INTERNAL */ } } #endif /* INCLUDED_SQL_COMMON_PROTOS */ #endif /* JUST_NEED_TYPES */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_dbcreator.cpp000066400000000000000000000505351462640461400214230ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifdef WITH_SQL #define SQL_DBCREATOR_INTERNAL_ACCESS #include "sql_dbcreator.h" #undef SQL_DBCREATOR_INTERNAL_ACCESS #include #include #include "common/eventlog.h" #include "common/xstr.h" #include "common/util.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { t_elem * curr_table = NULL; t_elem * curr_column = NULL; t_elem * curr_cmd = NULL; t_db_layout * db_layout; static void sql_escape_command(char *escape, const char *from, int len); t_column * create_column(char * name, char * value, char * mode, char * extra_cmd) { t_column * column; if (!(name)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL column name"); return NULL; } if (!(value)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL column value"); return NULL; } column = (t_column *)xmalloc(sizeof(t_column)); column->name = xstrdup(name); column->value = xstrdup(value); if (mode && extra_cmd) { column->mode = xstrdup(mode); column->extra_cmd = xstrdup(extra_cmd); } else { column->mode = NULL; column->extra_cmd = NULL; } return column; } void dispose_column(t_column * column) { if (column) { if (column->name) xfree((void *)column->name); if (column->value) xfree((void *)column->value); if (column->mode) xfree((void *)column->mode); if (column->extra_cmd) xfree((void *)column->extra_cmd); xfree((void *)column); } } t_sqlcommand * create_sqlcommand(char * sql_command, char * mode, char * extra_cmd) { t_sqlcommand * sqlcommand; if (!(sql_command)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL sql_command"); return NULL; } sqlcommand = (t_sqlcommand *)xmalloc(sizeof(t_sqlcommand)); sqlcommand->sql_command = xstrdup(sql_command); if (mode && extra_cmd) { sqlcommand->mode = xstrdup(mode); sqlcommand->extra_cmd = xstrdup(extra_cmd); } else { sqlcommand->mode = NULL; sqlcommand->extra_cmd = NULL; } return sqlcommand; } void dispose_sqlcommand(t_sqlcommand * sqlcommand) { if (sqlcommand) { if (sqlcommand->sql_command) xfree((void *)sqlcommand->sql_command); if (sqlcommand->mode) xfree((void *)sqlcommand->mode); if (sqlcommand->extra_cmd) xfree((void *)sqlcommand->extra_cmd); xfree(sqlcommand); } } t_table * create_table(char * name) { t_table * table; if (!(name)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL table name"); return NULL; } table = (t_table *)xmalloc(sizeof(t_table)); table->name = xstrdup(name); table->columns = list_create(); table->sql_commands = list_create(); return table; } void dispose_table(t_table * table) { t_elem * curr; t_column * column; t_sqlcommand * sql_command; if (table) { if (table->name) xfree((void *)table->name); // free list if (table->columns) { LIST_TRAVERSE(table->columns, curr) { if (!(column = (t_column *)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } dispose_column(column); list_remove_elem(table->columns, &curr); } list_destroy(table->columns); } if (table->sql_commands) { LIST_TRAVERSE(table->sql_commands, curr) { if (!(sql_command = (t_sqlcommand *)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } dispose_sqlcommand(sql_command); list_remove_elem(table->sql_commands, &curr); } list_destroy(table->sql_commands); } xfree((void *)table); } } void table_add_column(t_table * table, t_column * column) { if ((table) && (column)) { list_append_data(table->columns, column); } } void table_add_sql_command(t_table * table, t_sqlcommand * sql_command) { if ((table) && (sql_command)) { list_append_data(table->sql_commands, sql_command); } } t_db_layout * create_db_layout() { t_db_layout * db_layout; db_layout = (t_db_layout *)xmalloc(sizeof(t_db_layout)); db_layout->tables = list_create(); return db_layout; } void dispose_db_layout(t_db_layout * db_layout) { t_elem * curr; t_table * table; if (db_layout) { if (db_layout->tables) { LIST_TRAVERSE(db_layout->tables, curr) { if (!(table = (t_table *)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } dispose_table(table); list_remove_elem(db_layout->tables, &curr); } list_destroy(db_layout->tables); } xfree((void *)db_layout); } } void db_layout_add_table(t_db_layout * db_layout, t_table * table) { if ((db_layout) && (table)) { list_append_data(db_layout->tables, table); } } t_table * db_layout_get_first_table(t_db_layout * db_layout) { t_table * table; curr_column = NULL; if (!(db_layout)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL db_layout"); return NULL; } if (!(db_layout->tables)) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL db_layout->tables"); return NULL; } if (!(curr_table = list_get_first(db_layout->tables))) { eventlog(eventlog_level_error, __FUNCTION__, "db_layout has no tables"); return NULL; } if (!(table = (t_table *)elem_get_data(curr_table))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); return NULL; } return table; } t_table * db_layout_get_next_table(t_db_layout * db_layout) { t_table * table; curr_column = NULL; if (!(curr_table)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL curr_table"); return NULL; } if (!(curr_table = elem_get_next(db_layout->tables, curr_table))) return NULL; if (!(table = (t_table *)elem_get_data(curr_table))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); return NULL; } return table; } t_column * table_get_first_column(t_table * table) { t_column * column; if (!(table)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL table"); return NULL; } if (!(table->columns)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL table->columns"); return NULL; } if (!(curr_column = list_get_first(table->columns))) { eventlog(eventlog_level_error, __FUNCTION__, "table has no columns"); return NULL; } if (!(column = (t_column *)elem_get_data(curr_column))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); return NULL; } return column; } t_column * table_get_next_column(t_table * table) { t_column * column; if (!(curr_column)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL curr_column"); return NULL; } if (!(curr_column = elem_get_next(table->columns, curr_column))) return NULL; if (!(column = (t_column *)elem_get_data(curr_column))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); return NULL; } return column; } t_sqlcommand * table_get_first_sql_command(t_table * table) { t_sqlcommand * sql_command; if (!(table)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL table"); return NULL; } if (!(table->sql_commands)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL table->sql_commands"); return NULL; } if (!(curr_cmd = list_get_first(table->sql_commands))) { return NULL; } if (!(sql_command = (t_sqlcommand *)elem_get_data(curr_cmd))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); return NULL; } return sql_command; } t_sqlcommand * table_get_next_sql_command(t_table * table) { t_sqlcommand * sql_command; if (!(curr_cmd)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL curr_cmd"); return NULL; } if (!(curr_cmd = elem_get_next(table->sql_commands, curr_cmd))) return NULL; if (!(sql_command = (t_sqlcommand *)elem_get_data(curr_cmd))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); return NULL; } return sql_command; } int load_db_layout(char const * filename) { std::FILE * fp; int lineno; char * line = NULL; char * tmp = NULL; char * table = NULL; char * column = NULL; char * value = NULL; char * sqlcmd = NULL; char * sqlcmd_escaped = NULL; char * mode = NULL; char * extra_cmd = NULL; char * extra_cmd_escaped = NULL; t_table * _table = NULL; t_column * _column = NULL; t_sqlcommand * _sqlcommand = NULL; t_xstr * xstr = NULL; int prefix; if (!(filename)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "can't open sql_DB_layout file"); return -1; } if (!(db_layout = create_db_layout())) { eventlog(eventlog_level_error, __FUNCTION__, "failed to create db_layout"); std::fclose(fp); return -1; } for (lineno = 1; (line = file_get_line(fp)); lineno++) { /* convert ${prefix} replacement variable */ prefix = 0; for (tmp = line; (tmp = std::strstr(line, "${prefix}")); line = tmp + 9 /* std::strlen("${prefix}") */) { *tmp = '\0'; if (!prefix) { prefix = 1; if (!xstr) xstr = xstr_alloc(); xstr_clear(xstr); } xstr_cat_str(xstr, line); xstr_cat_str(xstr, tab_prefix); } if (prefix) { /* append any reminder of the original string */ xstr_cat_str(xstr, line); line = (char*)xstr_get_str(xstr); } switch (line[0]) { case '[': table = &line[1]; if (!(tmp = std::strchr(table, ']'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing ']' in line {}", lineno); continue; } tmp[0] = '\0'; if (_table) db_layout_add_table(db_layout, _table); _table = create_table(table); break; case '"': if (!(_table)) { eventlog(eventlog_level_error, __FUNCTION__, "found a column without previous table in line {}", lineno); continue; } column = &line[1]; if (!(tmp = std::strchr(column, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing '\"' at the end of column definition in line {}", lineno); continue; } tmp[0] = '\0'; tmp++; if (!(tmp = std::strchr(tmp, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing default value in line {}", lineno); continue; } value = ++tmp; if (!(tmp = std::strchr(value, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing '\"' at the end of default value in line {}", lineno); continue; } tmp[0] = '\0'; tmp++; mode = NULL; extra_cmd = NULL; extra_cmd_escaped = NULL; if ((mode = std::strchr(tmp, '&')) || (mode = std::strchr(tmp, '|'))) { if (mode[0] == mode[1]) { mode[2] = '\0'; tmp = mode + 3; if (!(tmp = std::strchr(tmp, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing starting '\"' in extra sql_command on line {}", lineno); continue; } extra_cmd = ++tmp; if (!(tmp = std::strchr(extra_cmd, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing ending '\"' in extra sql_command on line {}", lineno); continue; } tmp[0] = '\0'; } else { eventlog(eventlog_level_error, __FUNCTION__, "missing or non-matching secondary character in combination logic on line {}", lineno); continue; } } if (extra_cmd) { extra_cmd_escaped = (char *)xmalloc(std::strlen(extra_cmd) * 2); sql_escape_command(extra_cmd_escaped, extra_cmd, std::strlen(extra_cmd)); } _column = create_column(column, value, mode, extra_cmd_escaped); if (extra_cmd_escaped) xfree(extra_cmd_escaped); table_add_column(_table, _column); _column = NULL; break; case ':': if (!(_table)) { eventlog(eventlog_level_error, __FUNCTION__, "found a sql_command without previous table in line {}", lineno); continue; } if (line[1] != '"') { eventlog(eventlog_level_error, __FUNCTION__, "missing starting '\"' in sql_command definition on line {}", lineno); continue; } sqlcmd = &line[2]; if (!(tmp = std::strchr(sqlcmd, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing ending '\"' in sql_command definition on line {}", lineno); continue; } tmp[0] = '\0'; tmp++; mode = NULL; extra_cmd = NULL; extra_cmd_escaped = NULL; if ((mode = std::strchr(tmp, '&')) || (mode = std::strchr(tmp, '|'))) { if (mode[0] == mode[1]) { mode[2] = '\0'; tmp = mode + 3; if (!(tmp = std::strchr(tmp, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing starting '\"' in extra sql_command on line {}", lineno); continue; } extra_cmd = ++tmp; if (!(tmp = std::strchr(extra_cmd, '"'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing ending '\"' in extra sql_command on line {}", lineno); continue; } tmp[0] = '\0'; } else { eventlog(eventlog_level_error, __FUNCTION__, "missing or non-matching secondary character in combination logic on line {}", lineno); continue; } } sqlcmd_escaped = (char *)xmalloc(std::strlen(sqlcmd) * 2); sql_escape_command(sqlcmd_escaped, sqlcmd, std::strlen(sqlcmd)); if (extra_cmd) { extra_cmd_escaped = (char *)xmalloc(std::strlen(extra_cmd) * 2); sql_escape_command(extra_cmd_escaped, extra_cmd, std::strlen(extra_cmd)); } _sqlcommand = create_sqlcommand(sqlcmd_escaped, mode, extra_cmd_escaped); xfree(sqlcmd_escaped); if (extra_cmd_escaped) xfree(extra_cmd_escaped); table_add_sql_command(_table, _sqlcommand); _sqlcommand = NULL; break; case '#': break; default: eventlog(eventlog_level_error, __FUNCTION__, "illegal starting symbol at line {}", lineno); } } if (_table) db_layout_add_table(db_layout, _table); if (xstr) xstr_free(xstr); file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); return 0; } int sql_dbcreator(t_sql_engine * sql) { t_table * table; t_column * column; char _column[1024]; char query[1024]; t_sqlcommand * sqlcmd; load_db_layout(prefs_get_DBlayoutfile()); eventlog(eventlog_level_info, __FUNCTION__, "Creating missing tables and columns (if any)"); for (table = db_layout_get_first_table(db_layout); table; table = db_layout_get_next_table(db_layout)) { column = table_get_first_column(table); std::sprintf(query, "CREATE TABLE %s (%s default %s)", table->name, column->name, column->value); //create table if missing if (!(sql->query(query))) { eventlog(eventlog_level_info, __FUNCTION__, "added missing table {} to DB", table->name); eventlog(eventlog_level_info, __FUNCTION__, "added missing column {} to table {}", column->name, table->name); } for (; column; column = table_get_next_column(table)) { std::sprintf(query, "ALTER TABLE %s ADD %s DEFAULT %s", table->name, column->name, column->value); if (!(sql->query(query))) { eventlog(eventlog_level_info, __FUNCTION__, "added missing column {} to table {}", column->name, table->name); if ((column->mode != NULL) && (std::strcmp(column->mode, "&&") == 0)) { if (!(sql->query(column->extra_cmd))) { eventlog(eventlog_level_info, __FUNCTION__, "sucessfully issued: {} {}", column->mode, column->extra_cmd); } } /* std::sscanf(column->name,"%s",_column); std::sprintf(query,"ALTER TABLE %s ALTER %s SET DEFAULT %s",table->name,_column,column->value); // If failed, try alternate language. (From ZSoft for sql_odbc.) if(sql->query(query)) { std::sprintf(query,"ALTER TABLE %s ADD DEFAULT %s FOR %s",table->name,column->value,_column); sql->query(query); } // ALTER TABLE BNET add default 'false' for auth_admin; */ } else { if ((column->mode != NULL) && (std::strcmp(column->mode, "||") == 0)) { if (!(sql->query(column->extra_cmd))) { eventlog(eventlog_level_info, __FUNCTION__, "sucessfully issued: {} {}", column->mode, column->extra_cmd); } } } } for (sqlcmd = table_get_first_sql_command(table); sqlcmd; sqlcmd = table_get_next_sql_command(table)) { if (!(sql->query(sqlcmd->sql_command))) { eventlog(eventlog_level_info, __FUNCTION__, "sucessfully issued: {}", sqlcmd->sql_command); if ((sqlcmd->mode != NULL) && (std::strcmp(sqlcmd->mode, "&&") == 0)) { if (!(sql->query(sqlcmd->extra_cmd))) { eventlog(eventlog_level_info, __FUNCTION__, "sucessfully issued: {} {}", sqlcmd->mode, sqlcmd->extra_cmd); } } } else { if ((sqlcmd->mode != NULL) && (std::strcmp(sqlcmd->mode, "||") == 0)) { if (!(sql->query(sqlcmd->extra_cmd))) { eventlog(eventlog_level_info, __FUNCTION__, "sucessfully issued: {} {}", sqlcmd->mode, sqlcmd->extra_cmd); } } } } column = table_get_first_column(table); std::sscanf(column->name, "%s", _column); //get column name without format infos std::sprintf(query, "INSERT INTO %s (%s) VALUES (%s)", table->name, _column, column->value); if (!(sql->query(query))) { eventlog(eventlog_level_info, __FUNCTION__, "added missing default account to table {}", table->name); } } dispose_db_layout(db_layout); eventlog(eventlog_level_info, __FUNCTION__, "finished adding missing tables and columns"); return 0; } static void sql_escape_command(char *escape, const char *from, int len) { if (from != NULL && len != 0) /* make sure we have a command */ { char * tmp1 = xstrdup(from); /* copy of 'from' */ char * tmp2 = escape; char * tmp3 = NULL; /* begining of string to be escaped */ char * tmp4 = (char *)xmalloc(std::strlen(tmp1) * 2); /* escaped string */ unsigned int i, j; /* eventlog(eventlog_level_trace,__FUNCTION__,"COMMAND: {}",tmp1); */ for (i = 0; tmp1[i] && i < len; i++, tmp2++) { *tmp2 = tmp1[i]; /* copy 'from' to 'escape' */ if (tmp1[i] == '\'') /* check if we find a string by checking for a single quote (') */ { tmp3 = &tmp1[++i]; /* set tmp3 to the begining of the string to be escaped */ for (; tmp1[i] && tmp1[i] != '\'' && i < len; i++); /* find the end of the string to be escaped */ tmp1[i] = '\0'; /* set end of string with null terminator */ /* eventlog(eventlog_level_trace,__FUNCTION__,"STRING: {}",tmp3); */ sql->escape_string(tmp4, tmp3, std::strlen(tmp3)); /* escape the string */ /* eventlog(eventlog_level_trace,__FUNCTION__,"ESCAPE STRING: {}",tmp4); */ for (j = 0, tmp2++; tmp4[j]; j++, tmp2++) *tmp2 = tmp4[j]; /* add 'escaped string' to 'escape' */ *tmp2 = '\''; /* add single quote to end after adding 'escaped string' */ } } *tmp2 = '\0'; /* eventlog(eventlog_level_trace,__FUNCTION__,"ESCAPED COMMAND: {}",escape); */ xfree(tmp1); xfree(tmp4); } } } } #endif /* WITH_SQL */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_dbcreator.h000066400000000000000000000035011462640461400210570ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SQL_DBCREATOR_TYPES #define INCLUDED_SQL_DBCRETAOR_TYPES #include "common/list.h" namespace pvpgn { namespace bnetd { typedef struct sqlcommand #ifdef SQL_DBCREATOR_INTERNAL_ACCESS { char * sql_command; char * mode; char * extra_cmd; } #endif t_sqlcommand; typedef struct column #ifdef SQL_DBCREATOR_INTERNAL_ACCESS { char * name; char * value; char * mode; char * extra_cmd; } #endif t_column; typedef struct table #ifdef SQL_DBCREATOR_INTERNAL_ACCESS { char * name; t_list * columns; t_list * sql_commands; } #endif t_table; typedef struct db_layout #ifdef SQL_DBCREATOR_INTERNAL_ACCESS { t_list * tables; } #endif t_db_layout; } } #endif /* INCLUDED_SQL_DBCREATOR_TYPES */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_SQL_DBCREATOR_PROTOS #define INCLUDED_SQL_DBCRETAOR_PROTOS #ifndef SQL_INTERNAL #define SQL_INTERNAL 1 #endif # include "sql_common.h" #undef SQL_INTERNAL namespace pvpgn { namespace bnetd { int sql_dbcreator(t_sql_engine * sql); } } #endif /* INCLUDED_SQL_DBCREATOR_PROTOS */ #endif /* JUST_NEED_TYPES */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_mysql.cpp000066400000000000000000000267041462640461400206240ustar00rootroot00000000000000/* * Copyright (C) 2002,2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WITH_SQL_MYSQL #include "common/setup_before.h" #define NO_CLIENT_LONG_LONG #include "sql_mysql.h" #ifdef HAVE_WINDOWS_H #include #endif #include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "storage_sql.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static int sql_mysql_init(const char *, const char *, const char *, const char *, const char *, const char *); static int sql_mysql_close(void); static t_sql_res * sql_mysql_query_res(const char *); static int sql_mysql_query(const char *); static t_sql_row * sql_mysql_fetch_row(t_sql_res *); static void sql_mysql_free_result(t_sql_res *); static unsigned int sql_mysql_num_rows(t_sql_res *); static unsigned int sql_mysql_num_fields(t_sql_res *); static unsigned int sql_mysql_affected_rows(void); static t_sql_field * sql_mysql_fetch_fields(t_sql_res *); static int sql_mysql_free_fields(t_sql_field *); static void sql_mysql_escape_string(char *, const char *, int); t_sql_engine sql_mysql = { sql_mysql_init, sql_mysql_close, sql_mysql_query_res, sql_mysql_query, sql_mysql_fetch_row, sql_mysql_free_result, sql_mysql_num_rows, sql_mysql_num_fields, sql_mysql_affected_rows, sql_mysql_fetch_fields, sql_mysql_free_fields, sql_mysql_escape_string }; static MYSQL *mysql = NULL; #ifndef RUNTIME_LIBS #define p_mysql_affected_rows mysql_affected_rows #define p_mysql_close mysql_close #define p_mysql_error mysql_error #define p_mysql_fetch_fields mysql_fetch_fields #define p_mysql_fetch_row mysql_fetch_row #define p_mysql_free_result mysql_free_result #define p_mysql_init mysql_init #define p_mysql_num_fields mysql_num_fields #define p_mysql_num_rows mysql_num_rows #define p_mysql_query mysql_query #define p_mysql_real_connect mysql_real_connect #define p_mysql_real_escape_string mysql_real_escape_string #define p_mysql_store_result mysql_store_result #else /* RUNTIME_LIBS */ static int mysql_load_dll(void); typedef my_ulonglong(STDCALL *f_mysql_affected_rows)(MYSQL*); typedef void (STDCALL *f_mysql_close)(MYSQL*); typedef const char* (STDCALL *f_mysql_error)(MYSQL*); typedef MYSQL_FIELD* (STDCALL *f_mysql_fetch_fields)(MYSQL_RES*); typedef MYSQL_ROW(STDCALL *f_mysql_fetch_row)(MYSQL_RES*); typedef my_bool(STDCALL *f_mysql_free_result)(MYSQL_RES*); typedef MYSQL* (STDCALL *f_mysql_init)(MYSQL*); typedef unsigned int (STDCALL *f_mysql_num_fields)(MYSQL_RES*); typedef my_ulonglong(STDCALL *f_mysql_num_rows)(MYSQL_RES*); typedef int (STDCALL *f_mysql_query)(MYSQL*, const char*); typedef MYSQL* (STDCALL *f_mysql_real_connect)(MYSQL*, const char*, const char*, const char*, const char*, unsigned int, const char*, unsigned long); typedef unsigned long (STDCALL *f_mysql_real_escape_string)(MYSQL*, char*, const char*, unsigned long); typedef MYSQL_RES* (STDCALL *f_mysql_store_result)(MYSQL*); static f_mysql_affected_rows p_mysql_affected_rows; static f_mysql_close p_mysql_close; static f_mysql_error p_mysql_error; static f_mysql_fetch_fields p_mysql_fetch_fields; static f_mysql_fetch_row p_mysql_fetch_row; static f_mysql_free_result p_mysql_free_result; static f_mysql_init p_mysql_init; static f_mysql_num_fields p_mysql_num_fields; static f_mysql_num_rows p_mysql_num_rows; static f_mysql_query p_mysql_query; static f_mysql_real_connect p_mysql_real_connect; static f_mysql_real_escape_string p_mysql_real_escape_string; static f_mysql_store_result p_mysql_store_result; #include "compat/runtime_libs.h" /* defines OpenLibrary(), GetFunction(), CloseLibrary() & MYSQL_LIB */ static void * handle = NULL; static int mysql_load_dll(void) { if ((handle = OpenLibrary(MYSQL_LIB)) == NULL) return -1; if (((p_mysql_affected_rows = (f_mysql_affected_rows)GetFunction(handle, "mysql_affected_rows")) == NULL) || ((p_mysql_close = (f_mysql_close)GetFunction(handle, "mysql_close")) == NULL) || ((p_mysql_error = (f_mysql_error)GetFunction(handle, "mysql_error")) == NULL) || ((p_mysql_fetch_fields = (f_mysql_fetch_fields)GetFunction(handle, "mysql_fetch_fields")) == NULL) || ((p_mysql_fetch_row = (f_mysql_fetch_row)GetFunction(handle, "mysql_fetch_row")) == NULL) || ((p_mysql_free_result = (f_mysql_free_result)GetFunction(handle, "mysql_free_result")) == NULL) || ((p_mysql_init = (f_mysql_init)GetFunction(handle, "mysql_init")) == NULL) || ((p_mysql_num_fields = (f_mysql_num_fields)GetFunction(handle, "mysql_num_fields")) == NULL) || ((p_mysql_num_rows = (f_mysql_num_rows)GetFunction(handle, "mysql_num_rows")) == NULL) || ((p_mysql_query = (f_mysql_query)GetFunction(handle, "mysql_query")) == NULL) || ((p_mysql_real_connect = (f_mysql_real_connect)GetFunction(handle, "mysql_real_connect")) == NULL) || ((p_mysql_real_escape_string = (f_mysql_real_escape_string)GetFunction(handle, "mysql_real_escape_string")) == NULL) || ((p_mysql_store_result = (f_mysql_store_result)GetFunction(handle, "mysql_store_result")) == NULL)) { CloseLibrary(handle); handle = NULL; return -1; } return 0; } #endif /* RUNTIME_LIBS */ static int sql_mysql_init(const char *host, const char *port, const char *socket, const char *name, const char *user, const char *pass) { if (name == NULL || user == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL parameter"); return -1; } #ifdef RUNTIME_LIBS if (mysql_load_dll()) { eventlog(eventlog_level_error, __FUNCTION__, "error loading library file \"{}\"", MYSQL_LIB); return -1; } #endif if ((mysql = p_mysql_init(NULL)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got error from mysql_init"); return -1; } #if MYSQL_VERSION_ID >= 50003 #if MYSQL_VERSION_ID < 50013 eventlog(eventlog_level_warn, __FUNCTION__, "Your mySQL client lib version does not support reconnecting after a timeout."); eventlog(eventlog_level_warn, __FUNCTION__, "If this causes you any trouble you are advices to upgrade"); eventlog(eventlog_level_warn, __FUNCTION__, "your mySQL client libs to at least mySQL 5.0.13 to resolve this problem."); // we might try a dirty hack like the following, but I'm not sure if it will work // mysql->reconnect = 1; #endif #if MYSQL_VERSION_ID >= 50019 my_bool my_true = true; if (mysql_options(mysql, MYSQL_OPT_RECONNECT, &my_true)){ eventlog(eventlog_level_warn, __FUNCTION__, "Failed to turn on MYSQL_OPT_RECONNECT."); } else{ eventlog(eventlog_level_info, __FUNCTION__, "Successfully turned on MYSQL_OPT_RECONNECT."); } #endif #endif if (p_mysql_real_connect(mysql, host, user, pass, name, port ? atoi(port) : 0, socket, CLIENT_FOUND_ROWS) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "error connecting to database (db said: '{}')", p_mysql_error(mysql)); p_mysql_close(mysql); return -1; } #if MYSQL_VERSION_ID >= 50013 #if MYSQL_VERSION_ID < 50019 my_bool my_true = true; if (mysql_options(mysql, MYSQL_OPT_RECONNECT, &my_true)){ eventlog(eventlog_level_warn, __FUNCTION__, "Failed to turn on MYSQL_OPT_RECONNECT."); } else{ eventlog(eventlog_level_info, __FUNCTION__, "Successfully turned on MYSQL_OPT_RECONNECT."); } #endif #endif /* allows identifers (specificly column names) to be quoted using double quotes (") in addition to ticks (`) */ sql_mysql_query("SET sql_mode='ANSI_QUOTES'"); return 0; } static int sql_mysql_close(void) { if (mysql) { p_mysql_close(mysql); mysql = NULL; } #ifdef RUNTIME_LIBS if (handle) { CloseLibrary(handle); handle = NULL; } #endif return 0; } static t_sql_res * sql_mysql_query_res(const char * query) { t_sql_res *res; if (mysql == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "mysql driver not initilized"); return NULL; } if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL query"); return NULL; } if (p_mysql_query(mysql, query)) { // eventlog(eventlog_level_debug, __FUNCTION__, "got error from query ({})", query); return NULL; } res = p_mysql_store_result(mysql); if (res == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got error from store result"); return NULL; } return res; } static int sql_mysql_query(const char * query) { if (mysql == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "mysql driver not initilized"); return -1; } if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL query"); return -1; } return p_mysql_query(mysql, query) == 0 ? 0 : -1; } static t_sql_row * sql_mysql_fetch_row(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return NULL; } return p_mysql_fetch_row((MYSQL_RES *)result); } static void sql_mysql_free_result(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return; } p_mysql_free_result((MYSQL_RES *)result); } static unsigned int sql_mysql_num_rows(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return 0; } return p_mysql_num_rows((MYSQL_RES *)result); } static unsigned int sql_mysql_num_fields(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return 0; } return p_mysql_num_fields((MYSQL_RES *)result); } static unsigned int sql_mysql_affected_rows(void) { return p_mysql_affected_rows(mysql); } static t_sql_field * sql_mysql_fetch_fields(t_sql_res *result) { MYSQL_FIELD *fields; unsigned fieldno, i; t_sql_field *rfields; if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return NULL; } fieldno = p_mysql_num_fields((MYSQL_RES *)result); fields = p_mysql_fetch_fields((MYSQL_RES *)result); rfields = (t_sql_field *)xmalloc(sizeof(t_sql_field)* (fieldno + 1)); for (i = 0; i < fieldno; i++) rfields[i] = fields[i].name; rfields[i] = NULL; return rfields; } static int sql_mysql_free_fields(t_sql_field *fields) { if (fields == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL fields"); return -1; } xfree((void*)fields); return 0; /* mysql_free_result() should free the rest properly */ } static void sql_mysql_escape_string(char *escape, const char *from, int len) { if (mysql == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "mysql driver not initilized"); return; } p_mysql_real_escape_string(mysql, escape, from, len); } } } #endif /* WITH_SQL_MYSQL */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_mysql.h000066400000000000000000000017741462640461400202710ustar00rootroot00000000000000/* * Copyright (C) 2002,2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SQL_MYSQL_PROTOS #define INCLUDED_SQL_MYSQL_PROTOS #define JUST_NEED_TYPES # include "sql_common.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern t_sql_engine sql_mysql; } } #endif /* INCLUDED_SQL_MYSQL_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_odbc.cpp000066400000000000000000000356661462640461400203750ustar00rootroot00000000000000/* * Copyright (C) 2005 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WITH_SQL_ODBC #include "common/setup_before.h" #ifdef HAVE_WINDOWS_H # include #endif #include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "common/xstring.h" #include "storage_sql.h" #include "sql_odbc.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { /* t_sql_engine Interface methods. */ static int sql_odbc_init(const char *, const char *, const char *, const char *, const char *, const char *); static int sql_odbc_close(void); static t_sql_res * sql_odbc_query_res(const char *); static int sql_odbc_query(const char *); static t_sql_row * sql_odbc_fetch_row(t_sql_res *); static void sql_odbc_free_result(t_sql_res *); static unsigned int sql_odbc_num_rows(t_sql_res *); static unsigned int sql_odbc_affected_rows(void); static unsigned int sql_odbc_num_fields(t_sql_res *); static t_sql_field * sql_odbc_fetch_fields(t_sql_res *); static int sql_odbc_free_fields(t_sql_field *); static void sql_odbc_escape_string(char *, const char *, int); t_sql_engine sql_odbc = { sql_odbc_init, sql_odbc_close, sql_odbc_query_res, sql_odbc_query, sql_odbc_fetch_row, sql_odbc_free_result, sql_odbc_num_rows, sql_odbc_num_fields, sql_odbc_affected_rows, sql_odbc_fetch_fields, sql_odbc_free_fields, sql_odbc_escape_string }; struct t_odbc_rowSet_{ t_sql_row *row; struct t_odbc_rowSet_ *next; }; typedef struct t_odbc_rowSet_ t_odbc_rowSet; typedef struct{ HSTMT stmt; t_odbc_rowSet *curRow; t_odbc_rowSet *rowSet; unsigned int rowCount; } t_odbc_res; static t_sql_row *odbc_alloc_row(t_odbc_res *result, SQLINTEGER *sizes); static t_odbc_rowSet *odbc_alloc_rowSet(); static int odbc_Result(SQLRETURN retCode); static void odbc_Error(SQLSMALLINT type, void *obj, t_eventlog_level level, const char *function); static int odbc_Fail(); static HENV env = SQL_NULL_HENV; static HDBC con = SQL_NULL_HDBC; static SQLINTEGER ROWCOUNT = 0; #ifndef RUNTIME_LIBS #define p_SQLAllocEnv SQLAllocEnv #define p_SQLAllocConnect SQLAllocConnect #define p_SQLAllocStmt SQLAllocStmt #define p_SQLBindCol SQLBindCol #define p_SQLColAttribute SQLColAttributeA #define p_SQLConnect SQLConnectA #define p_SQLDisconnect SQLDisconnect #define p_SQLExecDirect SQLExecDirectA #define p_SQLFetch SQLFetch #define p_SQLFreeHandle SQLFreeHandle #define p_SQLRowCount SQLRowCount #define p_SQLGetDiagRec SQLGetDiagRecA #define p_SQLNumResultCols SQLNumResultCols #else /* RUNTIME_LIBS */ static int odbc_load_dll(void); typedef SQLRETURN(SQL_API *f_SQLAllocEnv)(SQLHENV*); typedef SQLRETURN(SQL_API *f_SQLAllocConnect)(SQLHENV, SQLHDBC*); typedef SQLRETURN(SQL_API *f_SQLAllocStmt)(SQLHDBC, SQLHSTMT*); typedef SQLRETURN(SQL_API *f_SQLBindCol)(SQLHSTMT, SQLUSMALLINT, SQLSMALLINT, SQLPOINTER, SQLLEN, SQLLEN*); typedef SQLRETURN(SQL_API *f_SQLColAttribute)(SQLHSTMT, SQLUSMALLINT, SQLUSMALLINT, SQLPOINTER, SQLSMALLINT, SQLSMALLINT*, SQLPOINTER); typedef SQLRETURN(SQL_API *f_SQLConnect)(SQLHDBC, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT, SQLCHAR*, SQLSMALLINT); typedef SQLRETURN(SQL_API *f_SQLDisconnect)(SQLHDBC); typedef SQLRETURN(SQL_API *f_SQLExecDirect)(SQLHSTMT, SQLCHAR*, SQLINTEGER); typedef SQLRETURN(SQL_API *f_SQLFetch)(SQLHSTMT); typedef SQLRETURN(SQL_API *f_SQLFreeHandle)(SQLSMALLINT, SQLHANDLE); typedef SQLRETURN(SQL_API *f_SQLRowCount)(SQLHSTMT, SQLLEN*); typedef SQLRETURN(SQL_API *f_SQLGetDiagRec)(SQLSMALLINT, SQLHANDLE, SQLSMALLINT, SQLCHAR*, SQLINTEGER*, SQLCHAR*, SQLSMALLINT, SQLSMALLINT*); typedef SQLRETURN(SQL_API *f_SQLNumResultCols)(SQLHSTMT, SQLSMALLINT*); static f_SQLAllocEnv p_SQLAllocEnv; static f_SQLAllocConnect p_SQLAllocConnect; static f_SQLAllocStmt p_SQLAllocStmt; static f_SQLBindCol p_SQLBindCol; static f_SQLColAttribute p_SQLColAttribute; static f_SQLConnect p_SQLConnect; static f_SQLDisconnect p_SQLDisconnect; static f_SQLExecDirect p_SQLExecDirect; static f_SQLFetch p_SQLFetch; static f_SQLFreeHandle p_SQLFreeHandle; static f_SQLRowCount p_SQLRowCount; static f_SQLGetDiagRec p_SQLGetDiagRec; static f_SQLNumResultCols p_SQLNumResultCols; #include "compat/runtime_libs.h" /* defines OpenLibrary(), GetFunction(), CloseLibrary() & ODBC_LIB */ static void * handle = NULL; static int odbc_load_dll(void) { if ((handle = OpenLibrary(ODBC_LIB)) == NULL) return -1; if (((p_SQLAllocEnv = (f_SQLAllocEnv)GetFunction(handle, "SQLAllocEnv")) == NULL) || ((p_SQLAllocConnect = (f_SQLAllocConnect)GetFunction(handle, "SQLAllocConnect")) == NULL) || ((p_SQLAllocStmt = (f_SQLAllocStmt)GetFunction(handle, "SQLAllocStmt")) == NULL) || ((p_SQLBindCol = (f_SQLBindCol)GetFunction(handle, "SQLBindCol")) == NULL) || ((p_SQLColAttribute = (f_SQLColAttribute)GetFunction(handle, "SQLColAttribute")) == NULL) || ((p_SQLConnect = (f_SQLConnect)GetFunction(handle, "SQLConnect")) == NULL) || ((p_SQLDisconnect = (f_SQLDisconnect)GetFunction(handle, "SQLDisconnect")) == NULL) || ((p_SQLExecDirect = (f_SQLExecDirect)GetFunction(handle, "SQLExecDirect")) == NULL) || ((p_SQLFetch = (f_SQLFetch)GetFunction(handle, "SQLFetch")) == NULL) || ((p_SQLFreeHandle = (f_SQLFreeHandle)GetFunction(handle, "SQLFreeHandle")) == NULL) || ((p_SQLRowCount = (f_SQLRowCount)GetFunction(handle, "SQLRowCount")) == NULL) || ((p_SQLGetDiagRec = (f_SQLGetDiagRec)GetFunction(handle, "SQLGetDiagRec")) == NULL) || ((p_SQLNumResultCols = (f_SQLNumResultCols)GetFunction(handle, "SQLNumResultCols")) == NULL)) { CloseLibrary(handle); handle = NULL; return -1; } return 0; } #endif /************************************************ t_sql_engine Interface methods. ************************************************/ static int sql_odbc_init(const char *host, const char *port, const char *socket, const char *name, const char *user, const char *pass) { #ifdef RUNTIME_LIBS if (odbc_load_dll()) { eventlog(eventlog_level_error, __FUNCTION__, "error loading library file \"{}\"", ODBC_LIB); return -1; } #endif /* Create environment. */ if (odbc_Result(p_SQLAllocEnv(&env)) && odbc_Result(p_SQLAllocConnect(env, &con))) { eventlog(eventlog_level_debug, __FUNCTION__, "Created ODBC environment."); } else { eventlog(eventlog_level_error, __FUNCTION__, "Unable to allocate ODBC environment."); return odbc_Fail(); } if (odbc_Result(p_SQLConnect(con, (SQLCHAR*)name, SQL_NTS, (SQLCHAR*)user, SQL_NTS, (SQLCHAR*)pass, SQL_NTS))) { eventlog(eventlog_level_debug, __FUNCTION__, "Connected to ODBC datasource \"{}\".", name); } else { eventlog(eventlog_level_error, __FUNCTION__, "Unable to connect to ODBC datasource \"{}\".", name); odbc_Error(SQL_HANDLE_DBC, con, eventlog_level_error, __FUNCTION__); return odbc_Fail(); } return 0; } static int sql_odbc_close(void) { if (con) { p_SQLDisconnect(con); p_SQLFreeHandle(SQL_HANDLE_DBC, con); con = NULL; } if (env) { p_SQLFreeHandle(SQL_HANDLE_ENV, env); env = NULL; } #ifdef RUNTIME_LIBS if (handle) { CloseLibrary(handle); handle = NULL; } #endif eventlog(eventlog_level_debug, __FUNCTION__, "ODBC connection closed."); return 0; } static t_sql_res* sql_odbc_query_res(const char *query) { t_odbc_res *res = NULL; HSTMT stmt = SQL_NULL_HSTMT; SQLRETURN result = 0; t_odbc_rowSet *rowSet = NULL; ROWCOUNT = 0; /* Validate query. */ if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "Got a NULL query!"); return NULL; } // eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); /* Run query and check for success. */ p_SQLAllocStmt(con, &stmt); result = p_SQLExecDirect(stmt, (SQLCHAR*)query, SQL_NTS); if (!odbc_Result(result)) { // odbc_Error(SQL_HANDLE_STMT, stmt, eventlog_level_debug, __FUNCTION__); p_SQLFreeHandle(SQL_HANDLE_STMT, stmt); return NULL; } /* Create a result. */ res = (t_odbc_res *)xmalloc(sizeof *res); res->stmt = stmt; rowSet = odbc_alloc_rowSet(); res->rowSet = rowSet; res->curRow = rowSet; res->rowCount = 0; /* Store rows. */ do { SQLINTEGER *sizes = NULL; t_sql_row *row = odbc_alloc_row(res, sizes); if (!row) { sql_odbc_free_result(res); return NULL; } result = p_SQLFetch(stmt); if (odbc_Result(result)) { rowSet->row = row; rowSet->next = odbc_alloc_rowSet(); rowSet = rowSet->next; res->rowCount++; } else { sql_odbc_free_fields(row); } if (sizes) xfree(sizes); } while (odbc_Result(result)); ROWCOUNT = res->rowCount; if (result == SQL_NO_DATA_FOUND) { return res; } else if (!odbc_Result(result)) { eventlog(eventlog_level_error, __FUNCTION__, "Unable to fetch row - ODBC error {}.", result); odbc_Error(SQL_HANDLE_STMT, stmt, eventlog_level_error, __FUNCTION__); sql_odbc_free_result(res); return NULL; } return res; } static int sql_odbc_query(const char *query) { int result; HSTMT stmt = SQL_NULL_HSTMT; /* Validate query. */ if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "Got a NULL query!"); return -1; } // eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); p_SQLAllocStmt(con, &stmt); result = odbc_Result(p_SQLExecDirect(stmt, (SQLCHAR*)query, SQL_NTS)); if (result) { p_SQLRowCount(stmt, &ROWCOUNT); } else { // odbc_Error(SQL_HANDLE_STMT, stmt, eventlog_level_trace, __FUNCTION__); } p_SQLFreeHandle(SQL_HANDLE_STMT, stmt); result = result == -1 ? 0 : -1; return result; } static t_sql_row* sql_odbc_fetch_row(t_sql_res *result) { t_odbc_res *res; t_sql_row *row; if (!result) { eventlog(eventlog_level_error, __FUNCTION__, "Got NULL result."); return NULL; } res = (t_odbc_res*)result; row = res->curRow->row; if (row) { res->curRow = res->curRow->next; } return row; } static void sql_odbc_free_result(t_sql_res *result) { if (result) { t_odbc_res *res = (t_odbc_res*)result; if (res->stmt) { p_SQLFreeHandle(SQL_HANDLE_STMT, res->stmt); res->stmt = NULL; } if (res->rowSet) { t_odbc_rowSet *rowSet = res->rowSet; while (rowSet) { t_odbc_rowSet *temp = (rowSet->next); sql_odbc_free_fields(rowSet->row); xfree(rowSet); rowSet = temp; } } xfree(result); } } static unsigned int sql_odbc_num_rows(t_sql_res *result) { if (!result) { eventlog(eventlog_level_error, __FUNCTION__, "Got NULL result."); return 0; } return ((t_odbc_res*)result)->rowCount; } static unsigned int sql_odbc_num_fields(t_sql_res *result) { SQLSMALLINT numCols = 0; if (!result) { eventlog(eventlog_level_error, __FUNCTION__, "Got NULL result."); } else { p_SQLNumResultCols(((t_odbc_res*)result)->stmt, &numCols); } return numCols; } static unsigned int sql_odbc_affected_rows(void) { return ROWCOUNT; } static t_sql_field* sql_odbc_fetch_fields(t_sql_res *result) { int i; t_odbc_res *res; int fieldCount; t_sql_field *fields; if (!result) { eventlog(eventlog_level_error, __FUNCTION__, "Got NULL result."); return NULL; } res = (t_odbc_res *)result; fieldCount = sql_odbc_num_fields(result); fields = (t_sql_field *)xcalloc(sizeof *fields, fieldCount + 1); if (!fields) { return NULL; } fields[fieldCount] = NULL; for (i = 0; i < fieldCount; i++) { SQLSMALLINT fNameSz; p_SQLColAttribute(res->stmt, i + 1, SQL_DESC_NAME, NULL, 0, &fNameSz, NULL); char* fName = (char*)xmalloc(fNameSz); if (!fName) { return NULL; } p_SQLColAttribute(res->stmt, i + 1, SQL_DESC_NAME, fName, fNameSz, &fNameSz, NULL); char* tmp = fName; for (; *tmp; ++tmp) { *tmp = safe_toupper(*tmp); } fields[i] = fName; } return fields; } static int sql_odbc_free_fields(t_sql_field *fields) { if (fields) { int i = 0; while (fields[i]) { xfree(fields[i]); i++; } xfree(fields); } return 0; } static void sql_odbc_escape_string(char *escape, const char *from, int len) { int i; for (i = 0; i < len && from[i] != 0; i++) { if (from[i] == '\'') { escape[i] = '"'; } else { escape[i] = from[i]; } } escape[i] = 0; } /************************************************ End t_sql_engine Interface methods. ************************************************/ static t_sql_row* odbc_alloc_row(t_odbc_res *result, SQLINTEGER *sizes) { int i, fieldCount; HSTMT stmt = result->stmt; t_sql_row *row; fieldCount = sql_odbc_num_fields(result); /* Create a new row. */ row = (t_sql_row *)xcalloc(sizeof *row, fieldCount + 1); if (!row) { return NULL; } row[fieldCount] = NULL; sizes = (SQLINTEGER *)xcalloc(sizeof *sizes, fieldCount); for (i = 0; i < fieldCount; i++) { SQLLEN cellSz; p_SQLColAttribute(stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &cellSz); char* cell = (char*)xcalloc(sizeof *cell, ++cellSz); if (!cell) { return NULL; } p_SQLBindCol(stmt, i + 1, SQL_C_CHAR, cell, cellSz, &sizes[i]); row[i] = cell; } return row; } static t_odbc_rowSet* odbc_alloc_rowSet() { t_odbc_rowSet *rowSet = (t_odbc_rowSet *)xmalloc(sizeof *rowSet); rowSet->row = NULL; rowSet->next = NULL; return rowSet; } static void odbc_Error(SQLSMALLINT type, void *obj, t_eventlog_level level, const char *function) { SQLCHAR mState[6] = "\0"; long native = 0; SQLSMALLINT mTextLen; short i = 0; while (p_SQLGetDiagRec(type, obj, ++i, NULL, NULL, NULL, 0, &mTextLen) != SQL_NO_DATA) { SQLCHAR *mText = (SQLCHAR *)xcalloc(sizeof *mText, ++mTextLen); p_SQLGetDiagRec(type, obj, i, mState, &native, mText, mTextLen, NULL); eventlog(level, function, "ODBC Error: State {}, Native {}: {}", mState, native, mText); xfree(mText); } } static int odbc_Result(SQLRETURN retCode) { int result = 0; if (retCode == SQL_SUCCESS || retCode == SQL_SUCCESS_WITH_INFO) { result = -1; } return result; } static int odbc_Fail() { sql_odbc_close(); return -1; } } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_odbc.h000066400000000000000000000020151462640461400200200ustar00rootroot00000000000000/* * Copyright (C) 2005 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SQL_ODBC_PROTOS #define INCLUDED_SQL_ODBC_PROTOS #define JUST_NEED_TYPES # include "sql_common.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern t_sql_engine sql_odbc; } } #endif /* INCLUDED_SQL_ODBC_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_pgsql.cpp000066400000000000000000000260771462640461400206100ustar00rootroot00000000000000/* * Copyright (C) 2002,2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WITH_SQL_PGSQL #include "common/setup_before.h" #include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "storage_sql.h" #include "sql_pgsql.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static int sql_pgsql_init(const char *, const char *, const char *, const char *, const char *, const char *); static int sql_pgsql_close(void); static t_sql_res * sql_pgsql_query_res(const char *); static int sql_pgsql_query(const char *); static t_sql_row * sql_pgsql_fetch_row(t_sql_res *); static void sql_pgsql_free_result(t_sql_res *); static unsigned int sql_pgsql_num_rows(t_sql_res *); static unsigned int sql_pgsql_num_fields(t_sql_res *); static unsigned int sql_pgsql_affected_rows(void); static t_sql_field * sql_pgsql_fetch_fields(t_sql_res *); static int sql_pgsql_free_fields(t_sql_field *); static void sql_pgsql_escape_string(char *, const char *, int); t_sql_engine sql_pgsql = { sql_pgsql_init, sql_pgsql_close, sql_pgsql_query_res, sql_pgsql_query, sql_pgsql_fetch_row, sql_pgsql_free_result, sql_pgsql_num_rows, sql_pgsql_num_fields, sql_pgsql_affected_rows, sql_pgsql_fetch_fields, sql_pgsql_free_fields, sql_pgsql_escape_string }; static PGconn *pgsql = NULL; static unsigned int lastarows = 0; typedef struct { int crow; char ** rowbuf; PGresult *pgres; } t_pgsql_res; #ifndef RUNTIME_LIBS #define p_PQclear PQclear #define p_PQcmdTuples PQcmdTuples #define p_PQerrorMessage PQerrorMessage #define p_PQescapeString PQescapeString #define p_PQexec PQexec #define p_PQfinish PQfinish #define p_PQfname PQfname #define p_PQgetvalue PQgetvalue #define p_PQnfields PQnfields #define p_PQntuples PQntuples #define p_PQresultStatus PQresultStatus #define p_PQsetdbLogin PQsetdbLogin #define p_PQstatus PQstatus #else /* RUNTIME_LIBS */ static int pgsql_load_dll(void); typedef void(*f_PQclear)(PGresult*); typedef char* (*f_PQcmdTuples)(PGresult*); typedef char* (*f_PQerrorMessage)(const PGconn*); typedef size_t(*f_PQescapeString)(char*, const char*, size_t); typedef PGresult* (*f_PQexec)(PGconn*, const char*); typedef void(*f_PQfinish)(PGconn*); typedef char* (*f_PQfname)(const PGresult*, int); typedef char* (*f_PQgetvalue)(const PGresult*, int, int); typedef int(*f_PQnfields)(const PGresult*); typedef int(*f_PQntuples)(const PGresult*); typedef ExecStatusType(*f_PQresultStatus)(const PGresult*); typedef PGconn* (*f_PQsetdbLogin)(const char*, const char*, const char*, const char*, const char*, const char*, const char*); typedef ConnStatusType(*f_PQstatus)(const PGconn*); static f_PQclear p_PQclear; static f_PQcmdTuples p_PQcmdTuples; static f_PQerrorMessage p_PQerrorMessage; static f_PQescapeString p_PQescapeString; static f_PQexec p_PQexec; static f_PQfinish p_PQfinish; static f_PQfname p_PQfname; static f_PQgetvalue p_PQgetvalue; static f_PQnfields p_PQnfields; static f_PQntuples p_PQntuples; static f_PQresultStatus p_PQresultStatus; static f_PQsetdbLogin p_PQsetdbLogin; static f_PQstatus p_PQstatus; #include "compat/runtime_libs.h" /* defines OpenLibrary(), GetFunction(), CloseLibrary() & PGSQL_LIB */ static void * handle = NULL; static int pgsql_load_dll(void) { if ((handle = OpenLibrary(PGSQL_LIB)) == NULL) return -1; if (((p_PQclear = (f_PQclear)GetFunction(handle, "PQclear")) == NULL) || ((p_PQcmdTuples = (f_PQcmdTuples)GetFunction(handle, "PQcmdTuples")) == NULL) || ((p_PQerrorMessage = (f_PQerrorMessage)GetFunction(handle, "PQerrorMessage")) == NULL) || ((p_PQescapeString = (f_PQescapeString)GetFunction(handle, "PQescapeString")) == NULL) || ((p_PQexec = (f_PQexec)GetFunction(handle, "PQexec")) == NULL) || ((p_PQfinish = (f_PQfinish)GetFunction(handle, "PQfinish")) == NULL) || ((p_PQfname = (f_PQfname)GetFunction(handle, "PQfname")) == NULL) || ((p_PQgetvalue = (f_PQgetvalue)GetFunction(handle, "PQgetvalue")) == NULL) || ((p_PQnfields = (f_PQnfields)GetFunction(handle, "PQnfields")) == NULL) || ((p_PQntuples = (f_PQntuples)GetFunction(handle, "PQntuples")) == NULL) || ((p_PQresultStatus = (f_PQresultStatus)GetFunction(handle, "PQresultStatus")) == NULL) || ((p_PQsetdbLogin = (f_PQsetdbLogin)GetFunction(handle, "PQsetdbLogin")) == NULL) || ((p_PQstatus = (f_PQstatus)GetFunction(handle, "PQstatus")) == NULL)) { CloseLibrary(handle); handle = NULL; return -1; } return 0; } #endif static int sql_pgsql_init(const char *host, const char *port, const char *socket, const char *name, const char *user, const char *pass) { const char *tmphost; if (name == NULL || user == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL parameter"); return -1; } tmphost = host != NULL ? host : socket; #ifdef RUNTIME_LIBS if (pgsql_load_dll()) { eventlog(eventlog_level_error, __FUNCTION__, "error loading library file \"{}\"", PGSQL_LIB); return -1; } #endif if ((pgsql = p_PQsetdbLogin(host, port, NULL, NULL, name, user, pass)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "not enougn memory for new pgsql connection"); return -1; } if (p_PQstatus(pgsql) != CONNECTION_OK) { eventlog(eventlog_level_error, __FUNCTION__, "error connecting to database (db said: '{}')", p_PQerrorMessage(pgsql)); p_PQfinish(pgsql); pgsql = NULL; return -1; } return 0; } static int sql_pgsql_close(void) { if (pgsql) { p_PQfinish(pgsql); pgsql = NULL; } #ifdef RUNTIME_LIBS if (handle) { CloseLibrary(handle); handle = NULL; } #endif return 0; } static t_sql_res * sql_pgsql_query_res(const char * query) { t_pgsql_res *res; PGresult *pgres; if (pgsql == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "pgsql driver not initilized"); return NULL; } if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL query"); return NULL; } if ((pgres = p_PQexec(pgsql, query)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "not enough memory for query ({})", query); return NULL; } if (p_PQresultStatus(pgres) != PGRES_TUPLES_OK) { /* eventlog(eventlog_level_debug, __FUNCTION__, "got error from query ({})", query); */ p_PQclear(pgres); return NULL; } res = (t_pgsql_res *)xmalloc(sizeof(t_pgsql_res)); res->rowbuf = (char **)xmalloc(sizeof(char *)* p_PQnfields(pgres)); res->pgres = pgres; res->crow = 0; /* eventlog(eventlog_level_debug, __FUNCTION__, "res: {:p} res->rowbuf: {:p} res->crow: {} res->pgres: {:p}", res, res->rowbuf, res->crow, res->pgres); */ return res; } static void _pgsql_update_arows(const char *str) { if (!str || str[0] == '\0') lastarows = 0; lastarows = (unsigned int)std::atoi(str); } static int sql_pgsql_query(const char * query) { PGresult *pgres; int res; if (pgsql == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "pgsql driver not initilized"); return -1; } if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL query"); return -1; } if ((pgres = p_PQexec(pgsql, query)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "not enough memory for result"); return -1; } res = p_PQresultStatus(pgres) == PGRES_COMMAND_OK ? 0 : -1; /* Dizzy: HACK ALERT! cache affected rows here before destroying result */ if (!res) _pgsql_update_arows(p_PQcmdTuples(pgres)); p_PQclear(pgres); return res; } static t_sql_row * sql_pgsql_fetch_row(t_sql_res *result) { int nofields, i; t_pgsql_res *res = (t_pgsql_res *)result; if (res == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return NULL; } if (res->crow < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got called without a proper res query"); return NULL; } if (res->crow >= p_PQntuples(res->pgres)) return NULL; /* end of result */ nofields = p_PQnfields(res->pgres); for (i = 0; i < nofields; i++) { res->rowbuf[i] = p_PQgetvalue(res->pgres, res->crow, i); /* the next line emulates the mysql way where NULL containing fields return NULL */ if (res->rowbuf[i] && res->rowbuf[i][0] == '\0') res->rowbuf[i] = NULL; } res->crow++; /* eventlog(eventlog_level_debug, __FUNCTION__, "res: {:p} res->rowbuf: {:p} res->crow: {} res->pgres: {:p}", res, res->rowbuf, res->crow, res->pgres); */ return res->rowbuf; } static void sql_pgsql_free_result(t_sql_res *result) { t_pgsql_res *res = (t_pgsql_res *)result; if (res == NULL) return; /* eventlog(eventlog_level_debug, __FUNCTION__, "res: {:p} res->rowbuf: {:p} res->crow: {} res->pgres: {:p}", res, res->rowbuf, res->crow, res->pgres); */ if (res->pgres) p_PQclear(res->pgres); if (res->rowbuf) xfree((void*)res->rowbuf); xfree((void*)res); } static unsigned int sql_pgsql_num_rows(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return 0; } return p_PQntuples(((t_pgsql_res *)result)->pgres); } static unsigned int sql_pgsql_num_fields(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return 0; } return p_PQnfields(((t_pgsql_res *)result)->pgres); } static unsigned int sql_pgsql_affected_rows(void) { return lastarows; } static t_sql_field * sql_pgsql_fetch_fields(t_sql_res *result) { t_pgsql_res *res = (t_pgsql_res *)result; unsigned fieldno, i; t_sql_field *rfields; if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return NULL; } fieldno = p_PQnfields(res->pgres); rfields = (t_sql_field *)xmalloc(sizeof(t_sql_field)* (fieldno + 1)); for (i = 0; i < fieldno; i++) rfields[i] = p_PQfname(res->pgres, i); rfields[i] = NULL; return rfields; } static int sql_pgsql_free_fields(t_sql_field *fields) { if (fields == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL fields"); return -1; } xfree((void*)fields); return 0; /* PQclear() should free the rest properly */ } static void sql_pgsql_escape_string(char *escape, const char *from, int len) { p_PQescapeString(escape, from, len); } } } #endif /* WITH_SQL_PGSQL */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_pgsql.h000066400000000000000000000017741462640461400202520ustar00rootroot00000000000000/* * Copyright (C) 2002,2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SQL_PGSQL_PROTOS #define INCLUDED_SQL_PGSQL_PROTOS #define JUST_NEED_TYPES # include "sql_common.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern t_sql_engine sql_pgsql; } } #endif /* INCLUDED_SQL_PGSQL_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_sqlite3.cpp000066400000000000000000000211141462640461400210310ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WITH_SQL_SQLITE3 #include "common/setup_before.h" # include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "storage_sql.h" #include "sql_sqlite3.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { typedef struct { char** results; int rows; int columns; int crow; } t_sqlite3_res; static int sql_sqlite3_init(const char *, const char *, const char *, const char *, const char *, const char *); static int sql_sqlite3_close(void); static t_sql_res * sql_sqlite3_query_res(const char *); static int sql_sqlite3_query(const char *); static t_sql_row * sql_sqlite3_fetch_row(t_sql_res *); static void sql_sqlite3_free_result(t_sql_res *); static unsigned int sql_sqlite3_num_rows(t_sql_res *); static unsigned int sql_sqlite3_num_fields(t_sql_res *); static unsigned int sql_sqlite3_affected_rows(void); static t_sql_field * sql_sqlite3_fetch_fields(t_sql_res *); static int sql_sqlite3_free_fields(t_sql_field *); static void sql_sqlite3_escape_string(char *, const char *, int); t_sql_engine sql_sqlite3 = { sql_sqlite3_init, sql_sqlite3_close, sql_sqlite3_query_res, sql_sqlite3_query, sql_sqlite3_fetch_row, sql_sqlite3_free_result, sql_sqlite3_num_rows, sql_sqlite3_num_fields, sql_sqlite3_affected_rows, sql_sqlite3_fetch_fields, sql_sqlite3_free_fields, sql_sqlite3_escape_string }; static sqlite3 *db = NULL; #ifndef RUNTIME_LIBS # define p_sqlite3_changes sqlite3_changes # define p_sqlite3_close sqlite3_close # define p_sqlite3_errmsg sqlite3_errmsg # define p_sqlite3_exec sqlite3_exec # define p_sqlite3_free_table sqlite3_free_table # define p_sqlite3_get_table sqlite3_get_table # define p_sqlite3_open sqlite3_open # define p_sqlite3_snprintf sqlite3_snprintf #else /* RUNTIME_LIBS */ static int sqlite_load_library(void); typedef int(*f_sqlite3_changes)(sqlite3*); typedef int(*f_sqlite3_close)(sqlite3*); typedef const char* (*f_sqlite3_errmsg)(sqlite3*); typedef int(*f_sqlite3_exec)(sqlite3*, const char*, sqlite3_callback, void*, char**); typedef void(*f_sqlite3_free_table)(char **); typedef int(*f_sqlite3_get_table)(sqlite3*, const char*, char***, int*, int*, char**); typedef int(*f_sqlite3_open)(const char*, sqlite3**); typedef char* (*f_sqlite3_snprintf)(int, char*, const char*, ...); static f_sqlite3_changes p_sqlite3_changes = NULL; static f_sqlite3_close p_sqlite3_close = NULL; static f_sqlite3_errmsg p_sqlite3_errmsg = NULL; static f_sqlite3_exec p_sqlite3_exec = NULL; static f_sqlite3_free_table p_sqlite3_free_table = NULL; static f_sqlite3_get_table p_sqlite3_get_table = NULL; static f_sqlite3_open p_sqlite3_open = NULL; static f_sqlite3_snprintf p_sqlite3_snprintf = NULL; #include "compat/runtime_libs.h" /* defines OpenLibrary(), GetFunction(), CloseLibrary() & SQLITE3_LIB */ static void * handle = NULL; static int sqlite_load_library(void) { if ((handle = OpenLibrary(SQLITE3_LIB)) == NULL) return -1; if (((p_sqlite3_changes = (f_sqlite3_changes)GetFunction(handle, "sqlite3_changes")) == NULL) || ((p_sqlite3_close = (f_sqlite3_close)GetFunction(handle, "sqlite3_close")) == NULL) || ((p_sqlite3_errmsg = (f_sqlite3_errmsg)GetFunction(handle, "sqlite3_errmsg")) == NULL) || ((p_sqlite3_exec = (f_sqlite3_exec)GetFunction(handle, "sqlite3_exec")) == NULL) || ((p_sqlite3_free_table = (f_sqlite3_free_table)GetFunction(handle, "sqlite3_free_table")) == NULL) || ((p_sqlite3_get_table = (f_sqlite3_get_table)GetFunction(handle, "sqlite3_get_table")) == NULL) || ((p_sqlite3_open = (f_sqlite3_open)GetFunction(handle, "sqlite3_open")) == NULL) || ((p_sqlite3_snprintf = (f_sqlite3_snprintf)GetFunction(handle, "sqlite3_snprintf")) == NULL)) { CloseLibrary(handle); handle = NULL; return -1; } return 0; } #endif /* RUNTIME_LIBS */ static int sql_sqlite3_init(const char *host, const char *port, const char *socket, const char *name, const char *user, const char *pass) { #ifdef RUNTIME_LIBS if (sqlite_load_library()) { eventlog(eventlog_level_error, __FUNCTION__, "error loading library file \"{}\"", SQLITE3_LIB); return -1; } #endif /* SQLite3 has no host, port, socket, user or password and the database name is the path to the db file */ if (p_sqlite3_open(name, &db) != SQLITE_OK) { eventlog(eventlog_level_error, __FUNCTION__, "got error from sqlite3_open ({})", p_sqlite3_errmsg(db)); p_sqlite3_close(db); return -1; } return 0; } static int sql_sqlite3_close(void) { if (db) { if (p_sqlite3_close(db) != SQLITE_OK) { eventlog(eventlog_level_error, __FUNCTION__, "got error from sqlite3_close ({})", p_sqlite3_errmsg(db)); return -1; } db = NULL; } #ifdef RUNTIME_LIBS if (handle) { CloseLibrary(handle); handle = NULL; } #endif return 0; } static t_sql_res * sql_sqlite3_query_res(const char* query) { t_sqlite3_res *res; if (db == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "sqlite3 driver not initilized"); return NULL; } if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL query"); return NULL; } res = (t_sqlite3_res *)xmalloc(sizeof(t_sqlite3_res)); if (p_sqlite3_get_table(db, query, &res->results, &res->rows, &res->columns, NULL) != SQLITE_OK) { /* eventlog(eventlog_level_debug, __FUNCTION__, "got error ({}) from query ({})", p_sqlite3_errmsg(db), query); */ xfree((void*)res); return NULL; } res->crow = 0; return res; } static int sql_sqlite3_query(const char* query) { if (db == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "sqlite3 driver not initilized"); return -1; } if (query == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL query"); return -1; } return p_sqlite3_exec(db, query, NULL, NULL, NULL) == SQLITE_OK ? 0 : -1; } static t_sql_row * sql_sqlite3_fetch_row(t_sql_res *result) { t_sqlite3_res *res = (t_sqlite3_res*)result; if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return NULL; } return res->crow < res->rows ? (res->results + res->columns * (++res->crow)) : NULL; } static void sql_sqlite3_free_result(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return; } p_sqlite3_free_table(((t_sqlite3_res *)result)->results); xfree(result); } static unsigned int sql_sqlite3_num_rows(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return 0; } return ((t_sqlite3_res *)result)->rows; } static unsigned int sql_sqlite3_num_fields(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return 0; } return ((t_sqlite3_res *)result)->columns; } static unsigned int sql_sqlite3_affected_rows(void) { return p_sqlite3_changes(db); } static t_sql_field * sql_sqlite3_fetch_fields(t_sql_res *result) { if (result == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL result"); return NULL; } /* the first "row" is actually the field names */ return ((t_sqlite3_res*)result)->results; } static int sql_sqlite3_free_fields(t_sql_field *fields) { if (fields == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL fields"); return -1; } return 0; /* sqlite3_free_table() should free it properly */ } static void sql_sqlite3_escape_string(char *escape, const char *from, int len) { if (db == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "sqlite3 driver not initilized"); return; } p_sqlite3_snprintf(len * 2 + 1, escape, "%q", from); } } } #endif /* WITH_SQL_SQLITE3 */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/sql_sqlite3.h000066400000000000000000000017751462640461400205110ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SQL_SQLITE3_PROTOS #define INCLUDED_SQL_SQLITE3_PROTOS #define JUST_NEED_TYPES # include "sql_common.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern t_sql_engine sql_sqlite3; } } #endif /* INCLUDED_SQL_MYSQL_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/storage.cpp000066400000000000000000000045351462640461400202420ustar00rootroot00000000000000/* * Copyright (C) 2002,2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "compat/strcasecmp.h" #include #include #include "storage.h" #include "storage_file.h" #ifdef WITH_SQL #include "storage_sql.h" #endif #include "compat/strdup.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { t_storage *storage = NULL; extern int storage_init(const char *spath) { char *p; int res; char dstr[256]; if (spath == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL spath"); return -1; } if ((p = std::strchr((char *)spath, ':')) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "malformed storage_path , driver not found"); return -1; } std::strcpy(dstr, "file"); #ifdef WITH_SQL std::strcat(dstr, ", sql"); #endif eventlog(eventlog_level_info, __FUNCTION__, "initializing storage layer (available drivers: {})", dstr); *p = '\0'; if (strcasecmp(spath, "file") == 0) { storage = &storage_file; res = storage->init(p + 1); if (!res) eventlog(eventlog_level_info, __FUNCTION__, "using file storage driver"); } #ifdef WITH_SQL else if (strcasecmp(spath, "sql") == 0) { storage = &storage_sql; res = storage->init(p + 1); if (!res) eventlog(eventlog_level_info, __FUNCTION__, "using sql storage driver"); } #endif else { eventlog(eventlog_level_fatal, __FUNCTION__, "no known driver specified ({})", spath); res = -1; } return res; } extern void storage_close(void) { storage->close(); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/storage.h000066400000000000000000000047571462640461400177150ustar00rootroot00000000000000/* * Copyright (C) 2002,2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STORAGE_TYPES #define INCLUDED_STORAGE_TYPES #include "common/elist.h" #include "attr.h" /* flags to tell if calling codes really want all accounts loading */ #define ST_NONE 0 #define ST_FORCE 1 namespace pvpgn { namespace bnetd { typedef const void t_storage_info; typedef int(*t_read_attr_func)(const char *, const char *, void *); typedef int(*t_read_accounts_func)(t_storage_info *, void*); typedef int(*t_load_clans_func)(void*); typedef int(*t_load_teams_func)(void*); typedef struct { int(*init)(const char *); int(*close)(void); unsigned(*read_maxuserid)(void); t_storage_info * (*create_account)(char const *); t_storage_info * (*get_defacct)(void); int(*free_info)(t_storage_info *); int(*read_attrs)(t_storage_info *, t_read_attr_func, void *, const char *); int(*write_attrs)(t_storage_info *, const t_hlist *); t_attr * (*read_attr)(t_storage_info *, const char *); int(*read_accounts)(int, t_read_accounts_func, void *); t_storage_info * (*read_account)(const char *, unsigned); int(*cmp_info)(t_storage_info *, t_storage_info *); const char * (*escape_key)(const char *); int(*load_clans)(t_load_clans_func); int(*write_clan)(void *); int(*remove_clan)(int); int(*remove_clanmember)(int); int(*load_teams)(t_load_teams_func); int(*write_team)(void *); int(*remove_team)(unsigned int); } t_storage; } } #endif /* INCLUDED_STORAGE_TYPES */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_STORAGE_PROTOS #define INCLUDED_STORAGE_PROTOS namespace pvpgn { namespace bnetd { extern t_storage *storage; extern int storage_init(const char *); extern void storage_close(void); } } #endif /* INCLUDED_STORAGE_PROTOS */ #endif /* JUST_NEED_TYPES */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/storage_file.cpp000066400000000000000000000554031462640461400212410ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2002,2003,2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "storage_file.h" #include #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include "compat/strcasecmp.h" #include "compat/pdir.h" #include "compat/rename.h" #include "common/eventlog.h" #include "common/list.h" #include "common/tag.h" #include "common/util.h" #define CLAN_INTERNAL_ACCESS #define TEAM_INTERNAL_ACCESS #include "team.h" #include "account.h" #include "file_plain.h" #include "prefs.h" #include "clan.h" #undef CLAN_INTERNAL_ACCESS #undef TEAM_INTERNAL_ACCESS #include "common/setup_after.h" namespace pvpgn { namespace bnetd { /* file storage API functions */ static int file_init(const char *); static int file_close(void); static unsigned file_read_maxuserid(void); static t_storage_info *file_create_account(char const *); static t_storage_info *file_get_defacct(void); static int file_free_info(t_storage_info *); static int file_read_attrs(t_storage_info *, t_read_attr_func, void *, const char *); static t_attr *file_read_attr(t_storage_info *, const char *); static int file_write_attrs(t_storage_info *, const t_hlist *); static int file_read_accounts(int, t_read_accounts_func, void *); static t_storage_info *file_read_account(const char *, unsigned); static int file_cmp_info(t_storage_info *, t_storage_info *); static const char *file_escape_key(const char *); static int file_load_clans(t_load_clans_func); static int file_write_clan(void *); static int file_remove_clan(int); static int file_remove_clanmember(int); static int file_load_teams(t_load_teams_func); static int file_write_team(void *); static int file_remove_team(unsigned int); /* storage struct populated with the functions above */ t_storage storage_file = { file_init, file_close, file_read_maxuserid, file_create_account, file_get_defacct, file_free_info, file_read_attrs, file_write_attrs, file_read_attr, file_read_accounts, file_read_account, file_cmp_info, file_escape_key, file_load_clans, file_write_clan, file_remove_clan, file_remove_clanmember, file_load_teams, file_write_team, file_remove_team }; /* start of actual file storage code */ static const char *accountsdir = NULL; static const char *clansdir = NULL; static const char *teamsdir = NULL; static const char *defacct = NULL; static t_file_engine *file = NULL; static unsigned file_read_maxuserid(void) { return maxuserid; } static int file_init(const char *path) { char *tok, *copy, *tmp, *p; const char *dir = NULL; const char *clan = NULL; const char *team = NULL; const char *def = NULL; const char *driver = NULL; if (path == NULL || path[0] == '\0') { eventlog(eventlog_level_error, __FUNCTION__, "got NULL or empty path"); return -1; } copy = xstrdup(path); tmp = copy; while ((tok = std::strtok(tmp, ";")) != NULL) { tmp = NULL; if ((p = std::strchr(tok, '=')) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "invalid storage_path, no '=' present in token"); xfree((void *)copy); return -1; } *p = '\0'; if (strcasecmp(tok, "dir") == 0) dir = p + 1; else if (strcasecmp(tok, "clan") == 0) clan = p + 1; else if (strcasecmp(tok, "team") == 0) team = p + 1; else if (strcasecmp(tok, "default") == 0) def = p + 1; else if (strcasecmp(tok, "mode") == 0) driver = p + 1; else eventlog(eventlog_level_warn, __FUNCTION__, "unknown token in storage_path : '{}'", tok); } if (def == NULL || clan == NULL || team == NULL || dir == NULL || driver == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "invalid storage_path line for file module (doesnt have a 'dir', a 'clan', a 'team', a 'default' token and a 'mode' token)"); xfree((void *)copy); return -1; } if (!strcasecmp(driver, "plain")) file = &file_plain; else { eventlog(eventlog_level_error, __FUNCTION__, "unknown mode '{}', mode must be plain", driver); xfree((void *)copy); return -1; } if (accountsdir) file_close(); accountsdir = xstrdup(dir); clansdir = xstrdup(clan); teamsdir = xstrdup(team); defacct = xstrdup(def); xfree((void *)copy); return 0; } static int file_close(void) { if (accountsdir) xfree((void *)accountsdir); accountsdir = NULL; if (clansdir) xfree((void *)clansdir); clansdir = NULL; if (teamsdir) xfree((void *)teamsdir); teamsdir = NULL; if (defacct) xfree((void *)defacct); defacct = NULL; file = NULL; return 0; } static t_storage_info *file_create_account(const char *username) { char *temp; if (accountsdir == NULL || file == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "file storage not initilized"); return NULL; } if (prefs_get_savebyname()) { char const *safename; if (!std::strcmp(username, defacct)) { eventlog(eventlog_level_error, __FUNCTION__, "username as defacct not allowed"); return NULL; } if (!(safename = escape_fs_chars(username, std::strlen(username)))) { eventlog(eventlog_level_error, __FUNCTION__, "could not escape username"); return NULL; } temp = (char*)xmalloc(std::strlen(accountsdir) + 1 + std::strlen(safename) + 1); /* dir + / + name + NUL */ std::sprintf(temp, "%s/%s", accountsdir, safename); xfree((void *)safename); /* avoid warning */ } else { temp = (char*)xmalloc(std::strlen(accountsdir) + 1 + 8 + 1); /* dir + / + uid + NUL */ std::sprintf(temp, "%s/%06u", accountsdir, maxuserid + 1); /* FIXME: hmm, maybe up the %06 to %08... */ } return temp; } static int file_write_attrs(t_storage_info * info, const t_hlist *attributes) { char *tempname; if (accountsdir == NULL || file == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "file storage not initilized"); return -1; } if (info == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL info storage"); return -1; } if (attributes == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL attributes"); return -1; } tempname = (char*)xmalloc(std::strlen(accountsdir) + 1 + std::strlen(BNETD_ACCOUNT_TMP) + 1); std::sprintf(tempname, "%s/%s", accountsdir, BNETD_ACCOUNT_TMP); if (file->write_attrs(tempname, attributes)) { /* no eventlog here, it should be reported from the file layer */ xfree(tempname); return -1; } if (p_rename(tempname, (const char *)info) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not std::rename account file to \"{}\" (std::rename: {})", (char *)info, std::strerror(errno)); xfree(tempname); return -1; } xfree(tempname); return 0; } static int file_read_attrs(t_storage_info * info, t_read_attr_func cb, void *data, const char *ktab) { if (accountsdir == NULL || file == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "file storage not initilized"); return -1; } if (info == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL info storage"); return -1; } if (cb == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL callback"); return -1; } eventlog(eventlog_level_debug, __FUNCTION__, "loading \"{}\"", reinterpret_cast(info)); if (file->read_attrs((const char *)info, cb, data)) { /* no eventlog, error reported earlier */ return -1; } return 0; } static t_attr *file_read_attr(t_storage_info * info, const char *key) { if (accountsdir == NULL || file == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "file storage not initilized"); return NULL; } if (info == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL info storage"); return NULL; } if (key == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return NULL; } return file->read_attr((const char *)info, key); } static int file_free_info(t_storage_info * info) { if (info) xfree((void *)info); return 0; } static t_storage_info *file_get_defacct(void) { t_storage_info *info; if (defacct == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "file storage not initilized"); return NULL; } info = xstrdup(defacct); return info; } static int file_read_accounts(int flag, t_read_accounts_func cb, void *data) { if (!accountsdir) { ERROR0("file storage not initilized"); return -1; } if (!cb) { ERROR0("got NULL callback"); return -1; } try { Directory accdir(accountsdir); char const *dentry; while ((dentry = accdir.read())) { std::ostringstream ostr; ostr << accountsdir << '/' << dentry; cb(xstrdup(ostr.str().c_str()), data); } } catch (const Directory::OpenError& ex) { ERROR2("unable to open user directory \"{}\" for reading (error: {})", accountsdir, ex.what()); return -1; } return 0; } static t_storage_info *file_read_account(const char *accname, unsigned uid) { char *pathname; if (accountsdir == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "file storage not initilized"); return NULL; } /* ONLY if requesting for a username and if savebyname() is true * PS: yes its kind of a hack, we will make a proper index file */ if (accname && prefs_get_savebyname()) { pathname = (char*)xmalloc(std::strlen(accountsdir) + 1 + std::strlen(accname) + 1); /* dir + / + file + NUL */ std::sprintf(pathname, "%s/%s", accountsdir, accname); if (access(pathname, F_OK)) /* if it doesn't exist */ { xfree((void *)pathname); return NULL; } return pathname; } return NULL; } static int file_cmp_info(t_storage_info * info1, t_storage_info * info2) { return std::strcmp((const char *)info1, (const char *)info2); } static const char *file_escape_key(const char *key) { return key; } static int file_load_clans(t_load_clans_func cb) { char const *dentry; char *pathname; int clantag; t_clan *clan; std::FILE *fp; char *clanname, *motd, *p; char line[1024]; int cid, creation_time; int member_uid, member_join_time; char member_status; t_clanmember *member; if (cb == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL callback"); return -1; } try { Directory clandir(clansdir); eventlog(eventlog_level_trace, __FUNCTION__, "start reading clans"); pathname = (char*)xmalloc(std::strlen(clansdir) + 1 + 4 + 1); while ((dentry = clandir.read())) { if (std::strlen(dentry) > 4) { eventlog(eventlog_level_error, __FUNCTION__, "found too long clan filename in clandir \"{}\"", dentry); continue; } std::sprintf(pathname, "%s/%s", clansdir, dentry); clantag = str_to_clantag(dentry); if ((fp = std::fopen(pathname, "r")) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "can't open clanfile \"{}\"", pathname); continue; } clan = (t_clan*)xmalloc(sizeof(t_clan)); clan->tag = clantag; if (!std::fgets(line, 1024, fp)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid clan file: no first line"); xfree((void*)clan); continue; } clanname = line; if (*clanname != '"') { eventlog(eventlog_level_error, __FUNCTION__, "invalid clan file: invalid first line"); xfree((void*)clan); continue; } clanname++; p = std::strchr(clanname, '"'); if (!p) { eventlog(eventlog_level_error, __FUNCTION__, "invalid clan file: invalid first line"); xfree((void*)clan); continue; } *p = '\0'; if (std::strlen(clanname) >= CLAN_NAME_MAX) { eventlog(eventlog_level_error, __FUNCTION__, "invalid clan file: invalid first line"); xfree((void*)clan); continue; } p++; if (*p != ',') { eventlog(eventlog_level_error, __FUNCTION__, "invalid clan file: invalid first line"); xfree((void*)clan); continue; } p++; if (*p != '"') { eventlog(eventlog_level_error, __FUNCTION__, "invalid clan file: invalid first line"); xfree((void*)clan); continue; } motd = p + 1; p = std::strchr(motd, '"'); if (!p) { eventlog(eventlog_level_error, __FUNCTION__, "invalid clan file: invalid first line"); xfree((void*)clan); continue; } *p = '\0'; if (std::sscanf(p + 1, ",%d,%d\n", &cid, &creation_time) != 2) { eventlog(eventlog_level_error, __FUNCTION__, "invalid first line in clanfile"); xfree((void*)clan); continue; } clan->clanname = xstrdup(clanname); clan->clan_motd = xstrdup(motd); clan->clanid = cid; clan->creation_time = (std::time_t) creation_time; clan->created = 1; clan->modified = 0; clan->channel_type = prefs_get_clan_channel_default_private(); eventlog(eventlog_level_trace, __FUNCTION__, "name: {} motd: {} clanid: {} time: {}", clanname, motd, cid, creation_time); clan->members = list_create(); while (std::fscanf(fp, "%i,%c,%i\n", &member_uid, &member_status, &member_join_time) == 3) { member = (t_clanmember*)xmalloc(sizeof(t_clanmember)); if (!(member->memberacc = accountlist_find_account_by_uid(member_uid))) { eventlog(eventlog_level_error, __FUNCTION__, "cannot find uid {}", member_uid); xfree((void *)member); continue; } member->status = member_status - '0'; member->join_time = member_join_time; member->clan = clan; member->fullmember = 1; /* In files we have only fullmembers */ if ((member->status == CLAN_NEW) && (std::time(NULL) - member->join_time > prefs_get_clan_newer_time() * 3600)) { member->status = CLAN_PEON; clan->modified = 1; } list_append_data(clan->members, member); account_set_clanmember((t_account*)member->memberacc, member); eventlog(eventlog_level_trace, __FUNCTION__, "added member: uid: {} status: {} join_time: {}", member_uid, member_status + '0', member_join_time); } std::fclose(fp); cb(clan); } xfree((void *)pathname); } catch (const Directory::OpenError& ex) { ERROR2("unable to open clan directory \"{}\" for reading (error: {})", clansdir, ex.what()); return -1; } eventlog(eventlog_level_trace, __FUNCTION__, "finished reading clans"); return 0; } static int file_write_clan(void *data) { std::FILE *fp; t_elem *curr; t_clanmember *member; char *clanfile; t_clan *clan = (t_clan *)data; clanfile = (char*)xmalloc(std::strlen(clansdir) + 1 + 4 + 1); std::sprintf(clanfile, "%s/%s", clansdir, clantag_to_str(clan->tag)); if ((fp = std::fopen(clanfile, "w")) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "can't open clanfile \"{}\"", clanfile); xfree((void *)clanfile); return -1; } std::fprintf(fp, "\"%s\",\"%s\",%i,%i\n", clan->clanname, clan->clan_motd, clan->clanid, (int)clan->creation_time); LIST_TRAVERSE(clan->members, curr) { if (!(member = (t_clanmember*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem in list"); continue; } if ((member->status == CLAN_NEW) && (std::time(NULL) - member->join_time > prefs_get_clan_newer_time() * 3600)) member->status = CLAN_PEON; if (member->fullmember == 1) /* only fullmembers are stored */ std::fprintf(fp, "%i,%c,%u\n", account_get_uid((t_account*)member->memberacc), member->status + '0', (unsigned)member->join_time); } std::fclose(fp); xfree((void *)clanfile); return 0; } static int file_remove_clan(int clantag) { char *tempname; tempname = (char*)xmalloc(std::strlen(clansdir) + 1 + 4 + 1); std::sprintf(tempname, "%s/%s", clansdir, clantag_to_str(clantag)); if (std::remove((const char *)tempname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not delete clan file \"{}\" (std::remove: {})", (char *)tempname, std::strerror(errno)); xfree(tempname); return -1; } xfree(tempname); return 0; } static int file_remove_clanmember(int uid) { return 0; } static int file_load_teams(t_load_teams_func cb) { char const *dentry; char *pathname; unsigned int teamid; t_team *team; std::FILE *fp; char * line; unsigned int fteamid, lastgame; unsigned char size; char clienttag[5]; int i; if (cb == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "get NULL callback"); return -1; } try { Directory teamdir(teamsdir); eventlog(eventlog_level_trace, __FUNCTION__, "start reading teams"); pathname = (char*)xmalloc(std::strlen(teamsdir) + 1 + 8 + 1); while ((dentry = teamdir.read())) { if (std::strlen(dentry) != 8) { eventlog(eventlog_level_error, __FUNCTION__, "found invalid team filename in teamdir \"{}\"", dentry); continue; } std::sprintf(pathname, "%s/%s", teamsdir, dentry); teamid = (unsigned int)std::strtoul(dentry, NULL, 16); // we use hexadecimal teamid as filename if ((fp = std::fopen(pathname, "r")) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "can't open teamfile \"{}\"", pathname); continue; } team = (t_team*)xmalloc(sizeof(t_team)); team->teamid = teamid; if (!(line = file_get_line(fp))) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: file is empty"); goto load_team_failure; } if (std::sscanf(line, "%u,%c,%4s,%u", &fteamid, &size, clienttag, &lastgame) != 4) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: invalid number of arguments on first line"); goto load_team_failure; } if (fteamid != teamid) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: filename and stored teamid don't match"); goto load_team_failure; } size -= '0'; if ((size<2) || (size>4)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: invalid team size"); goto load_team_failure; } team->size = size; if (!(tag_check_client(team->clienttag = tag_str_to_uint(clienttag)))) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: invalid clienttag"); goto load_team_failure; } team->lastgame = (std::time_t)lastgame; if (!(line = file_get_line(fp))) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: missing 2nd line"); goto load_team_failure; } if (std::sscanf(line, "%u,%u,%u,%u", &team->teammembers[0], &team->teammembers[1], &team->teammembers[2], &team->teammembers[3]) != 4) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: invalid number of arguments on 2nd line"); goto load_team_failure; } for (i = 0; i < MAX_TEAMSIZE; i++) { if (i < size) { if ((team->teammembers[i] == 0)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: too few members"); goto load_team_failure; } } else { if ((team->teammembers[i] != 0)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: too many members"); goto load_team_failure; } } team->members[i] = NULL; } if (!(line = file_get_line(fp))) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: missing 3rd line"); goto load_team_failure; } if (std::sscanf(line, "%d,%d,%d,%d,%d", &team->wins, &team->losses, &team->xp, &team->level, &team->rank) != 5) { eventlog(eventlog_level_error, __FUNCTION__, "invalid team file: invalid number of arguments on 3rd line"); goto load_team_failure; } eventlog(eventlog_level_trace, __FUNCTION__, "succesfully loaded team {}", dentry); cb(team); goto load_team_success; load_team_failure: xfree((void*)team); eventlog(eventlog_level_error, __FUNCTION__, "error while reading file \"{}\"", dentry); load_team_success: file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); } xfree((void *)pathname); } catch (const Directory::OpenError& ex) { ERROR2("unable to open team directory \"{}\" for reading (error: {})", teamsdir, ex.what()); return -1; } eventlog(eventlog_level_trace, __FUNCTION__, "finished reading teams"); return 0; } static int file_write_team(void *data) { std::FILE *fp; char *teamfile; t_team *team = (t_team *)data; teamfile = (char*)xmalloc(std::strlen(teamsdir) + 1 + 8 + 1); std::sprintf(teamfile, "%s/%08x", teamsdir, team->teamid); if ((fp = std::fopen(teamfile, "w")) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "can't open teamfile \"{}\"", teamfile); xfree((void *)teamfile); return -1; } std::fprintf(fp, "%u,%c,%s,%u\n", team->teamid, team->size + '0', clienttag_uint_to_str(team->clienttag), (unsigned int)team->lastgame); std::fprintf(fp, "%u,%u,%u,%u\n", team->teammembers[0], team->teammembers[1], team->teammembers[2], team->teammembers[3]); std::fprintf(fp, "%d,%d,%d,%d,%d\n", team->wins, team->losses, team->xp, team->level, team->rank); std::fclose(fp); xfree((void *)teamfile); return 0; } static int file_remove_team(unsigned int teamid) { char *tempname; tempname = (char*)xmalloc(std::strlen(clansdir) + 1 + 8 + 1); std::sprintf(tempname, "%s/%08x", clansdir, teamid); if (std::remove((const char *)tempname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not delete team file \"{}\" (std::remove: {})", (char *)tempname, std::strerror(errno)); xfree(tempname); return -1; } xfree(tempname); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/storage_file.h000066400000000000000000000027151462640461400207040ustar00rootroot00000000000000/* * Copyright (C) 2002,2003,2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INClUDED_STORAGE_FILE_TYPES #define INClUDED_STORAGE_FILE_TYPES #include "storage.h" namespace pvpgn { namespace bnetd { typedef const char t_file_info; typedef struct { t_attr * (*read_attr)(const char *filename, const char *key); int(*read_attrs)(const char *filename, t_read_attr_func cb, void *data); int(*write_attrs)(const char *filename, const t_hlist *attributes); } t_file_engine; } } #endif /* INClUDED_STORAGE_FILE_TYPES */ #ifndef JUST_NEED_TYPES #ifndef INClUDED_STORAGE_FILE_PROTOS #define INClUDED_STORAGE_FILE_PROTOS namespace pvpgn { namespace bnetd { extern t_storage storage_file; } } #endif /* INClUDED_STORAGE_FILE_PROTOS */ #endif /* JUST_NEED_TYPES */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/storage_sql.cpp000066400000000000000000000436211462640461400211200ustar00rootroot00000000000000/* * Copyright (C) 2002 TheUndying * Copyright (C) 2002 zap-zero * Copyright (C) 2002,2003 Dizzy * Copyright (C) 2002 Zzzoom * Copyright (C) 2014 HarpyWar * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifdef WITH_SQL #include "storage_sql.h" #include #include #include #include #include #include #include "common/eventlog.h" #include "common/util.h" #include "common/xstring.h" #define SQL_INTERNAL # include "sql_common.h" #undef SQL_INTERNAL #include "account.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_storage_info *sql_create_account(char const *); static int sql_read_attrs(t_storage_info *, t_read_attr_func, void *, const char *); static t_attr *sql_read_attr(t_storage_info *, const char *); static int sql_write_attrs(t_storage_info *, const t_hlist *); static t_storage_info * sql_read_account(const char *, unsigned); static const char *sql_escape_key(const char *); t_storage storage_sql = { sql_init, sql_close, sql_read_maxuserid, sql_create_account, sql_get_defacct, sql_free_info, sql_read_attrs, sql_write_attrs, sql_read_attr, sql_read_accounts, sql_read_account, sql_cmp_info, sql_escape_key, sql_load_clans, sql_write_clan, sql_remove_clan, sql_remove_clanmember, sql_load_teams, sql_write_team, sql_remove_team }; // Attribute names that are assurance exist in database std::map > knownattributes; static char query[512]; #ifndef SQL_ON_DEMAND static const char *_db_add_tab(const char *tab, const char *key) { static char nkey[DB_MAX_ATTRKEY]; std::strncpy(nkey, tab, sizeof(nkey)-1); nkey[std::strlen(nkey) + 1] = '\0'; nkey[std::strlen(nkey)] = '_'; std::strncpy(nkey + std::strlen(nkey), key, sizeof(nkey)-std::strlen(nkey)); return nkey; } #endif /* SQL_ON_DEMAND */ static int _db_get_tab(const char *key, char **ptab, char **pcol) { static char tab[DB_MAX_ATTRKEY]; static char col[DB_MAX_ATTRKEY]; std::strncpy(tab, key, DB_MAX_TAB - 1); tab[DB_MAX_TAB - 1] = 0; if (!std::strchr(tab, '_')) return -1; *(std::strchr(tab, '_')) = 0; std::strncpy(col, key + std::strlen(tab) + 1, DB_MAX_TAB - 1); col[DB_MAX_TAB - 1] = 0; /* return tab and col as 2 static buffers */ *ptab = tab; *pcol = col; return 0; } static t_storage_info *sql_create_account(char const *username) { t_sql_res *result = NULL; t_sql_row *row; int uid = maxuserid + 1; t_storage_info *info; char *user; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return NULL; } user = xstrdup(username); strtolower(user); std::snprintf(query, sizeof(query), "SELECT count(*) FROM %sBNET WHERE username='%s'", tab_prefix, user); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL) { int num; row = sql->fetch_row(result); if (row == NULL || row[0] == NULL) { sql->free_result(result); eventlog(eventlog_level_error, __FUNCTION__, "got NULL count"); goto err_dup; } num = std::atol(row[0]); sql->free_result(result); if (num > 0) { eventlog(eventlog_level_error, __FUNCTION__, "got existant username"); goto err_dup; } } else { eventlog(eventlog_level_error, __FUNCTION__, "error trying query: \"{}\"", query); goto err_dup; } info = xmalloc(sizeof(t_sql_info)); *((unsigned int *)info) = uid; std::snprintf(query, sizeof(query), "DELETE FROM %sBNET WHERE " SQL_UID_FIELD " = '%u'", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); sql->query(query); std::snprintf(query, sizeof(query), "INSERT INTO %sBNET (" SQL_UID_FIELD ",username) VALUES('%u','%s')", tab_prefix, uid, user); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query)) { eventlog(eventlog_level_error, __FUNCTION__, "user insert failed (query: '{}')", query); goto err_info; } std::snprintf(query, sizeof(query), "DELETE FROM %sprofile WHERE " SQL_UID_FIELD " = '%u'", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); sql->query(query); std::snprintf(query, sizeof(query), "INSERT INTO %sprofile (" SQL_UID_FIELD ") VALUES('%u')", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query)) { eventlog(eventlog_level_error, __FUNCTION__, "user insert failed (query: '{}')", query); goto err_info; } std::snprintf(query, sizeof(query), "DELETE FROM %sRecord WHERE " SQL_UID_FIELD " = '%u'", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); sql->query(query); std::snprintf(query, sizeof(query), "INSERT INTO %sRecord (" SQL_UID_FIELD ") VALUES('%u')", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query)) { eventlog(eventlog_level_error, __FUNCTION__, "user insert failed (query: '{}')", query); goto err_info; } std::snprintf(query, sizeof(query), "DELETE FROM %sfriend WHERE " SQL_UID_FIELD " = '%u'", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); sql->query(query); std::snprintf(query, sizeof(query), "INSERT INTO %sfriend (" SQL_UID_FIELD ") VALUES('%u')", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if (sql->query(query)) { eventlog(eventlog_level_error, __FUNCTION__, "user insert failed (query: '{}')", query); goto err_info; } xfree(user); return info; err_info: xfree((void *)info); err_dup: xfree(user); return NULL; } static int sql_read_attrs(t_storage_info * info, t_read_attr_func cb, void *data, const char *ktab) { #ifndef SQL_ON_DEMAND t_sql_res *result = NULL; t_sql_row *row; char **tab; unsigned int uid; unsigned int num_fields; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } if (info == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL storage info"); return -1; } if (cb == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL callback"); return -1; } uid = *((unsigned int *)info); for (tab = const_cast(sql_tables); *tab; tab++) { // process only a table where the attribute is in if (strcmp(ktab, *tab) != 0) continue; std::snprintf(query, sizeof(query), "SELECT * FROM %s%s WHERE " SQL_UID_FIELD "='%u'", tab_prefix, *tab, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); if ((result = sql->query_res(query)) != NULL && sql->num_rows(result) == 1 && (num_fields = sql->num_fields(result)) > 1) { unsigned int i; t_sql_field *fields, *fentry; if ((fields = sql->fetch_fields(result)) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "could not fetch the fields"); sql->free_result(result); return -1; } if (!(row = sql->fetch_row(result))) { eventlog(eventlog_level_error, __FUNCTION__, "could not fetch row"); sql->free_fields(fields); sql->free_result(result); return -1; } for (i = 0, fentry = fields; *fentry; fentry++, i++) { char *output; // (HarpyWar) fix for sqlite3, cause it return columns+rows in "fields", unlike only columns in other databases // and this row[i] goes beyond the bounds of the array. This restriction handles it. if (i >= num_fields) break; /* we have to skip "uid" */ /* we ignore the field used internally by sql */ if (std::strcmp(*fentry, SQL_UID_FIELD) == 0) continue; // eventlog(eventlog_level_trace, __FUNCTION__, "read key (step1): '{}' val: '{}'", _db_add_tab(*tab, *fentry), unescape_chars(row[i])); if (row[i] == NULL) continue; /* its an NULL value sql field */ // eventlog(eventlog_level_trace, __FUNCTION__, "read key (step2): '{}' val: '{}'", _db_add_tab(*tab, *fentry), unescape_chars(row[i])); if (cb(_db_add_tab(*tab, *fentry), (output = unescape_chars(row[i])), data)) eventlog(eventlog_level_error, __FUNCTION__, "got error from callback on UID: {}", uid); if (output) xfree((void *)output); // eventlog(eventlog_level_trace, __FUNCTION__, "read key (final): '{}' val: '{}'", _db_add_tab(*tab, *fentry), unescape_chars(row[i])); } sql->free_fields(fields); } if (result) sql->free_result(result); } #endif /* SQL_ON_DEMAND */ return 0; } static t_attr *sql_read_attr(t_storage_info * info, const char *key) { #ifdef SQL_ON_DEMAND t_sql_res *result = NULL; t_sql_row *row; char *tab, *col; unsigned int uid; t_attr *attr; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return NULL; } if (info == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL storage info"); return NULL; } if (key == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL key"); return NULL; } uid = *((unsigned int *)info); if (_db_get_tab(key, &tab, &col) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error from _db_get_tab"); return NULL; } std::snprintf(query, sizeof(query), "SELECT %s FROM %s%s WHERE " SQL_UID_FIELD " = %u", col, tab_prefix, tab, uid); eventlog(eventlog_level_trace, __FUNCTION__, query); if ((result = sql->query_res(query)) == NULL) return NULL; if (sql->num_rows(result) != 1) { // eventlog(eventlog_level_debug, __FUNCTION__, "wrong numer of rows from query ({})", query); sql->free_result(result); return NULL; } if (!(row = sql->fetch_row(result))) { eventlog(eventlog_level_error, __FUNCTION__, "could not fetch row"); sql->free_result(result); return NULL; } if (row[0] == NULL) { // eventlog(eventlog_level_debug, __FUNCTION__, "NULL value from query ({})", query); sql->free_result(result); return NULL; } /* Because we are double quoting the column name sqlite3 treats it as a string when the column does not exist and returns the column name as the result. We need to return NULL under this condition */ if (std::strcmp(row[0], col) == 0) { sql->free_result(result); return NULL; } attr = attr_create(key, row[0]); sql->free_result(result); return attr; #else return NULL; #endif /* SQL_ON_DEMAND */ } /* write ONLY dirty attributes */ int sql_write_attrs(t_storage_info * info, const t_hlist *attrs) { char escape[DB_MAX_ATTRVAL * 2 + 1]; /* sql docs say the escape can take a maximum of double original size + 1 */ char safeval[DB_MAX_ATTRVAL]; char *p, *tab, *col; t_attr *attr; t_hlist *curr; unsigned int uid; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return -1; } if (info == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL sql info"); return -1; } if (attrs == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL attributes list"); return -1; } uid = *((unsigned int *)info); std::map queries; hlist_for_each(curr, (t_hlist*)attrs) { attr = hlist_entry(curr, t_attr, link); if (!attr_get_dirty(attr)) continue; /* save ONLY dirty attributes */ if (attr_get_key(attr) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL key in attributes list"); continue; } if (attr_get_val(attr) == NULL) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL value in attributes list ({})", attr_get_key(attr)); continue; } if (_db_get_tab(attr_get_key(attr), &tab, &col) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error from _db_get_tab"); continue; } std::strncpy(safeval, attr_get_val(attr), DB_MAX_ATTRVAL - 1); safeval[DB_MAX_ATTRVAL - 1] = 0; for (p = safeval; *p; p++) if (*p == '\'') /* value shouldn't contain ' */ *p = '"'; sql->escape_string(escape, safeval, std::strlen(safeval)); // if attribute found in known attributes list if (std::find(knownattributes[tab].begin(), knownattributes[tab].end(), col) != knownattributes[tab].end()) { // append new field and value queries[tab] += std::string(col) + " = '" + std::string(escape) + "', "; /* PASS NEXT CODE EXECUTION (MERGED QUERIES WILL BE EXECUTED AT THE END OF THE FUNCTION) */ continue; } /* FIRST TIME UPDATE EACH ATTRIBUTE IN A SINGLE QUERY AND SAVE ATTRIBUTE NAME IN 'knownattributes' */ std::snprintf(query, sizeof(query), "UPDATE %s%s SET %s = '%s' WHERE " SQL_UID_FIELD " = '%u'", tab_prefix, tab, col, escape, uid); eventlog(eventlog_level_trace, "db_set", "{}", query); if (sql->query(query) || !sql->affected_rows()) { char query2[512]; // eventlog(eventlog_level_debug, __FUNCTION__, "trying to insert new column {}", col); std::snprintf(query2, sizeof(query2), "ALTER TABLE %s%s ADD COLUMN %s VARCHAR(128)", tab_prefix, tab, col); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query2); sql->query(query2); /* try query again */ // eventlog(eventlog_level_trace, "db_set", "retry insert query: {}", query); if (sql->query(query) || !sql->affected_rows()) { // Tried everything, now trying to insert that user to the table for the first time std::snprintf(query2, sizeof(query2), "INSERT INTO %s%s (" SQL_UID_FIELD ",%s) VALUES ('%u','%s')", tab_prefix, tab, col, uid, escape); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query2); // eventlog(eventlog_level_error, __FUNCTION__, "update failed so tried INSERT for the last chance"); if (sql->query(query2)) { eventlog(eventlog_level_error, __FUNCTION__, "could not INSERT attribute '{}'->'{}'", attr_get_key(attr), attr_get_val(attr)); continue; } else knownattributes[tab].push_back(col); // if query success add attribute in known table } else knownattributes[tab].push_back(col); // if query success add attribute in known table } else knownattributes[tab].push_back(col); // if query success add attribute in known table attr_clear_dirty(attr); } std::string query_s; // iterate all queries for (std::map::iterator q = queries.begin(); q != queries.end(); ++q) { query_s = "UPDATE " + std::string(tab_prefix) + q->first + " SET "; query_s += q->second.substr(0, q->second.size() - 2); // remove last reduntant comma at the end of the string with parameters query_s += " WHERE " SQL_UID_FIELD " = '" + std_to_string(uid) + "'"; if (!sql->query(query_s.c_str())) { eventlog(eventlog_level_trace, __FUNCTION__, "multi-update query: {}", query_s.c_str()); } else { eventlog(eventlog_level_error, __FUNCTION__, "sql error ({})", query_s.c_str()); } } return 0; } static t_storage_info * sql_read_account(const char *name, unsigned uid) { t_sql_res *result = NULL; t_sql_row *row; t_storage_info *info; if (!sql) { eventlog(eventlog_level_error, __FUNCTION__, "sql layer not initilized"); return NULL; } /* SELECT uid from BNET WHERE uid=x sounds stupid, I agree but its a clean * way to check for account existence by an uid */ if (name) { char *user = xstrdup(name); strtolower(user); std::snprintf(query, sizeof(query), "SELECT " SQL_UID_FIELD " FROM %sBNET WHERE username='%s'", tab_prefix, user); xfree(user); } else std::snprintf(query, sizeof(query), "SELECT " SQL_UID_FIELD " FROM %sBNET WHERE " SQL_UID_FIELD " = '%u'", tab_prefix, uid); eventlog(eventlog_level_trace, __FUNCTION__, "{}", query); result = sql->query_res(query); if (!result) { eventlog(eventlog_level_error, __FUNCTION__, "error query db (query:\"{}\")", query); return NULL; } if (sql->num_rows(result) < 1) { sql->free_result(result); return NULL; /* empty user list */ } row = sql->fetch_row(result); if (!row) { /* could not fetch row, this should not happen */ sql->free_result(result); return NULL; } if (row[0] == NULL) /* empty UID field */ eventlog(eventlog_level_error, __FUNCTION__, "got NULL uid from db"); else if ((unsigned int)std::atoi(row[0]) == sql_defacct); /* skip default account */ else { info = xmalloc(sizeof(t_sql_info)); *((unsigned int *)info) = std::atoi(row[0]); sql->free_result(result); return info; } sql->free_result(result); return NULL; } static const char *sql_escape_key(const char *key) { const char *newkey = key; char *p; int idx; for (idx = 0, p = (char *)newkey; *p; p++, idx++) if (*p == '\\' || *p == '`' || *p == '"' || *p == '\'') { newkey = xstrdup(key); p = (char *)(newkey + idx); *(p++) = '_'; for (; *p; p++) if (*p == '\\' || *p == '`' || *p == '"' || *p == '\'') *p = '_'; break; } return newkey; } } } #endif /* WITH_SQL */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/storage_sql.h000066400000000000000000000020041462640461400205530ustar00rootroot00000000000000/* * Copyright (C) 2002,2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_STORAGE_SQL_PROTOS #define INCLUDED_STORAGE_SQL_PROTOS #include "storage.h" namespace pvpgn { namespace bnetd { extern t_storage storage_sql; } } #endif /* INCLUDED_STORAGE_SQL_PROTOS */ #endif /* JUST_NEED_TYPES */ pvpgn-1.99.7.2.1+dfsg/src/bnetd/support.cpp000066400000000000000000000044261462640461400203110ustar00rootroot00000000000000/* * Copyright (C) 2004 Lector * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "support.h" #include #include #include #include "compat/access.h" #include "common/eventlog.h" #include "common/util.h" #include "common/xalloc.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { extern int support_check_files(char const * supportfile) { std::FILE *fp; char *buff; unsigned int line; int filedirlen; char * namebuff; if (!(supportfile)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL supportfile"); return -1; } if (!(fp = std::fopen(supportfile, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", supportfile, std::strerror(errno)); eventlog(eventlog_level_error, __FUNCTION__, "can't guarantee that everything will run smooth"); return 0; } filedirlen = std::strlen(prefs_get_filedir()); for (line = 1; (buff = file_get_line(fp)); line++) { if (buff[0] == '#' || buff[0] == '\0') { continue; } namebuff = (char*)xmalloc(filedirlen + 1 + std::strlen(buff) + 1); std::sprintf(namebuff, "%s/%s", prefs_get_filedir(), buff); if (access(namebuff, F_OK) < 0) { eventlog(eventlog_level_fatal, __FUNCTION__, "necessary file \"{}\" missing", namebuff); xfree((void *)namebuff); std::fclose(fp); return -1; } xfree((void *)namebuff); } file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/support.h000066400000000000000000000016501462640461400177520ustar00rootroot00000000000000/* * Copyright (C) 2004 Lector * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SUPPORT_PROTOS #define INCLUDED_SUPPORT_PROTOS namespace pvpgn { namespace bnetd { extern int support_check_files(char const * supportfile); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/team.cpp000066400000000000000000000265261462640461400175300ustar00rootroot00000000000000/* * (C) 2004 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define TEAM_INTERNAL_ACCESS #include "team.h" #include #include #include "common/eventlog.h" #include "common/list.h" #include "common/bnet_protocol.h" #include "account.h" #include "account_wrap.h" #include "storage.h" #include "ladder.h" #include "server.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_list *teamlist_head = NULL; unsigned max_teamid = 0; int teamlist_add_team(t_team * team); /* callback function for storage use */ static int _cb_load_teams(void *team) { if (teamlist_add_team((t_team*)team) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to add team to teamlist"); return -1; } if (((t_team *)team)->teamid > max_teamid) max_teamid = ((t_team *)team)->teamid; return 0; } int teamlist_add_team(t_team * team) { int i; if (!(team)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL team"); return -1; } for (i = 0; i < team->size; i++) { if (!(team->members[i] = accountlist_find_account_by_uid(team->teammembers[i]))) { eventlog(eventlog_level_error, __FUNCTION__, "at least one non-existant member (id {}) in team {} - discarding team", team->teammembers[i], team->teamid); //FIXME: delete team file now??? return team->teamid; //we return teamid even though we have an error, we don't want unintentional overwriting } } for (i = 0; i < team->size; i++) account_add_team(team->members[i], team); if (!(team->teamid)) team->teamid = ++max_teamid; list_append_data(teamlist_head, team); return team->teamid; } int teamlist_load(void) { // make sure to unload previous teamlist before loading again if (teamlist_head) teamlist_unload(); teamlist_head = list_create(); storage->load_teams(_cb_load_teams); return 0; } int teamlist_unload(void) { t_elem *curr; t_team *team; if ((teamlist_head)) { LIST_TRAVERSE(teamlist_head, curr) { if (!(team = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } xfree((void *)team); list_remove_elem(teamlist_head, &curr); } if (list_destroy(teamlist_head) < 0) return -1; teamlist_head = NULL; } return 0; } int teams_destroy(t_list * teams) { t_elem *curr; t_team *team; if ((teams)) { LIST_TRAVERSE(teams, curr) { if (!(team = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } list_remove_elem(teams, &curr); } if (list_destroy(teams) < 0) return -1; } teams = NULL; return 0; } t_team* create_team(t_account **accounts, t_clienttag clienttag) { t_team * team; int i; unsigned char size; team = (t_team*)xmalloc(sizeof(t_team)); std::memset(team, 0, sizeof(t_team)); size = 0; for (i = 0; i < MAX_TEAMSIZE; i++) { team->members[i] = accounts[i]; if ((accounts[i])) { team->teammembers[i] = account_get_uid(accounts[i]); size++; } } team->size = size; team->clienttag = clienttag; _cb_load_teams(team); storage->write_team(team); return team; } void dispose_team(t_team * team) { if ((team)) xfree((void *)team); team = NULL; } t_team * teamlist_find_team_by_accounts(t_account **accounts, t_clienttag clienttag) { return _list_find_team_by_accounts(accounts, clienttag, teamlist_head); } t_team * _list_find_team_by_accounts(t_account **accounts, t_clienttag clienttag, t_list * teamlist) { t_elem *curr; t_team *cteam; int i, j, found; unsigned char size; assert(accounts); found = 0; if ((teamlist)) { LIST_TRAVERSE(teamlist, curr) { if (!(cteam = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } size = 0; for (i = 0; i < MAX_TEAMSIZE; i++) { if (!(accounts[i])) break; size++; found = 0; for (j = 0; j < MAX_TEAMSIZE; j++) { if ((accounts[i] == cteam->members[j])) { found = 1; break; } } if (!(found)) break; } if ((found) && (clienttag == cteam->clienttag) && (size == cteam->size)) return cteam; } } return NULL; } t_team * teamlist_find_team_by_uids(unsigned int * uids, t_clienttag clienttag) { return _list_find_team_by_uids(uids, clienttag, teamlist_head); } t_team * _list_find_team_by_uids(unsigned int * uids, t_clienttag clienttag, t_list * teamlist) { t_elem *curr; t_team *cteam; int i, j, found; unsigned char size; assert(uids); found = 0; if ((teamlist)) { LIST_TRAVERSE(teamlist, curr) { if (!(cteam = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } size = 0; for (i = 0; i < MAX_TEAMSIZE; i++) { if (!(uids[i])) break; found = 0; for (j = 0; j < MAX_TEAMSIZE; j++) { if ((uids[i] == cteam->teammembers[j])) { found = 1; break; } } if (!(found)) break; } if ((found) && (clienttag == cteam->clienttag) && (size == cteam->size)) return cteam; } } return NULL; } t_team * teamlist_find_team_by_teamid(unsigned int teamid) { return _list_find_team_by_teamid(teamid, teamlist_head); } t_team* _list_find_team_by_teamid(unsigned int teamid, t_list * teamlist) { t_elem * curr; t_team * cteam; assert(teamid); if ((teamlist)) { LIST_TRAVERSE(teamlist, curr) { if (!(cteam = (t_team*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } if ((cteam->teamid == teamid)) return cteam; } } return NULL; } unsigned int team_get_teamid(t_team * team) { assert(team); return team->teamid; } t_account * team_get_member(t_team * team, int count) { assert(team); assert(count >= 0); assert(count < MAX_TEAMSIZE); return team->members[count]; } unsigned int team_get_memberuid(t_team * team, int count) { assert(team); assert(count >= 0); assert(count < MAX_TEAMSIZE); return team->teammembers[count]; } t_clienttag team_get_clienttag(t_team * team) { assert(team); return team->clienttag; } unsigned char team_get_size(t_team * team) { assert(team); return team->size; } int team_get_wins(t_team * team) { assert(team); return team->wins; } int team_get_losses(t_team * team) { assert(team); return team->losses; } int team_get_xp(t_team * team) { assert(team); return team->xp; } int team_get_level(t_team * team) { assert(team); return team->level; } int team_set_rank(t_team * team, unsigned int rank) { assert(team); team->rank = rank; return 0; } int team_get_rank(t_team * team) { assert(team); return team->rank; } time_t team_get_lastgame(t_team * team) { assert(team); return team->lastgame; } int team_inc_wins(t_team * team) { assert(team); team->wins++; return 0; } int team_inc_losses(t_team * team) { assert(team); team->losses++; return 0; } int team_update_lastgame(t_team * team) { assert(team); team->lastgame = now; return 0; } extern int team_update_xp(t_team * team, int gameresult, unsigned int opponlevel, int * xp_diff) { int xp; int mylevel; int xpdiff = 0, placeholder; xp = team->xp; //get current xp if (xp < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got negative XP"); return -1; } mylevel = team->level; //get teams level if (mylevel > W3_XPCALC_MAXLEVEL) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid level: {}", mylevel); return -1; } if (mylevel <= 0) //if level is 0 then set it to 1 mylevel = 1; if (opponlevel < 1) opponlevel = 1; switch (gameresult) { case W3_GAMERESULT_WIN: ladder_war3_xpdiff(mylevel, opponlevel, &xpdiff, &placeholder); break; case W3_GAMERESULT_LOSS: ladder_war3_xpdiff(opponlevel, mylevel, &placeholder, &xpdiff); break; default: eventlog(eventlog_level_error, __FUNCTION__, "got invalid game result: {}", gameresult); return -1; } *xp_diff = xpdiff; xp += xpdiff; if (xp < 0) xp = 0; team->xp = xp; return 0; } int team_update_level(t_team * team) { int xp, mylevel; xp = team->xp; if (xp < 0) xp = 0; mylevel = team->level; if (mylevel < 1) mylevel = 1; if (mylevel > W3_XPCALC_MAXLEVEL) { eventlog(eventlog_level_error, "account_set_sololevel", "got invalid level: {}", mylevel); return -1; } mylevel = ladder_war3_updatelevel(mylevel, xp); team->level = mylevel; return 0; } int team_set_saveladderstats(t_team * team, unsigned int gametype, int result, unsigned int opponlevel, t_clienttag clienttag) { unsigned int intrace; int xpdiff; int i; t_account * account; if (!team) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL team"); return -1; } //added for better tracking down of problems with gameresults eventlog(eventlog_level_trace, __FUNCTION__, "parsing game result for team: {} result: {}", team_get_teamid(team), (result == W3_GAMERESULT_WIN) ? "WIN" : "LOSS"); if (result == W3_GAMERESULT_WIN) { team_inc_wins(team); } if (result == W3_GAMERESULT_LOSS) { team_inc_losses(team); } team_update_xp(team, result, opponlevel, &xpdiff); team_update_level(team); team_update_lastgame(team); LadderList* ladderList = ladders.getLadderList(LadderKey(ladder_id_ateam, clienttag, ladder_sort_default, ladder_time_default)); LadderReferencedObject reference(team); ladderList->updateEntry(team->teamid, team->level, team->xp, 0, reference); storage->write_team(team); // now set currentatteam to 0 so we only get one report for each team for (i = 0; i < MAX_TEAMSIZE; i++) { if ((account = team->members[i])) { intrace = account_get_w3pgrace(account, clienttag); if (result == W3_GAMERESULT_WIN) { account_inc_racewins(account, intrace, clienttag); } if (result == W3_GAMERESULT_LOSS) { account_inc_racelosses(account, intrace, clienttag); } account_set_currentatteam(account, 0); } } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/team.h000066400000000000000000000062751462640461400171740ustar00rootroot00000000000000/* * (C) 2004 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_TEAM_TYPES #define INCLUDED_TEAM_TYPES #include #ifndef JUST_NEED_TYPES # define JUST_NEED_TYPES # include "common/list.h" # include "common/tag.h" # include "account.h" # undef JUST_NEED_TYPES #else # include "common/list.h" # include "common/tag.h" # include "account.h" #endif #define MAX_TEAMSIZE 4 namespace pvpgn { namespace bnetd { typedef struct team #ifdef TEAM_INTERNAL_ACCESS { unsigned char size; int wins; int losses; int xp; int level; int rank; unsigned int teamid; unsigned int teammembers[MAX_TEAMSIZE]; t_account * members[MAX_TEAMSIZE]; t_clienttag clienttag; std::time_t lastgame; } #endif t_team; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TEAM_PROTOS #define INCLUDED_TEAM_PROTOS namespace pvpgn { namespace bnetd { extern int teamlist_load(void); extern int teamlist_unload(void); extern int teams_destroy(t_list *); extern t_team* create_team(t_account **accounts, t_clienttag clienttag); extern void dispose_team(t_team * team); extern t_team* teamlist_find_team_by_accounts(t_account **accounts, t_clienttag clienttag); extern t_team* teamlist_find_team_by_uids(unsigned int * uids, t_clienttag clienttag); extern t_team* teamlist_find_team_by_teamid(unsigned int teamid); extern t_team* _list_find_team_by_accounts(t_account **accounts, t_clienttag clienttag, t_list * list); extern t_team* _list_find_team_by_uids(unsigned int * uids, t_clienttag clienttag, t_list * list); extern t_team* _list_find_team_by_teamid(unsigned int teamid, t_list * list); extern unsigned int team_get_teamid(t_team * team); extern t_account * team_get_member(t_team * team, int count); extern unsigned int team_get_memberuid(t_team * team, int count); extern t_clienttag team_get_clienttag(t_team * team); extern unsigned char team_get_size(t_team * team); extern int team_get_wins(t_team * team); extern int team_get_losses(t_team * team); extern int team_get_xp(t_team * team); extern int team_get_level(t_team * team); extern int team_set_rank(t_team * team, unsigned int rank); extern int team_get_rank(t_team * team); extern std::time_t team_get_lastgame(t_team * team); extern int team_inc_wins(t_team * team); extern int team_inc_losses(t_team * team); extern int team_set_saveladderstats(t_team * team, unsigned int gametype, int result, unsigned int opponlevel, t_clienttag clienttag); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/tick.cpp000066400000000000000000000031741462640461400175260ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "tick.h" #include #include #include #include "compat/gettimeofday.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { /* * This routine returns the number of miliseconds that have passed since one second * before it is first called. This is used for timing fields in some packets. */ extern unsigned int get_ticks(void) { static int first = 1; static long beginsec; struct timeval tv; if (gettimeofday(&tv, NULL) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not get std::time (gettimeofday: {})", std::strerror(errno)); return 0; } if (first) { beginsec = tv.tv_sec - 1; first = 0; } return (unsigned int)((tv.tv_sec - beginsec) * 1000 + tv.tv_usec / 1000); } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/tick.h000066400000000000000000000017171462640461400171740ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TICK_PROTOS #define INCLUDED_TICK_PROTOS namespace pvpgn { namespace bnetd { extern unsigned int get_ticks(void); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/timer.cpp000066400000000000000000000064461462640461400177210ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define TIMER_INTERNAL_ACCESS #include "common/setup_before.h" #include "timer.h" #include #include "common/elist.h" #include "connection.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_elist timerlist_head; extern int timerlist_add_timer(t_connection * owner, std::time_t when, t_timer_cb cb, t_timer_data data) { t_timer * timer, *ctimer; t_elist * curr; if (!owner) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL owner"); return -1; } timer = (t_timer*)xmalloc(sizeof(t_timer)); timer->owner = owner; timer->when = when; timer->cb = cb; timer->data = data; /* try to preserve a when based order */ elist_for_each(curr, &timerlist_head) { ctimer = elist_entry(curr, t_timer, timers); if (ctimer->when > when) break; } elist_add_tail(curr, &timer->timers); /* add it to the t_conn timers list */ elist_add_tail(conn_get_timer(owner), &timer->owners); return 0; } extern int timerlist_del_all_timers(t_connection * owner) { t_elist * curr, *save; t_timer * timer; if (!owner) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL owner"); return -1; } elist_for_each_safe(curr, conn_get_timer(owner), save) { timer = elist_entry(curr, t_timer, owners); if (timer->cb) timer->cb(timer->owner, (std::time_t)0, timer->data); elist_del(&timer->owners); elist_del(&timer->timers); xfree((void*)timer); } return 0; } extern int timerlist_check_timers(std::time_t when) { t_elist * curr, *save; t_timer * timer; elist_for_each_safe(curr, &timerlist_head, save) { timer = elist_entry(curr, t_timer, timers); if (timer->owner && timer->when < when) { if (timer->cb) timer->cb(timer->owner, timer->when, timer->data); elist_del(&timer->owners); elist_del(&timer->timers); xfree((void*)timer); } else break; /* beeing sorted there is no need to go beyond this point */ } return 0; } extern int timerlist_create(void) { elist_init(&timerlist_head); return 0; } extern int timerlist_destroy(void) { t_elist * curr, *save; t_timer * timer; elist_for_each_safe(curr, &timerlist_head, save) { timer = elist_entry(curr, t_timer, timers); elist_del(&timer->owners); elist_del(&timer->timers); xfree((void*)timer); } elist_init(&timerlist_head); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/timer.h000066400000000000000000000042221462640461400173540ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_TIMER_TYPES #define INCLUDED_TIMER_TYPES #include #ifdef JUST_NEED_TYPES # include "connection.h" #else # define JUST_NEED_TYPES # include "connection.h" # undef JUST_NEED_TYPES #endif #include "common/elist.h" namespace pvpgn { namespace bnetd { typedef union { unsigned long n; void * p; } t_timer_data; typedef void(*t_timer_cb)(t_connection * owner, std::time_t when, t_timer_data data); typedef struct timer_struct #ifdef TIMER_INTERNAL_ACCESS { t_connection * owner; /* who to notify */ std::time_t when; /* when the timer expires */ t_timer_cb cb; /* what to call */ t_timer_data data; /* data argument */ t_elist owners; /* list to the setup timers of same owner */ t_elist timers; /* timers list, used for final cleaning */ } #endif t_timer; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TIMER_PROTOS #define INCLUDED_TIMER_PROTOS #include #define JUST_NEED_TYPES #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int timerlist_create(void); extern int timerlist_destroy(void); extern int timerlist_add_timer(t_connection * owner, std::time_t when, t_timer_cb cb, t_timer_data data); extern int timerlist_del_all_timers(t_connection * owner); extern int timerlist_check_timers(std::time_t when); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/topic.cpp000066400000000000000000000153131462640461400177100ustar00rootroot00000000000000/* * Copyright (C) 2015 xboi209 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "common/setup_before.h" #include "topic.h" #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/field_sizes.h" #include "message.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { std::vector> class_topic::class_topiclist::Head; bool class_topic::class_topiclist::IsHeadLoaded = false; class_topic::class_topic() { //Already loaded, do not load again if (this->topiclist.IsHeadLoaded == true) return; std::ifstream topicfile_stream(prefs_get_topicfile()); if (!topicfile_stream) { eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file"); return; } std::string strLine; std::smatch match; const std::regex rgx("(.*)\t(.*)"); // tab character separates the channel name and topic //loop through each line in topic file while (std::getline(topicfile_stream, strLine)) { //skip empty lines if (strLine.empty() == true) continue; if (!std::regex_search(strLine, match, rgx)) { eventlog(eventlog_level_error, __FUNCTION__, "Invalid line in topic file ({})", strLine.c_str()); continue; } //check if current line in file already exists in Head if (this->topiclist.get(match[1].str()) != nullptr) { eventlog(eventlog_level_error, __FUNCTION__, "Duplicate line for channel {} in topic file", match[1].str().c_str()); continue; } //save current line to Head this->topiclist.add(match[1].str(), match[2].str(), true); } this->topiclist.IsHeadLoaded = true; } //Get channel_name's topic string std::string class_topic::get(const std::string channel_name) { if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length"); return std::string(); } auto topic = this->topiclist.get(channel_name); if (topic == nullptr) return std::string(); return topic->topicstr; } //Sets channel_name's topic bool class_topic::set(const std::string channel_name, const std::string topic_text, bool do_save) { if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length"); return false; } if ((topic_text.size() + 1) > MAX_TOPIC_LEN || topic_text.empty() == true) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid topic length"); return false; } auto topic = this->topiclist.get(channel_name); if (topic != nullptr) { eventlog(eventlog_level_trace, __FUNCTION__, "Setting <{}>'s topic to <{}>", channel_name.c_str(), topic_text.c_str()); topic->topicstr = topic_text; } else { eventlog(eventlog_level_trace, __FUNCTION__, "Adding <{}:{}> to topiclist", channel_name.c_str(), topic_text.c_str()); this->topiclist.add(channel_name, topic_text, do_save); } if (do_save == true) { if (this->topiclist.save() == false) { eventlog(eventlog_level_error, __FUNCTION__, "error saving topic list"); return false; } } return true; } // Displays channel_name's topic to connection c bool class_topic::display(t_connection * c, const std::string channel_name) { if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length"); return false; } auto topic = this->topiclist.get(channel_name); if (topic == nullptr) return false; auto topicstr = topic->topicstr; if (topicstr.empty()) { eventlog(eventlog_level_error, __FUNCTION__, "topic is empty"); return false; } //send parts of topic string as separate message if there's a newline character std::regex rgx("\\\\n+"); std::sregex_token_iterator iter(topicstr.begin(), topicstr.end(), rgx, -1), end; for (bool first = true; iter != end; ++iter) { std::string msg(iter->str()); if (first == true) { msg.insert(0, topic->channel_name + " topic: "); first = false; } message_send_text(c, message_type_info, c, msg); } return true; } //Get t_topic pointer of channel_name std::shared_ptr class_topic::class_topiclist::get(const std::string channel_name) { for (auto topic : this->Head) { if (strcasecmp(channel_name.c_str(), topic->channel_name.c_str()) == 0) return topic; } eventlog(eventlog_level_debug, __FUNCTION__, "returning nullptr"); return nullptr; } //Saves data from Head vector to topic file bool class_topic::class_topiclist::save() { std::fstream topicfile_stream(prefs_get_topicfile(), std::ofstream::app); if (!topicfile_stream) { eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file"); return false; } std::string strLine; std::smatch match; const std::regex rgx("(.*)\t(.*)"); // tab character separates the channel name and topic //Check if data in Head vector already exists in topic file while (std::getline(topicfile_stream, strLine)) { if (!std::regex_search(strLine, match, rgx)) { eventlog(eventlog_level_error, __FUNCTION__, "Invalid line in topic file ({})", strLine.c_str()); continue; } for (auto topic : this->Head) { if (topic->save == true) { if (match[1].str() == topic->channel_name) { break; } else { topicfile_stream << topic->channel_name << "\t" << topic->topicstr << std::endl; break; } } } } return true; } //Adds a new pointer to the Head vector void class_topic::class_topiclist::add(std::string channel_name, std::string topic_text, bool do_save) { auto topic = std::make_shared(class_topic::t_topic{ channel_name, topic_text, do_save }); this->Head.push_back(std::move(topic)); } } }pvpgn-1.99.7.2.1+dfsg/src/bnetd/topic.h000066400000000000000000000034441462640461400173570ustar00rootroot00000000000000/* * Copyright (C) 2015 xboi209 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef INCLUDED_TOPIC_TYPES #define INCLUDED_TOPIC_TYPES #include #include #include #include "connection.h" #include "common/list.h" #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TOPIC_PROTOS #define INCLUDED_TOPIC_PROTOS namespace pvpgn { namespace bnetd { class class_topic { //all public member functions of class_topic verify channel name and topic length public: class_topic(); std::string get(const std::string channel_name); bool set(const std::string channel_name, const std::string topic_text, bool do_save); bool display(t_connection * c, const std::string channel_name); private: struct t_topic { std::string channel_name; std::string topicstr; bool save; }; class class_topiclist { public: static bool IsHeadLoaded; std::shared_ptr get(const std::string channel_name); bool save(); void add(std::string channel_name, std::string topic_text, bool do_save); private: static std::vector> Head; } topiclist; }; } //namespace bnetd } //namespace pvpgn #endif #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/tournament.cpp000066400000000000000000000530441462640461400207710ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "tournament.h" #include #include #include #include #include #include "common/list.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/util.h" #include "common/tag.h" /* #include "common/packet.h" */ #include "anongame_maplists.h" #include "account.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_tournament_info * tournament_info = NULL; static t_list * tournament_head = NULL; static int tournamentlist_create(void); static int _gamelist_destroy(void); static t_tournament_user * tournament_get_user(t_account * account); //static int tournament_get_in_game_status(t_account * account); /*****/ static int tournamentlist_create(void) { tournament_head = list_create(); return 0; } static int _gamelist_destroy(void) { t_elem * curr; t_tournament_user * user; if (tournament_head) { LIST_TRAVERSE(tournament_head, curr) { if (!(user = (t_tournament_user*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "tournament list contains NULL item"); continue; } if (list_remove_elem(tournament_head, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); if (user->name) xfree((void *)user->name); /* avoid warning */ xfree(user); } if (list_destroy(tournament_head) < 0) return -1; tournament_head = NULL; } return 0; } /*****/ extern int tournament_check_client(t_clienttag clienttag) { if (clienttag == CLIENTTAG_WAR3XP_UINT && tournament_info->game_client == 2) return 1; if (clienttag == CLIENTTAG_WARCRAFT3_UINT && tournament_info->game_client == 1) return 1; return -1; } extern int tournament_signup_user(t_account * account) { t_tournament_user * user; if (!(account)) return -1; if ((user = tournament_get_user(account))) { eventlog(eventlog_level_info, __FUNCTION__, "user \"{}\" already signed up in tournament", account_get_name(account)); return 0; } user = (t_tournament_user*)xmalloc(sizeof(t_tournament_user)); user->name = xstrdup(account_get_name(account)); user->wins = 0; user->losses = 0; user->ties = 0; user->in_game = 0; user->in_finals = 0; list_prepend_data(tournament_head, user); eventlog(eventlog_level_info, __FUNCTION__, "added user \"{}\" to tournament", account_get_name(account)); return 0; } static t_tournament_user * tournament_get_user(t_account * account) { t_elem const * curr; t_tournament_user * user; if (tournament_head) LIST_TRAVERSE(tournament_head, curr) { user = (t_tournament_user*)elem_get_data(curr); if (std::strcmp(user->name, account_get_name(account)) == 0) return user; } return NULL; } extern int tournament_user_signed_up(t_account * account) { if (!(tournament_get_user(account))) return -1; return 0; } extern int tournament_add_stat(t_account * account, int stat) { t_tournament_user * user; if (!(user = tournament_get_user(account))) return -1; if (stat == 1) user->wins++; if (stat == 2) user->losses++; if (stat == 3) user->ties++; return 0; } extern int tournament_get_stat(t_account * account, int stat) { t_tournament_user * user; if (!(user = tournament_get_user(account))) return 0; if (stat == 1) return user->wins; if (stat == 2) return user->losses; if (stat == 3) return user->ties; return 0; } extern int tournament_get_player_score(t_account * account) { t_tournament_user * user; int score; if (!(user = tournament_get_user(account))) return 0; score = user->wins * 3 + user->ties - user->losses; if (score < 0) return 0; return score; } extern int tournament_set_in_game_status(t_account * account, int status) { t_tournament_user * user; if (!(user = tournament_get_user(account))) return -1; user->in_game = status; return 0; } /* static int tournament_get_in_game_status(t_account * account) { t_tournament_user * user; if (!(user = tournament_get_user(account))) return 0; return user->in_game; } */ extern int tournament_get_in_finals_status(t_account * account) { t_tournament_user * user; if (!(user = tournament_get_user(account))) return 0; return user->in_finals; } extern int tournament_get_game_in_progress(void) { t_elem const * curr; t_tournament_user * user; if (tournament_head) LIST_TRAVERSE_CONST(tournament_head, curr) { user = (t_tournament_user*)elem_get_data(curr); if (user->in_game == 1) return 1; } return 0; } extern int tournament_is_arranged(void) { if (tournament_info->game_selection == 2) return 1; else return 0; } extern int tournament_get_totalplayers(void) { return tournament_info->game_type * 2; } void tournament_check_date(unsigned int *mon, unsigned int *mday, unsigned int *year, unsigned int *hour, unsigned int *min, unsigned int *sec, char const * caller) { if (*mon > 12) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid month ({}) in {}", *mon, caller); *mon = 12; } if (*mday > 31) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid mday ({}) in {}", *mday, caller); *mday = 31; } if (*hour > 23) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid hour ({}) from {}", *hour, caller); *hour = 23; } if (*min > 59) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid min ({}) from {}", *min, caller); *min = 59; } if (*sec > 59) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid sec ({}) from {}", *sec, caller); *sec = 59; } return; } /*****/ extern int tournament_init(char const * filename) { std::FILE * fp; unsigned int line, pos, mon, day, year, hour, min, sec; char *buff, *temp, *pointer, *value; char format[30]; char *variable; char *sponsor = NULL; char *have_sponsor = NULL; char *have_icon = NULL; struct std::tm * timestamp = (struct std::tm*)xmalloc(sizeof(struct std::tm)); std::sprintf(format, "%%02u/%%02u/%%04u %%02u:%%02u:%%02u"); tournament_info = (t_tournament_info*)xmalloc(sizeof(t_tournament_info)); tournament_info->start_preliminary = 0; tournament_info->end_signup = 0; tournament_info->end_preliminary = 0; tournament_info->start_round_1 = 0; tournament_info->start_round_2 = 0; tournament_info->start_round_3 = 0; tournament_info->start_round_4 = 0; tournament_info->tournament_end = 0; tournament_info->game_selection = 1; /* Default to PG */ tournament_info->game_type = 1; /* Default to 1v1 */ tournament_info->game_client = 2; /* Default to FT */ tournament_info->races = 0x3F; /* Default to all races */ tournament_info->format = xstrdup(""); tournament_info->sponsor = xstrdup(""); tournament_info->thumbs_down = 0; anongame_tournament_maplists_destroy(); if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); xfree((void *)timestamp); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); xfree((void *)timestamp); return -1; } for (line = 1; (buff = file_get_line(fp)); line++) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if ((temp = std::strrchr(buff, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } if (std::strcmp(buff, "[MAPS]") == 0) { char *clienttag, *mapname, *mname; t_clienttag ctag; for (; (buff = file_get_line(fp));) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if ((temp = std::strrchr(buff, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } /* FIXME: use next_token() */ if (!(clienttag = std::strtok(buff, " \t"))) { /* std::strtok modifies the string it is passed */ continue; } if (std::strlen(clienttag) != 4) { continue; } if (std::strcmp(buff, "[ENDMAPS]") == 0) { break; } if (!(mapname = std::strtok(NULL, " \t"))) { continue; } if (!tag_check_client((ctag = tag_case_str_to_uint(clienttag)))) { continue; } mname = xstrdup(mapname); anongame_add_tournament_map(ctag, mname); eventlog(eventlog_level_trace, __FUNCTION__, "added tournament map \"{}\" for {}", mname, clienttag); xfree(mname); } } else { variable = buff; pointer = std::strchr(variable, '='); for (pointer--; pointer[0] == ' ' || pointer[0] == '\t'; pointer--); pointer[1] = '\0'; pointer++; pointer++; pointer = std::strchr(pointer, '='); pointer++; if (std::strcmp(variable, "start_preliminary") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->start_preliminary = std::mktime(timestamp); } else if (std::strcmp(variable, "end_signup") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->end_signup = std::mktime(timestamp); } else if (std::strcmp(variable, "end_preliminary") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->end_preliminary = std::mktime(timestamp); } else if (std::strcmp(variable, "start_round_1") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->start_round_1 = std::mktime(timestamp); } else if (std::strcmp(variable, "start_round_2") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->start_round_2 = std::mktime(timestamp); } else if (std::strcmp(variable, "start_round_3") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->start_round_3 = std::mktime(timestamp); } else if (std::strcmp(variable, "start_round_4") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->start_round_4 = std::mktime(timestamp); } else if (std::strcmp(variable, "tournament_end") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; std::sscanf(value, format, &mon, &day, &year, &hour, &min, &sec); tournament_check_date(&mon, &day, &year, &hour, &min, &sec, variable); timestamp->tm_mon = mon - 1; timestamp->tm_mday = day; timestamp->tm_year = year - 1900; timestamp->tm_hour = hour; timestamp->tm_min = min; timestamp->tm_sec = sec; timestamp->tm_isdst = -1; tournament_info->tournament_end = std::mktime(timestamp); } else if (std::strcmp(variable, "game_selection") == 0) { if (std::atoi(pointer) >= 1 && std::atoi(pointer) <= 2) tournament_info->game_selection = std::atoi(pointer); } else if (std::strcmp(variable, "game_type") == 0) { if (std::atoi(pointer) >= 1 && std::atoi(pointer) <= 4) tournament_info->game_type = std::atoi(pointer); } else if (std::strcmp(variable, "game_client") == 0) { if (std::atoi(pointer) >= 1 && std::atoi(pointer) <= 2) tournament_info->game_client = std::atoi(pointer); } else if (std::strcmp(variable, "format") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; if (tournament_info->format) xfree((void *)tournament_info->format); tournament_info->format = xstrdup(value); } else if (std::strcmp(variable, "races") == 0) { unsigned int intvalue = 0; unsigned int i; pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; size_t valuelen = std::strlen(value); for (i = 0; i < valuelen; i++) { if (value[i] == 'H') intvalue = intvalue | 0x01; if (value[i] == 'O') intvalue = intvalue | 0x02; if (value[i] == 'N') intvalue = intvalue | 0x04; if (value[i] == 'U') intvalue = intvalue | 0x08; if (value[i] == 'R') intvalue = intvalue | 0x20; } if (intvalue == 0 || intvalue == 0x2F) intvalue = 0x3F; /* hack to make all races availiable */ tournament_info->races = intvalue; } else if (std::strcmp(variable, "sponsor") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; have_sponsor = xstrdup(value); } else if (std::strcmp(variable, "icon") == 0) { pointer = std::strchr(pointer, '\"'); pointer++; value = pointer; pointer = std::strchr(pointer, '\"'); pointer[0] = '\0'; have_icon = xstrdup(value); } else if (std::strcmp(variable, "thumbs_down") == 0) { tournament_info->thumbs_down = std::atoi(pointer); } else eventlog(eventlog_level_error, __FUNCTION__, "bad option \"{}\" in \"{}\"", variable, filename); if (have_sponsor && have_icon) { sponsor = (char*)xmalloc(std::strlen(have_sponsor) + 6); if (std::strlen(have_icon) == 4) std::sprintf(sponsor, "%c%c%c%c,%s", have_icon[3], have_icon[2], have_icon[1], have_icon[0], have_sponsor); else if (std::strlen(have_icon) == 2) std::sprintf(sponsor, "%c%c3W,%s", have_icon[1], have_icon[0], have_sponsor); else { std::sprintf(sponsor, "PX3W,%s", have_sponsor); /* default to standard FT icon */ eventlog(eventlog_level_warn, __FUNCTION__, "bad icon length, using W3XP"); } if (tournament_info->sponsor) xfree((void *)tournament_info->sponsor); tournament_info->sponsor = xstrdup(sponsor); xfree((void *)have_sponsor); xfree((void *)have_icon); xfree((void *)sponsor); have_sponsor = NULL; have_icon = NULL; } } } if (have_sponsor) xfree((void *)have_sponsor); if (have_icon) xfree((void *)have_icon); xfree((void *)timestamp); file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); /* check if we have timestamps for all the times */ /* if not disable tournament by setting "start_preliminary" to 0 */ if (tournament_info->end_signup == 0 || tournament_info->end_preliminary == 0 || tournament_info->start_round_1 == 0 || tournament_info->start_round_2 == 0 || tournament_info->start_round_3 == 0 || tournament_info->start_round_4 == 0 || tournament_info->tournament_end == 0) { tournament_info->start_preliminary = 0; eventlog(eventlog_level_warn, __FUNCTION__, "one or more timestamps for tournaments is not valid, tournament has been disabled"); } else { tournamentlist_create(); } return 0; } extern int tournament_destroy(void) { if (tournament_info) { if (tournament_info->format) xfree((void *)tournament_info->format); if (tournament_info->sponsor) xfree((void *)tournament_info->sponsor); xfree((void *)tournament_info); } tournament_info = NULL; _gamelist_destroy(); return 0; } extern int tournament_reload(char const * filename) { std::time_t tm; std::time(&tm); if ((tm >= tournament_info->start_preliminary) && (tm <= tournament_info->tournament_end)) { eventlog(eventlog_level_info, __FUNCTION__, "unable to reload tournament, tournament is in process"); return -1; } tournament_destroy(); return tournament_init(filename); } /*****/ extern unsigned int tournament_get_start_preliminary(void) { return tournament_info->start_preliminary; } extern unsigned int tournament_get_end_signup(void) { return tournament_info->end_signup; } extern unsigned int tournament_get_end_preliminary(void) { return tournament_info->end_preliminary; } extern unsigned int tournament_get_start_round_1(void) { return tournament_info->start_round_1; } extern unsigned int tournament_get_start_round_2(void) { return tournament_info->start_round_2; } extern unsigned int tournament_get_start_round_3(void) { return tournament_info->start_round_3; } extern unsigned int tournament_get_start_round_4(void) { return tournament_info->start_round_4; } extern unsigned int tournament_get_tournament_end(void) { return tournament_info->tournament_end; } extern unsigned int tournament_get_game_selection(void) { return tournament_info->game_selection; } extern unsigned int tournament_get_game_type(void) { return tournament_info->game_type; } extern unsigned int tournament_get_races(void) { return tournament_info->races; } extern char * tournament_get_format(void) { return tournament_info->format; } extern char * tournament_get_sponsor(void) { return tournament_info->sponsor; } extern unsigned int tournament_get_thumbs_down(void) { return tournament_info->thumbs_down; } } } /****/ pvpgn-1.99.7.2.1+dfsg/src/bnetd/tournament.h000066400000000000000000000067471462640461400204460ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_TOURNAMENT_TYPES #define INCLUDED_TOURNAMENT_TYPES #include namespace pvpgn { namespace bnetd { typedef struct { std::time_t start_preliminary; std::time_t end_signup; std::time_t end_preliminary; std::time_t start_round_1; std::time_t start_round_2; std::time_t start_round_3; std::time_t start_round_4; std::time_t tournament_end; unsigned int game_selection; unsigned int game_type; unsigned int game_client; unsigned int races; char * format; char * sponsor; /* format: "ricon,sponsor" * ricon = W3+icon reversed , if 2 char icon is selected * or reversed icon if 4 char icon is selected * ie. "4R3W,The PvPGN Team" */ unsigned int thumbs_down; } t_tournament_info; typedef struct { char * name; unsigned int wins; unsigned int losses; unsigned int ties; int in_game; int in_finals; } t_tournament_user; } } #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TOURNAMENT_PROTOS #define INCLUDED_TOURNAMENT_PROTOS #define JUST_NEED_TYPES # include "account.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern int tournament_signup_user(t_account * account); extern int tournament_user_signed_up(t_account * account); extern int tournament_add_stat(t_account * account, int stat); extern int tournament_get_stat(t_account * account, int stat); extern int tournament_get_player_score(t_account * account); extern int tournament_set_in_game_status(t_account * account, int status); extern int tournament_get_in_finals_status(t_account * account); extern int tournament_get_game_in_progress(void); extern int tournament_check_client(t_clienttag clienttag); extern int tournament_get_totalplayers(void); extern int tournament_is_arranged(void); /*****/ extern int tournament_init(char const * filename); extern int tournament_destroy(void); extern int tournament_reload(char const * filename); /*****/ extern unsigned int tournament_get_start_preliminary(void); extern unsigned int tournament_get_end_signup(void); extern unsigned int tournament_get_end_preliminary(void); extern unsigned int tournament_get_start_round_1(void); extern unsigned int tournament_get_start_round_2(void); extern unsigned int tournament_get_start_round_3(void); extern unsigned int tournament_get_start_round_4(void); extern unsigned int tournament_get_tournament_end(void); extern unsigned int tournament_get_game_selection(void); extern unsigned int tournament_get_game_type(void); extern unsigned int tournament_get_races(void); extern char * tournament_get_format(void); extern char * tournament_get_sponsor(void); extern unsigned int tournament_get_thumbs_down(void); } } /*****/ #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/tracker.cpp000066400000000000000000000133251462640461400202260ustar00rootroot00000000000000/* * Copyright (C) 1999 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define TRACKER_INTERNAL_ACCESS #define SERVER_INTERNAL_ACCESS #include "tracker.h" #include #include #include "compat/psock.h" #include "compat/strerror.h" #include "compat/uname.h" #include "common/eventlog.h" #include "common/list.h" #include "common/addr.h" #include "common/tracker.h" #include "common/bn_type.h" #include "prefs.h" #include "connection.h" #include "channel.h" #include "game.h" #include "server.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { static t_addrlist * track_servers = NULL; extern int tracker_set_servers(char const * servers) { t_addr const * addr; t_elem const * curr; char temp[32]; if (track_servers && addrlist_destroy(track_servers) < 0) eventlog(eventlog_level_error, __FUNCTION__, "unable to destroy tracker list"); if (!servers) { track_servers = NULL; return 0; } if (!(track_servers = addrlist_create(servers, INADDR_LOOPBACK, BNETD_TRACK_PORT))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create tracking server list"); return -1; } LIST_TRAVERSE_CONST(track_servers, curr) { addr = (t_addr*)elem_get_data(curr); if (!addr_get_addr_str(addr, temp, sizeof(temp))) std::strcpy(temp, "x.x.x.x:x"); eventlog(eventlog_level_info, __FUNCTION__, "tracking packets will be sent to {}", temp); } return 0; } extern int tracker_send_report(t_addrlist const * laddrs) { t_addr const * addrl; t_elem const * currl; t_addr const * addrt; t_elem const * currt; struct sockaddr_in tempaddr; t_laddr_info * laddr_info; char tempa[64]; char tempb[64]; t_trackpacket packet = {}; if (addrlist_get_length(track_servers) > 0) { bn_short_nset(&packet.packet_version, static_cast(TRACK_VERSION)); /* packet.port is set below */ bn_int_nset(&packet.flags, 0); std::snprintf(reinterpret_cast(packet.server_location), sizeof packet.server_location, "%s", prefs_get_location()); std::snprintf(reinterpret_cast(packet.software), sizeof packet.software, PVPGN_SOFTWARE); std::snprintf(reinterpret_cast(packet.version), sizeof packet.version, PVPGN_VERSION); std::snprintf(reinterpret_cast(packet.server_desc), sizeof packet.server_desc, "%s", prefs_get_description()); std::snprintf(reinterpret_cast(packet.server_url), sizeof packet.server_url, "%s", prefs_get_url()); std::snprintf(reinterpret_cast(packet.contact_name), sizeof packet.contact_name, "%s", prefs_get_contact_name()); std::snprintf(reinterpret_cast(packet.contact_email), sizeof packet.contact_email, "%s", prefs_get_contact_email()); bn_int_nset(&packet.users, connlist_login_get_length()); bn_int_nset(&packet.channels, channellist_get_length()); bn_int_nset(&packet.games, gamelist_get_length()); bn_int_nset(&packet.uptime, server_get_uptime()); bn_int_nset(&packet.total_logins, connlist_total_logins()); bn_int_nset(&packet.total_games, gamelist_total_games()); static struct utsname utsbuf = {}; if (utsbuf.sysname[0] == '\0') { if (uname(&utsbuf) != 0) { eventlog(eventlog_level_warn, __FUNCTION__, "could not get platform info (uname: {})", pstrerror(errno)); std::snprintf(reinterpret_cast(packet.platform), sizeof packet.platform, ""); } } std::snprintf(reinterpret_cast(packet.platform), sizeof packet.platform, "%s", utsbuf.sysname); LIST_TRAVERSE_CONST(laddrs, currl) { addrl = (t_addr*)elem_get_data(currl); if (!(laddr_info = (t_laddr_info*)addr_get_data(addrl).p)) { eventlog(eventlog_level_error, __FUNCTION__, "address data is NULL"); continue; } if (laddr_info->type != laddr_type_bnet) continue; /* don't report IRC, telnet, and other non-game ports */ bn_short_nset(&packet.port, addr_get_port(addrl)); LIST_TRAVERSE_CONST(track_servers, currt) { addrt = (t_addr*)elem_get_data(currt); std::memset(&tempaddr, 0, sizeof(tempaddr)); tempaddr.sin_family = PSOCK_AF_INET; tempaddr.sin_port = htons(addr_get_port(addrt)); tempaddr.sin_addr.s_addr = htonl(addr_get_ip(addrt)); if (!addr_get_addr_str(addrl, tempa, sizeof(tempa))) std::strcpy(tempa, "x.x.x.x:x"); if (!addr_get_addr_str(addrt, tempb, sizeof(tempb))) std::strcpy(tempa, "x.x.x.x:x"); /* eventlog(eventlog_level_debug,__FUNCTION__,"sending tracking info from {} to {}",tempa,tempb); */ if (psock_sendto(laddr_info->usocket, &packet, sizeof(packet), 0, (struct sockaddr *)&tempaddr, (psock_t_socklen)sizeof(tempaddr)) < 0) eventlog(eventlog_level_warn, __FUNCTION__, "could not send tracking information from {} to {} (psock_sendto: {})", tempa, tempb, pstrerror(errno)); } } } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/tracker.h000066400000000000000000000004101462640461400176620ustar00rootroot00000000000000#ifndef BNETD_TRACKER_H_INCLUDED #define BNETD_TRACKER_H_INCLUDED #include "common/addr.h" namespace pvpgn { namespace bnetd { extern int tracker_set_servers(char const * servers); extern int tracker_send_report(t_addrlist const * addrs); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/udptest_send.cpp000066400000000000000000000063211462640461400212720ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "udptest_send.h" #include #include #include "compat/psock.h" #include "compat/strerror.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/bn_type.h" #include "common/hexdump.h" #include "common/addr.h" #include "common/tag.h" #include "common/setup_after.h" extern std::FILE * hexstrm; /* from main.c */ namespace pvpgn { namespace bnetd { extern int udptest_send(t_connection const * c) { t_packet * upacket; struct sockaddr_in caddr; unsigned int tries, successes; std::memset(&caddr, 0, sizeof(caddr)); caddr.sin_family = PSOCK_AF_INET; caddr.sin_port = htons(conn_get_game_port(c)); caddr.sin_addr.s_addr = htonl(conn_get_game_addr(c)); for (tries = successes = 0; successes != 2 && tries < 5; tries++) { if (!(upacket = packet_create(packet_class_udp))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not allocate memory for packet", conn_get_socket(c)); continue; } packet_set_size(upacket, sizeof(t_server_udptest)); packet_set_type(upacket, SERVER_UDPTEST); bn_int_tag_set(&upacket->u.server_udptest.bnettag, BNETTAG); if (hexstrm) { std::fprintf(hexstrm, "%d: send class=%s[0x%02x] type=%s[0x%04x] ", conn_get_game_socket(c), packet_get_class_str(upacket), (unsigned int)packet_get_class(upacket), packet_get_type_str(upacket, packet_dir_from_server), packet_get_type(upacket)); std::fprintf(hexstrm, "from=%s ", addr_num_to_addr_str(conn_get_game_addr(c), conn_get_game_port(c))); std::fprintf(hexstrm, "to=%s ", addr_num_to_addr_str(ntohl(caddr.sin_addr.s_addr), ntohs(caddr.sin_port))); std::fprintf(hexstrm, "length=%u\n", packet_get_size(upacket)); hexdump(hexstrm, packet_get_raw_data(upacket, 0), packet_get_size(upacket)); } if (psock_sendto(conn_get_game_socket(c), packet_get_raw_data_const(upacket, 0), packet_get_size(upacket), 0, (struct sockaddr *)&caddr, (psock_t_socklen)sizeof(caddr)) != (int)packet_get_size(upacket)) eventlog(eventlog_level_error, __FUNCTION__, "[{}] failed to send UDPTEST to {} (attempt {}) (psock_sendto: {})", conn_get_socket(c), addr_num_to_addr_str(ntohl(caddr.sin_addr.s_addr), conn_get_game_port(c)), tries + 1, pstrerror(psock_errno())); else successes++; packet_del_ref(upacket); } if (successes != 2) return -1; return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/udptest_send.h000066400000000000000000000017771462640461400207510ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_UDPTEST_SEND_PROTOS #define INCLUDED_UDPTEST_SEND_PROTOS #include "connection.h" namespace pvpgn { namespace bnetd { extern int udptest_send(t_connection const * c); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bnetd/userlog.cpp000066400000000000000000000227051462640461400202550ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "compat/pdir.h" #include "compat/mkdir.h" #include "common/util.h" #include "common/eventlog.h" #include "common/xstring.h" #include "connection.h" #include "message.h" #include "account.h" #include "account_wrap.h" #include "command_groups.h" #include "command.h" #include "prefs.h" #include "helpfile.h" #include "i18n.h" #include "userlog.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { // max output lines to send user from /log command result static const int userlog_max_output_lines = 50; static std::vector userlog_commands; extern void userlog_init() { char * temp; char const * tok; userlog_commands.clear(); // fill command list that must be logged if (const char * cmdlist = prefs_get_log_command_list()) { temp = xstrdup(cmdlist); tok = std::strtok(temp, ","); /* std::strtok modifies the string it is passed */ while (tok) { userlog_commands.push_back(tok); tok = std::strtok(NULL, ","); } xfree(temp); } } // add new line at the end of log file extern void userlog_append(t_account * account, const char * text) { // is logging enabled? if (!prefs_get_log_commands()) return; unsigned int groups = 0; const char * cglist = prefs_get_log_command_groups(); size_t cglistlen = std::strlen(cglist); // convert string groups from config to integer for (std::size_t i = 0; i < cglistlen; i++) { if (cglist[i] == '1') groups |= 1; else if (cglist[i] == '2') groups |= 2; else if (cglist[i] == '3') groups |= 4; else if (cglist[i] == '4') groups |= 8; else if (cglist[i] == '5') groups |= 16; else if (cglist[i] == '6') groups |= 32; else if (cglist[i] == '7') groups |= 64; else if (cglist[i] == '8') groups |= 128; } // log only commands for admins/operators and users in "groups" defined in config if (!account_is_operator_or_admin(account, NULL) && !(account_get_command_groups(account) & groups)) return; bool is_cmd_found = false; // if command list empty then log all commands if (userlog_commands.size() == 0) is_cmd_found = true; else { // get command name std::vector args = split_command(text, 0); std::string cmd = args[0]; // find command in defined command list for (std::vector::iterator it = userlog_commands.begin(); it != userlog_commands.end(); ++it) { if (*it == cmd) { is_cmd_found = true; break; } } } if (!is_cmd_found) return; // get time string char time_string[USEREVENT_TIME_MAXLEN]; struct std::tm * tmnow; std::time_t now; std::time(&now); if (!(tmnow = std::localtime(&now))) std::strcpy(time_string, "?"); else std::strftime(time_string, USEREVENT_TIME_MAXLEN, USEREVENT_TIME_FORMAT, tmnow); std::string filename = userlog_filename(account_get_name(account), true); if (FILE *fp = fopen(filename.c_str(), "a")) { // append date and text std::fprintf(fp, "[%s] %s\n", time_string, text); std::fclose(fp); } else { ERROR1("could not write into user log file \"{}\"", filename); } } // read "count" lines from the end starting from "startline" extern std::map userlog_read(const char* username, long startline, const char* search_substr) { if (!username) throw std::runtime_error("username is a nullptr"); FILE* fp = std::fopen(userlog_filename(username).c_str(), "r"); if (!fp) throw std::runtime_error("Could not open userlog"); // set position to the end of file std::fseek(fp, 0, SEEK_END); long pos = std::ftell(fp); int c = {}, prev_c = {}; std::map lines; long linecount = 0; char line[MAX_MESSAGE_LEN + 1] = {}; int linepos = 0; // read file in reverse, byte by byte do { pos--; std::fseek(fp, pos, SEEK_SET); c = std::fgetc(fp); // add char into line array if (c != '\n') line[linepos] = c; // end of line (or start of file) if ((c == '\n' && c != prev_c) || pos == -1) { // hack for large lines (instead we will receive cut line without start symbols) if (linepos == MAX_MESSAGE_LEN) { // return carriage to read whole line to(from) start pos = pos + MAX_MESSAGE_LEN; linepos = 0; } if (linepos > 0) { line[linepos] = '\0'; // set end of string strreverse(line); linepos = 0; // reset position inside line linecount++; if (linecount >= startline) { if (search_substr && std::strlen(search_substr) > 0) { if (find_substr(line, search_substr)) lines[linecount] = xstrdup(line); } else { lines[linecount] = xstrdup(line); } } // limitation of results if (lines.size() >= userlog_max_output_lines) break; } } prev_c = c; if (c != '\n' && linepos < MAX_MESSAGE_LEN) linepos++; } while (c != EOF); std::fclose(fp); return lines; } // search "substring" in user log file from the end starting from "startline" and return "count" results extern std::map userlog_find_text(const char * username, const char * search_substr, long startline) { return userlog_read(username, startline, search_substr); } // return full path for user log filename // if (force_create_path == true) then create dirs in path if not exist extern std::string userlog_filename(const char * username, bool force_create_path) { // lowercase username std::string lusername = std::string(username); std::transform(lusername.begin(), lusername.end(), lusername.begin(), ::tolower); // get first 3 symbols of account and use it in path // it will improve performance with large count of files std::string dir_prefix = lusername.substr(0, 3); std::string filepath = fmt::format("{}/{}", prefs_get_userlogdir(), dir_prefix); // create directories in path if (force_create_path) { struct stat statbuf; // create inside user dir if (stat(filepath.c_str(), &statbuf) == -1) { p_mkdir(filepath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); eventlog(eventlog_level_info, __FUNCTION__, "created user directory: {}", filepath); } } filepath += fmt::format("/{}.log", lusername.c_str()); return filepath; } // handle command // /log read user startline // /log find user substr startline extern int handle_log_command(t_connection * c, char const *text) { const char *subcommand, *username; long startline = 0; std::map lines; // split command args std::vector args = split_command(text, 4); if (args[1].empty() || args[2].empty() || (args[1].at(0) != 'r' && args[1].at(0) != 'f')) // check start symbols for subcommand { describe_command(c, args[0].c_str()); return -1; } subcommand = args[1].c_str(); // sub command username = args[2].c_str(); // username if (!accountlist_find_account(username)) { message_send_text(c, message_type_error, c, localize(c, "Invalid user.")); return -1; } std::string title = localize(c, "{}'s log output", username); // read if (subcommand[0] == 'r') { if (!args[3].empty()) startline = atoi(args[3].c_str()); try { lines = userlog_read(username, startline); } catch (...) { message_send_text(c, message_type_error, c, "Could not read user log"); return 0; } } // find else if (subcommand[0] == 'f') { if (args[3].empty()) { describe_command(c, args[0].c_str()); return -1; } const char * search = args[3].c_str(); title += localize(c, " by occurrence \"{}\"", search); if (!args[4].empty()) startline = atoi(args[4].c_str()); lines = userlog_find_text(username, search, startline); } title += ":"; message_send_text(c, message_type_info, c, title); int linelen = 0; int paddedlen = 0; std::string linenum; // send each log line to user for (std::map::reverse_iterator it = lines.rbegin(); it != lines.rend(); ++it) { int linelen = floor(log10(static_cast(abs(it->first)))) + 1; // get length of integer (line number) if (linelen > paddedlen) paddedlen = linelen; linenum = std_to_string(it->first); // pad left to max line length linenum.insert(linenum.begin(), paddedlen - linenum.size(), '0'); message_send_text(c, message_type_info, c, linenum + ": " + std::string(it->second)); } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/userlog.h000066400000000000000000000027741462640461400177260ustar00rootroot00000000000000/* * Copyright (C) 2014 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_USERLOGS_PROTOS #define INCLUDED_USERLOGS_PROTOS #include #include #define JUST_NEED_TYPES #include "connection.h" #undef JUST_NEED_TYPES namespace pvpgn { namespace bnetd { extern void userlog_init(); extern void userlog_append(t_account * account, const char * text); extern std::map userlog_read(const char* username, long startline, const char* search_substr = nullptr); extern std::map userlog_find_text(const char * username, const char * search_substr, long startline); extern std::string userlog_filename(const char * username, bool force_create_path = false); extern int handle_log_command(t_connection * c, char const *text); } } #endif #endifpvpgn-1.99.7.2.1+dfsg/src/bnetd/versioncheck.cpp000066400000000000000000000201641462640461400212550ustar00rootroot00000000000000/* * Copyright (C) 2000 Onlyer (onlyer@263.net) * Copyright (C) 2001 Ross Combs (ross@bnetd.org) * Copyright (C) 2002 Gianluigi Tiesi (sherpya@netfarm.it) * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common/setup_before.h" #include "versioncheck.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/eventlog.h" #include "common/field_sizes.h" #include "common/format.h" #include "common/hash_tuple.hpp" #include "common/proginfo.h" #include "compat/strcasecmp.h" #include "common/token.h" #include "common/util.h" #include "json/json.hpp" #include "prefs.h" #include "common/setup_after.h" using json = nlohmann::json; namespace pvpgn { namespace bnetd { std::unordered_map, VersionCheck, hash_tuple::hash>> vc_entries; std::unordered_map, std::tuple, hash_tuple::hash>> cr_entries; bool versioncheck_conf_is_loaded = false; // bool validate_file_metadata_format(const std::string& metadata); bool load_versioncheck_conf(const std::string& filename) { if (versioncheck_conf_is_loaded) { eventlog(eventlog_level_error, __FUNCTION__, "Could not load {}, a versioncheck configuration file is already loaded", filename); return false; } auto t0 = std::chrono::steady_clock::now(); std::ifstream file_stream(filename, std::ios::in); if (!file_stream.is_open()) { eventlog(eventlog_level_error, __FUNCTION__, "Could not open file \"{}\" for reading", filename); return false; } json jconf; try { file_stream >> jconf; } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "Could not parse {}: {}", filename, e.what()); return false; } vc_entries.reserve(185); for (auto jclient : jconf.items()) { t_tag client = tag_str_to_uint(jclient.key().c_str()); if (!tag_check_client(client)) { eventlog(eventlog_level_error, __FUNCTION__, "Invalid client: {}", jclient.key()); continue; } for (auto jarchitecture : jclient.value().items()) { t_tag architecture = tag_str_to_uint(jarchitecture.key().c_str()); if (!tag_check_arch(architecture)) { eventlog(eventlog_level_error, __FUNCTION__, "Invalid architecture: {}", jarchitecture.key()); continue; } for (auto jversion_id : jarchitecture.value().items()) { std::uint32_t version_id; try { version_id = std::stoul(jversion_id.key(), nullptr, 0); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "Invalid version id \"{}\": {}", jversion_id.key(), e.what()); continue; } // FIXME: do error checking for these std::string checkrevision_filename = jversion_id.value()["checkRevisionFile"].get(); std::string checkrevision_equation = jversion_id.value()["equation"].get(); cr_entries.emplace( std::piecewise_construct, std::forward_as_tuple(architecture, client, version_id), std::forward_as_tuple(checkrevision_filename, checkrevision_equation) ); for (auto jentry : jversion_id.value()["entries"]) { try { VersionCheck entry(jentry["title"].get(), version_id, jentry["version"].get(), jentry["hash"].get(), architecture, client, jentry["versionTag"].get() ); vc_entries.insert({ std::make_tuple(entry.m_version_id, entry.m_game_version, entry.m_architecture, entry.m_client), entry }); } catch (const std::exception& e) { eventlog(eventlog_level_error, __FUNCTION__, "Failed to load versioncheck entry: {}", e.what()); continue; } } } } } auto t1 = std::chrono::steady_clock::now(); eventlog(eventlog_level_info, __FUNCTION__, "Successfully loaded {} versioncheck entries in {} microseconds", vc_entries.size(), std::chrono::duration_cast(t1 - t0).count()); versioncheck_conf_is_loaded = true; return true; } void unload_versioncheck_conf() { if (versioncheck_conf_is_loaded) { vc_entries.clear(); cr_entries.clear(); versioncheck_conf_is_loaded = false; eventlog(eventlog_level_info, __FUNCTION__, "Successfully unloaded all version check entries"); } } std::tuple select_checkrevision(t_tag architecture, t_tag client, std::uint32_t version_id) { static const std::tuple default_checkrevision { "ver-IX86-1.mpq", "A=42 B=42 C=42 4 A=A^S B=B^B C=C^C A=A^S" }; auto key = cr_entries.find(std::make_tuple(architecture, client, version_id)); if (key != cr_entries.end()) { return key->second; } eventlog(eventlog_level_debug, __FUNCTION__, "Could not find corresponding CheckRevision entry, returning default CheckRevision"); return default_checkrevision; } // could use C++17 std::optional here const VersionCheck* select_versioncheck(t_tag architecture, t_tag client, std::uint32_t version_id, std::uint32_t checkrevision_version, std::uint32_t checkrevision_checksum) { auto it = vc_entries.find(std::make_tuple(version_id, checkrevision_version, architecture, client)); if (it == vc_entries.end()) { return nullptr; } if (it->second.m_checksum != checkrevision_checksum && !prefs_get_allow_bad_version()) { return nullptr; } return &(it->second); } VersionCheck::VersionCheck(const std::string& title, std::uint32_t version_id, const std::string& game_version, const std::string& checksum, t_tag architecture, t_tag client, const std::string& version_tag) : m_version_id(version_id), m_architecture(architecture), m_client(client) { if (verstr_to_vernum(game_version.c_str(), reinterpret_cast(&this->m_game_version)) < 0) { throw std::runtime_error("Invalid version \"" + game_version + "\" in entry \"" + title + "\""); } this->m_checksum = std::stoul(checksum, nullptr, 0); // FIXME: check for uniqueness this->m_version_tag = version_tag; } std::string VersionCheck::get_version_tag() const { return this->m_version_tag; } /* // This function only validates the format of the metadata string // There is no need to validate the information in the string because validating the checksum and file version is sufficient bool validate_file_metadata_format(const std::string& metadata) { if (metadata.empty()) { return false; } try { std::smatch tokens; if (std::regex_match(metadata, tokens, std::regex{ R"(([[:print:]]+\.exe) (\d\d/\d\d/\d\d \d\d:\d\d:\d\d) (\d+))" }) == false) { eventlog(eventlog_level_error, __FUNCTION__, "got invalid file metadata \"{}\"", metadata); return false; } } catch (const std::regex_error& e) { eventlog(eventlog_level_error, __FUNCTION__, "{} (code {})", e.what(), e.code()); return false; } return true; } */ } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/versioncheck.h000066400000000000000000000070451462640461400207250ustar00rootroot00000000000000/* * Copyright (C) 2000 Onlyer (onlyer@263.net) * Copyright (C) 2001 Ross Combs (ross@bnetd.org) * Copyright (C) 2002 Gianluigi Tiesi (sherpya@netfarm.it) * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PVPGN_BNETD_VERSIONCHECK_H #define PVPGN_BNETD_VERSIONCHECK_H #include #include #include #include #include "common/tag.h" namespace pvpgn { namespace bnetd { class VersionCheck; // filename, equation std::tuple select_checkrevision(t_tag architecture, t_tag client, std::uint32_t version_id); const VersionCheck* select_versioncheck(t_tag architecture, t_tag client, std::uint32_t version_id, std::uint32_t checkrevision_version, std::uint32_t checkrevision_checksum); /*******************************************************************************/ // Conf /*******************************************************************************/ bool load_versioncheck_conf(const std::string& filename); void unload_versioncheck_conf(); class VersionCheck { public: /*******************************************************************************/ // Getters /*******************************************************************************/ std::string get_version_tag() const; /*******************************************************************************/ // Deconstructors /*******************************************************************************/ ~VersionCheck() = default; private: /*******************************************************************************/ // Friend functions /*******************************************************************************/ friend bool load_versioncheck_conf(const std::string& filename); friend const VersionCheck* select_versioncheck(t_tag architecture, t_tag client, std::uint32_t version_id, std::uint32_t checkrevision_version, std::uint32_t checkrevision_checksum); /*******************************************************************************/ // Constructors /*******************************************************************************/ VersionCheck(const std::string& title, std::uint32_t version_id, const std::string& game_version, const std::string& checksum, t_tag architecture, t_tag client, const std::string& version_tag); /*******************************************************************************/ // Member variables /*******************************************************************************/ std::uint32_t m_version_id; // AKA "Version Byte" std::uint32_t m_game_version; // Windows file version std::uint32_t m_checksum; t_tag m_architecture; t_tag m_client; std::string m_version_tag; }; } // namespace bnetd } // namespace pvpgn #endif //PVPGN_BNETD_VERSIONCHECK_Hpvpgn-1.99.7.2.1+dfsg/src/bnetd/watch.cpp000066400000000000000000000151411462640461400176770ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "watch.h" #include "common/field_sizes.h" #include "common/eventlog.h" #include "common/list.h" #include "message.h" #include "friends.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace bnetd { scoped_ptr watchlist; Watch::Watch(t_connection* owner_, t_account* who_, unsigned what_, t_clienttag ctag_) :owner(owner_), who(who_), what(what_), ctag(ctag_) { } Watch::~Watch() throw() { } t_connection* Watch::getOwner() const { return owner; } t_account* Watch::getAccount() const { return who; } unsigned Watch::getEventMask() const { return what; } t_clienttag Watch::getClientTag() const { return ctag; } void Watch::setEventMask(unsigned what_) { what = what_; } /* who == NULL means anybody */ void WatchComponent::add(t_connection * owner, t_account * who, t_clienttag clienttag, unsigned events) { for (WatchList::iterator it(wlist.begin()); it != wlist.end(); ++it) { if (owner == it->getOwner() && who == it->getAccount() && clienttag == it->getClientTag()) { it->setEventMask(it->getEventMask() | events); return; } } wlist.push_back(Watch(owner, who, events, clienttag)); } /* who == NULL means anybody */ int WatchComponent::del(t_connection * owner, t_account * who, t_clienttag clienttag, unsigned events) { for (WatchList::iterator it(wlist.begin()); it != wlist.end(); ++it) { if (owner == it->getOwner() && who == it->getAccount() && (!clienttag || clienttag == it->getClientTag())) { unsigned evmask = it->getEventMask() & (~events); if (!evmask) wlist.erase(it); else it->setEventMask(evmask); return 0; } } return -1; } /* this differs from del_events because it doesn't return an error if nothing was found */ void WatchComponent::del(t_connection * owner) { for (WatchList::iterator it(wlist.begin()); it != wlist.end();) { if (owner == it->getOwner()) it = wlist.erase(it); else ++it; } } int WatchComponent::dispatch_whisper(t_account *account, char const *gamename, t_clienttag clienttag, Watch::EventType event) const { t_elem const * curr; char msg[512]; int cnt = 0; char const *myusername; t_list * flist; t_connection * dest_c, *my_c; t_friend * fr; char const * game_title; if (!(myusername = account_get_name(account))) { ERROR0("got NULL account name"); return -1; } my_c = account_get_conn(account); game_title = clienttag_get_title(clienttag); /* mutual friends handling */ flist = account_get_friends(account); if (flist) { switch (event) { case Watch::ET_joingame: if (gamename) std::snprintf(msg, sizeof(msg), "Your friend %s has entered a %s game named \"%s\".", myusername, game_title, gamename); else std::snprintf(msg, sizeof(msg), "Your friend %s has entered a %s game", myusername, game_title); break; case Watch::ET_leavegame: std::snprintf(msg, sizeof(msg), "Your friend %s has left a %s game.", myusername, game_title); break; case Watch::ET_login: std::snprintf(msg, sizeof(msg), "Your friend %s has entered %s.", myusername, prefs_get_servername()); break; case Watch::ET_logout: std::snprintf(msg, sizeof(msg), "Your friend %s has left %s.", myusername, prefs_get_servername()); break; } LIST_TRAVERSE(flist, curr) { if (!(fr = (t_friend*)elem_get_data(curr))) { ERROR0("found NULL entry in list"); continue; } dest_c = connlist_find_connection_by_account(fr->friendacc); if (dest_c == NULL) /* If friend is offline, go on to next */ continue; else { cnt++; /* keep track of successful whispers */ if (friend_get_mutual(fr)) message_send_text(dest_c, message_type_whisper, my_c, msg); } } } if (cnt) DEBUG2("notified {} friends about {}", cnt, myusername); /* watchlist handling */ switch (event) { case Watch::ET_joingame: if (gamename) std::snprintf(msg, sizeof(msg), "Watched user %s has entered a %s game named \"%s\".", myusername, game_title, gamename); else std::snprintf(msg, sizeof(msg), "Watched user %s has entered a %s game", myusername, game_title); break; case Watch::ET_leavegame: std::snprintf(msg, sizeof(msg), "Watched user %s has left a %s game.", myusername, game_title); break; case Watch::ET_login: std::snprintf(msg, sizeof(msg), "Watched user %s has entered %s.", myusername, prefs_get_servername()); break; case Watch::ET_logout: std::snprintf(msg, sizeof(msg), "Watched user %s has left %s", myusername, prefs_get_servername()); break; } for (WatchList::const_iterator it(wlist.begin()); it != wlist.end(); ++it) { if (it->getOwner() && (!it->getAccount() || it->getAccount() == account) && (!it->getClientTag() || (clienttag == it->getClientTag())) && (it->getEventMask() & event)) { message_send_text(it->getOwner(), message_type_whisper, my_c, msg); } } return 0; } int WatchComponent::dispatch(t_account * who, char const * gamename, t_clienttag clienttag, Watch::EventType event) const { switch (event) { case Watch::ET_login: case Watch::ET_logout: case Watch::ET_joingame: case Watch::ET_leavegame: return dispatch_whisper(who, gamename, clienttag, event); default: eventlog(eventlog_level_error, __FUNCTION__, "got unknown event {}", (unsigned int)event); return -1; } return 0; } WatchComponent::WatchComponent() :wlist() { } WatchComponent::~WatchComponent() throw () { } } } pvpgn-1.99.7.2.1+dfsg/src/bnetd/watch.h000066400000000000000000000045041462640461400173450ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef PVPGN_BNETD_WATCH_H #define PVPGN_BNETD_WATCH_H #include #include "common/scoped_ptr.h" #include "account.h" #include "connection.h" #include "common/tag.h" namespace pvpgn { namespace bnetd { class Watch { public: enum EventType { ET_login = 1, ET_logout = 2, ET_joingame = 4, ET_leavegame = 8 }; Watch(t_connection* owner_, t_account* who_, unsigned what_, t_clienttag ctag_); ~Watch() throw(); t_connection* getOwner() const; t_account* getAccount() const; unsigned getEventMask() const; t_clienttag getClientTag() const; void setEventMask(unsigned what_); private: t_connection * owner; /* who to notify */ t_account * who; /* when this account */ unsigned what; /* does one of these things */ t_clienttag ctag; /* while logged in with this clienttag (0 for any) */ }; class WatchComponent { public: WatchComponent(); ~WatchComponent() throw(); void add(t_connection * owner, t_account * who, t_clienttag clienttag, unsigned events); int del(t_connection * owner, t_account * who, t_clienttag clienttag, unsigned events); void del(t_connection * owner); int dispatch(t_account * who, char const * gamename, t_clienttag clienttag, Watch::EventType event) const; private: typedef std::list WatchList; WatchList wlist; int dispatch_whisper(t_account *account, char const *gamename, t_clienttag clienttag, Watch::EventType event) const; }; extern scoped_ptr watchlist; } } #endif pvpgn-1.99.7.2.1+dfsg/src/bniutils/000077500000000000000000000000001462640461400166205ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/bniutils/CMakeLists.txt000066400000000000000000000012521462640461400213600ustar00rootroot00000000000000add_executable(bnilist bnilist.cpp fileio.cpp fileio.h tga.cpp tga.h) target_link_libraries(bnilist common compat) add_executable(bni2tga bni2tga.cpp fileio.cpp fileio.h) target_link_libraries(bni2tga common) add_executable(bniextract bniextract.cpp fileio.cpp fileio.h tga.cpp tga.h bni.cpp bni.h) target_link_libraries(bniextract common compat) add_executable(bnibuild bnibuild.cpp fileio.cpp fileio.h bni.cpp bni.h tga.cpp tga.h) target_link_libraries(bnibuild common compat) add_executable(tgainfo tgainfo.cpp fileio.cpp fileio.h tga.cpp tga.h) target_link_libraries(tgainfo common compat) install(TARGETS bnilist bni2tga bniextract bnibuild tgainfo DESTINATION ${BINDIR}) pvpgn-1.99.7.2.1+dfsg/src/bniutils/bni.cpp000066400000000000000000000070321462640461400200760ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech (mmz@gmx.net) Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is xfree software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include "bni.h" #include "common/xalloc.h" #include "fileio.h" #include "common/setup_after.h" namespace pvpgn { namespace bni { extern t_bnifile * load_bni(std::FILE *f) { t_bnifile *b; unsigned int i; if (f == NULL) return NULL; b = (t_bnifile*)xmalloc(sizeof(t_bnifile)); file_rpush(f); b->unknown1 = file_readd_le(); if (b->unknown1 != 0x00000010) std::fprintf(stderr, "load_bni: field 1 is not 0x00000010. Data may be invalid!\n"); b->unknown2 = file_readd_le(); if (b->unknown2 != 0x00000001) std::fprintf(stderr, "load_bni: field 2 is not 0x00000001. Data may be invalid!\n"); b->numicons = file_readd_le(); if (b->numicons > BNI_MAXICONS) { std::fprintf(stderr, "load_bni: more than %d (BNI_MAXICONS) icons. Increase maximum number of icons in \"bni.h\".\n", BNI_MAXICONS); b->numicons = BNI_MAXICONS; } b->dataoffset = file_readd_le(); if (b->numicons < 1) { std::fprintf(stderr, "load_bni: strange, no icons present in BNI file\n"); b->icons = NULL; } else { b->icons = (struct bni_iconlist_struct*)xmalloc(b->numicons*sizeof(t_bniicon)); } for (i = 0; i < b->numicons; i++) { b->icons->icon[i].id = file_readd_le(); b->icons->icon[i].x = file_readd_le(); b->icons->icon[i].y = file_readd_le(); if (b->icons->icon[i].id == 0) { b->icons->icon[i].tag = file_readd_le(); } else { b->icons->icon[i].tag = 0; } b->icons->icon[i].unknown = file_readd_le(); } if (std::ftell(f) != (long)b->dataoffset) std::fprintf(stderr, "load_bni: Warning, %lu bytes of garbage after BNI header\n", (unsigned long)(b->dataoffset - std::ftell(f))); file_rpop(); return b; } extern int write_bni(std::FILE *f, t_bnifile *b) { unsigned int i; if (f == NULL) return -1; if (b == NULL) return -1; file_wpush(f); file_writed_le(b->unknown1); if (b->unknown1 != 0x00000010) std::fprintf(stderr, "write_bni: field 1 is not 0x00000010. Data may be invalid!\n"); file_writed_le(b->unknown2); if (b->unknown2 != 0x00000001) std::fprintf(stderr, "write_bni: field 2 is not 0x00000001. Data may be invalid!\n"); file_writed_le(b->numicons); file_writed_le(b->dataoffset); for (i = 0; i < b->numicons; i++) { file_writed_le(b->icons->icon[i].id); file_writed_le(b->icons->icon[i].x); file_writed_le(b->icons->icon[i].y); if (b->icons->icon[i].id == 0) { file_writed_le(b->icons->icon[i].tag); } file_writed_le(b->icons->icon[i].unknown); } if (std::ftell(f) != (long)b->dataoffset) std::fprintf(stderr, "Warning: dataoffset is incorrect! (=0x%lx should be 0x%lx)\n", (unsigned long)b->dataoffset, (unsigned long)std::ftell(f)); file_wpop(); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bniutils/bni.h000066400000000000000000000031121462640461400175360ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef INCLUDED_BNI_H #define INCLUDED_BNI_H #include #ifndef BNI_MAXICONS #define BNI_MAXICONS 4096 #endif namespace pvpgn { namespace bni { typedef struct { unsigned int id; /* Icon ID */ unsigned int x, y; /* width and height */ unsigned int tag; /* if ID == 0 */ unsigned int unknown; /* 0x00000000 */ } t_bniicon; struct bni_iconlist_struct { t_bniicon icon[BNI_MAXICONS]; }; /* The icons */ typedef struct { unsigned int unknown1; /* 0x00000010 */ unsigned int unknown2; /* 0x00000001 */ unsigned int numicons; /* Number of icons */ unsigned int dataoffset; /* Start of TGA-File */ struct bni_iconlist_struct *icons; /* The icons */ } t_bnifile; extern t_bnifile * load_bni(std::FILE *f); extern int write_bni(std::FILE *f, t_bnifile *b); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bniutils/bni2tga.cpp000066400000000000000000000110101462640461400206430ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech (mmz@gmx.net) Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include #include #include #include "common/version.h" #include "fileio.h" #include "bni.h" #include "common/setup_after.h" #define BUFSIZE 1024 using namespace pvpgn::bni; namespace { void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [--] [ []]\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { char const * bnifile = NULL; char const * tgafile = NULL; std::FILE * fbni; std::FILE * ftga; int a; int forcefile = 0; char dash[] = "-"; /* unique address used as flag */ if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (forcefile && !bnifile) bnifile = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !bnifile) bnifile = dash; else if (argv[a][0] != '-' && !bnifile) bnifile = argv[a]; else if (forcefile && !tgafile) tgafile = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !tgafile) tgafile = dash; else if (argv[a][0] != '-' && !tgafile) tgafile = argv[a]; else if (forcefile || argv[a][0] != '-' || std::strcmp(argv[a], "-") == 0) { std::fprintf(stderr, "%s: extra file argument \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "--") == 0) forcefile = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (!bnifile) bnifile = dash; if (!tgafile) tgafile = dash; if (bnifile == dash) fbni = stdin; else if (!(fbni = std::fopen(bnifile, "r"))) { std::fprintf(stderr, "%s: could not open BNI file \"%s\" for reading (std::fopen: %s)\n", argv[0], bnifile, std::strerror(errno)); std::exit(EXIT_FAILURE); } if (tgafile == dash) ftga = stdout; else if (!(ftga = std::fopen(tgafile, "w"))) { std::fprintf(stderr, "%s: could not open TGA file \"%s\" for reading (std::fopen: %s)\n", argv[0], tgafile, std::strerror(errno)); std::exit(EXIT_FAILURE); } { unsigned char buf[BUFSIZE]; std::size_t rc; t_bnifile bnih; file_rpush(fbni); bnih.unknown1 = file_readd_le(); bnih.unknown2 = file_readd_le(); bnih.numicons = file_readd_le(); bnih.dataoffset = file_readd_le(); std::fprintf(stderr, "Info: numicons=%d dataoffset=0x%08x(%d)\n", bnih.numicons, bnih.dataoffset, bnih.dataoffset); if (std::fseek(fbni, bnih.dataoffset, SEEK_SET)<0) { std::fprintf(stderr, "%s: could not seek to offset %u in BNI file \"%s\" (std::fseek: %s)\n", argv[0], bnih.dataoffset, bnifile, std::strerror(errno)); return EXIT_FAILURE; } while ((rc = std::fread(buf, 1, sizeof(buf), fbni))>0) { if (std::fwrite(buf, rc, 1, ftga) < 1) { std::fprintf(stderr, "%s: could not write data to TGA file \"%s\" (std::fwrite: %s)\n", argv[0], tgafile, std::strerror(errno)); return EXIT_FAILURE; } } file_rpop(); } if (tgafile != dash && std::fclose(ftga) < 0) std::fprintf(stderr, "%s: could not close TGA file \"%s\" after writing (std::fclose: %s)\n", argv[0], tgafile, std::strerror(errno)); if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bniutils/bnibuild.cpp000066400000000000000000000242061462640461400211200ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is xfree software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include "compat/statmacros.h" #include "common/xalloc.h" #include "common/version.h" #include "fileio.h" #include "tga.h" #include "bni.h" #include "common/setup_after.h" #define BUFSIZE 1024 using namespace pvpgn; using namespace pvpgn::bni; namespace { int read_list(char const * progname, t_bnifile * bnifile, char const * name) { std::FILE * f; char line[BUFSIZE]; f = std::fopen(name, "r"); if (f == NULL) { std::fprintf(stderr, "%s: could not open index file \"%s\" for reading (std::fopen: %s)\n", progname, name, std::strerror(errno)); return -1; } bnifile->unknown1 = 0x00000010; /* in case they are not set */ bnifile->unknown2 = 0x00000001; bnifile->numicons = 0; bnifile->dataoffset = 16; /* size of header */ bnifile->icons = (struct bni_iconlist_struct*)xmalloc(1); /* some xrealloc()s are broken */ while (std::fgets(line, sizeof(line), f)) { char cmd[BUFSIZE]; std::sscanf(line, "%s", cmd); if (std::strcmp(cmd, "unknown1") == 0) { std::sscanf(line, "unknown1 %08x", &bnifile->unknown1); } else if (std::strcmp(cmd, "unknown2") == 0) { std::sscanf(line, "unknown2 %08x", &bnifile->unknown2); } else if (std::strcmp(cmd, "icon") == 0) { char c; std::sscanf(line, "icon %c", &c); if (c == '!') { unsigned char tg[4]; int tag; unsigned int x, y, unknown; std::sscanf(line, "icon !%c%c%c%c %u %u %08x", &tg[0], &tg[1], &tg[2], &tg[3], &x, &y, &unknown); tag = tg[3] + (tg[2] << 8) + (tg[1] << 16) + (tg[0] << 24); std::fprintf(stderr, "Icon[%d]: id=0x%x x=%u y=%u unknown=0x%x tag=\"%c%c%c%c\"\n", bnifile->numicons, 0, x, y, unknown, ((tag >> 24) & 0xff), ((tag >> 16) & 0xff), ((tag >> 8) & 0xff), ((tag >> 0) & 0xff)); bnifile->icons = (struct bni_iconlist_struct*)xrealloc(bnifile->icons, ((bnifile->numicons + 1)*sizeof(t_bniicon))); bnifile->icons->icon[bnifile->numicons].id = 0; bnifile->icons->icon[bnifile->numicons].x = x; bnifile->icons->icon[bnifile->numicons].y = y; bnifile->icons->icon[bnifile->numicons].tag = tag; bnifile->icons->icon[bnifile->numicons].unknown = unknown; bnifile->numicons++; bnifile->dataoffset += 20; } else if (c == '#') { unsigned int id, x, y, unknown; std::sscanf(line, "icon #%08x %u %u %08x", &id, &x, &y, &unknown); std::fprintf(stderr, "Icon[%d]: id=0x%x x=%u y=%u unknown=0x%x tag=0x00000000\n", bnifile->numicons, id, x, y, unknown); bnifile->icons = (struct bni_iconlist_struct*)xrealloc(bnifile->icons, ((bnifile->numicons + 1)*sizeof(t_bniicon))); bnifile->icons->icon[bnifile->numicons].id = id; bnifile->icons->icon[bnifile->numicons].x = x; bnifile->icons->icon[bnifile->numicons].y = y; bnifile->icons->icon[bnifile->numicons].tag = 0; bnifile->icons->icon[bnifile->numicons].unknown = unknown; bnifile->numicons++; bnifile->dataoffset += 16; } else std::fprintf(stderr, "Bad character '%c' in icon specifier for icon %u in index file \"%s\"\n", c, bnifile->numicons + 1, name); } else std::fprintf(stderr, "Unknown command \"%s\" in index file \"%s\"\n", cmd, name); } if (std::fclose(f) < 0) std::fprintf(stderr, "%s: could not close index file \"%s\" after reading (std::fclose: %s)\n", progname, name, std::strerror(errno)); return 0; } char * geticonfilename(t_bnifile *bnifile, char const * indir, int i) { char * name; if (bnifile->icons->icon[i].id == 0) { unsigned int tag = bnifile->icons->icon[i].tag; name = (char*)xmalloc(std::strlen(indir) + 10); std::sprintf(name, "%s/%c%c%c%c.tga", indir, ((tag >> 24) & 0xff), ((tag >> 16) & 0xff), ((tag >> 8) & 0xff), ((tag >> 0) & 0xff)); } else { name = (char*)xmalloc(std::strlen(indir) + 16); std::sprintf(name, "%s/%08x.tga", indir, bnifile->icons->icon[i].id); } return name; } int img2area(t_tgaimg *dst, t_tgaimg *src, int x, int y) { unsigned char *sdp; unsigned char *ddp; int pixelsize; int i; pixelsize = getpixelsize(dst); if (getpixelsize(src) != pixelsize) { std::fprintf(stderr, "Error: source pixelsize is %d should be %d!\n", getpixelsize(src), pixelsize); return -1; } if (src->width + x > dst->width) return -1; if (src->height + y > dst->height) return -1; sdp = src->data; ddp = dst->data + (y * dst->width * pixelsize); for (i = 0; i < src->height; i++) { ddp += x*pixelsize; std::memcpy(ddp, sdp, src->width*pixelsize); sdp += src->width*pixelsize; ddp += (dst->width - x)*pixelsize; } return 0; } void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [--] []\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { char const * indir = NULL; char const * bnifile = NULL; std::FILE * fbni; struct stat s; int a; int forcefile = 0; char dash[] = "-"; /* unique address used as flag */ if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (forcefile && !indir) indir = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !indir) indir = dash; else if (argv[a][0] != '-' && !indir) indir = argv[a]; else if (forcefile && !bnifile) bnifile = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !bnifile) bnifile = dash; else if (argv[a][0] != '-' && !bnifile) bnifile = argv[a]; else if (forcefile || argv[a][0] != '-' || std::strcmp(argv[a], "-") == 0) { std::fprintf(stderr, "%s: extra file argument \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "--") == 0) forcefile = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (!indir) { std::fprintf(stderr, "%s: input directory not specified\n", argv[0]); usage(argv[0]); } if (!bnifile) bnifile = dash; if (indir == dash) { std::fprintf(stderr, "%s: can not read directory from \n", argv[0]); return EXIT_FAILURE; } if (stat(indir, &s) < 0) { std::fprintf(stderr, "%s: could not stat input directory \"%s\" (stat: %s)\n", argv[0], indir, std::strerror(errno)); return EXIT_FAILURE; } if (!S_ISDIR(s.st_mode)) { std::fprintf(stderr, "%s: \"%s\" is not a directory\n", argv[0], indir); return -1; } if (bnifile == dash) fbni = stdout; else if (!(fbni = std::fopen(bnifile, "w"))) { std::fprintf(stderr, "%s: could not open BNI file \"%s\" for writing (std::fopen: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } { unsigned int i; unsigned int yline; t_tgaimg * img; t_bnifile bni; char * listfilename; listfilename = (char*)xmalloc(std::strlen(indir) + 14); std::sprintf(listfilename, "%s/bniindex.lst", indir); std::fprintf(stderr, "Info: Reading index from file \"%s\"...\n", listfilename); if (read_list(argv[0], &bni, listfilename) < 0) return EXIT_FAILURE; std::fprintf(stderr, "BNIHeader: unknown1=%u unknown2=%u numicons=%u dataoffset=%u\n", bni.unknown1, bni.unknown2, bni.numicons, bni.dataoffset); if (write_bni(fbni, &bni) < 0) { std::fprintf(stderr, "Error: Failed to write BNI header.\n"); return EXIT_FAILURE; } img = new_tgaimg(0, 0, 24, tgaimgtype_rlecompressed_truecolor); for (i = 0; i < bni.numicons; i++) { if (bni.icons->icon[i].x > img->width) img->width = bni.icons->icon[i].x; img->height += bni.icons->icon[i].y; } std::fprintf(stderr, "Info: Creating TGA with %ux%ux%ubpp.\n", img->width, img->height, img->bpp); img->data = (std::uint8_t*)xmalloc(img->width*img->height*getpixelsize(img)); yline = 0; for (i = 0; i < bni.numicons; i++) { t_tgaimg *icon; std::FILE *f; char *name; name = geticonfilename(&bni, indir, i); f = std::fopen(name, "r"); if (f == NULL) { std::perror("std::fopen"); xfree(name); return EXIT_FAILURE; } xfree(name); icon = load_tga(f); if (std::fclose(f) < 0) std::fprintf(stderr, "Error: could not close TGA file \"%s\" after reading (std::fclose: %s)\n", name, std::strerror(errno)); if (icon == NULL) { std::fprintf(stderr, "Error: load_tga failed with data from TGA file \"%s\"\n", name); return EXIT_FAILURE; } if (img2area(img, icon, 0, yline) < 0) { std::fprintf(stderr, "Error: inserting icon from TGA file \"%s\" into big TGA failed\n", name); return EXIT_FAILURE; } yline += icon->height; destroy_img(icon); } if (write_tga(fbni, img) < 0) { std::fprintf(stderr, "Error: Failed to write TGA to BNI file.\n"); return EXIT_FAILURE; } if (bnifile != dash && std::fclose(fbni) < 0) { std::fprintf(stderr, "%s: could not close BNI file \"%s\" after writing (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } } std::fprintf(stderr, "Info: Writing to \"%s\" finished.\n", bnifile); return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bniutils/bniextract.cpp000066400000000000000000000230531462640461400214720ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech (mmz@gmx.net) Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is xfree software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include "compat/mkdir.h" #include "compat/statmacros.h" #include "common/xalloc.h" #include "common/version.h" #include "fileio.h" #include "tga.h" #include "bni.h" #include "common/setup_after.h" using namespace pvpgn::bni; using namespace pvpgn; namespace { /* extract a portion of an image creating a new image */ t_tgaimg * area2img(t_tgaimg *src, int x, int y, int width, int height, t_tgaimgtype type) { t_tgaimg *dst; int pixelsize; unsigned char *datap; unsigned char *destp; int i; if (src == NULL) return NULL; if ((x + width) > src->width) return NULL; if ((y + height) > src->height) return NULL; pixelsize = getpixelsize(src); if (pixelsize == 0) return NULL; dst = new_tgaimg(width, height, src->bpp, type); dst->data = (std::uint8_t*)xmalloc(width*height*pixelsize); datap = src->data; datap += y*src->width*pixelsize; destp = dst->data; for (i = 0; i < height; i++) { datap += x*pixelsize; std::memcpy(destp, datap, width*pixelsize); destp += width*pixelsize; datap += (src->width - x)*pixelsize; } return dst; } void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [--] \n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { char const * outdir = NULL; char const * bnifile = NULL; std::FILE * fbni; struct stat s; int a; int forcefile = 0; char dash[] = "-"; /* unique address used as flag */ if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (forcefile && !bnifile) bnifile = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !bnifile) bnifile = dash; else if (argv[a][0] != '-' && !bnifile) bnifile = argv[a]; else if (forcefile && !outdir) outdir = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !outdir) outdir = dash; else if (argv[a][0] != '-' && !outdir) outdir = argv[a]; else if (forcefile || argv[a][0] != '-' || std::strcmp(argv[a], "-") == 0) { std::fprintf(stderr, "%s: extra file argument \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "--") == 0) forcefile = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (!bnifile) { std::fprintf(stderr, "%s: BNI file not specified\n", argv[0]); usage(argv[0]); } if (!outdir) { std::fprintf(stderr, "%s: output directory not specified\n", argv[0]); usage(argv[0]); } if (bnifile == dash) fbni = stdin; else if (!(fbni = std::fopen(bnifile, "r"))) { std::fprintf(stderr, "%s: could not open BNI file \"%s\" for reading (std::fopen: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } if (outdir == dash) { std::fprintf(stderr, "%s: can not write directory to \n", argv[0]); if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } if (stat(outdir, &s) < 0) { if (errno == ENOENT) { std::fprintf(stderr, "Info: Creating directory \"%s\" ...\n", outdir); if (p_mkdir(outdir, S_IRWXU + S_IRWXG + S_IRWXO) < 0) { std::fprintf(stderr, "%s: could not create output directory \"%s\" (mkdir: %s)", argv[0], outdir, std::strerror(errno)); if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } } else { std::fprintf(stderr, "%s: could not stat output directory \"%s\" (stat: %s)\n", argv[0], outdir, std::strerror(errno)); if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } } else if (S_ISDIR(s.st_mode) == 0) { std::fprintf(stderr, "%s: \"%s\" is not a directory\n", argv[0], outdir); if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } { unsigned int i; int curry; t_tgaimg * iconimg; t_bnifile * bni; std::FILE * indexfile; char * indexfilename; std::fprintf(stderr, "Info: Loading \"%s\" ...\n", bnifile); bni = load_bni(fbni); if (bni == NULL) return EXIT_FAILURE; if (std::fseek(fbni, bni->dataoffset, SEEK_SET) < 0) { std::fprintf(stderr, "%s: could not seek to TGA data offset %lu (std::fseek: %s)\n", argv[0], (unsigned long int)bni->dataoffset, std::strerror(errno)); if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } std::fprintf(stderr, "Info: Loading image ...\n"); iconimg = load_tga(fbni); if (iconimg == NULL) return EXIT_FAILURE; std::fprintf(stderr, "Info: Extracting icons ...\n"); indexfilename = (char*)xmalloc(std::strlen(outdir) + 14); std::sprintf(indexfilename, "%s/bniindex.lst", outdir); std::fprintf(stderr, "Info: Writing Index to \"%s\" ... \n", indexfilename); indexfile = std::fopen(indexfilename, "w"); if (indexfile == NULL) { std::fprintf(stderr, "%s: could not open index file \"%s\" for writing (std::fopen: %s)\n", argv[0], indexfilename, std::strerror(errno)); if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_FAILURE; } std::fprintf(indexfile, "unknown1 %08x\n", bni->unknown1); std::fprintf(indexfile, "unknown2 %08x\n", bni->unknown2); curry = 0; for (i = 0; i < bni->numicons; i++) { std::FILE *dsttga; char *name; t_tgaimg *icn; icn = area2img(iconimg, 0, curry, bni->icons->icon[i].x, bni->icons->icon[i].y, tgaimgtype_uncompressed_truecolor); if (icn == NULL) { std::fprintf(stderr, "Error: area2img failed!\n"); return EXIT_FAILURE; } if (bni->icons->icon[i].id == 0) { int tag = bni->icons->icon[i].tag; name = (char*)xmalloc(std::strlen(outdir) + 10); std::sprintf(name, "%s/%c%c%c%c.tga", outdir, ((tag >> 24) & 0xff), ((tag >> 16) & 0xff), ((tag >> 8) & 0xff), ((tag >> 0) & 0xff)); } else { name = (char*)xmalloc(std::strlen(outdir) + 16); std::sprintf(name, "%s/%08x.tga", outdir, bni->icons->icon[i].id); } std::fprintf(stderr, "Info: Writing icon %u(%ux%u) to file \"%s\" ... \n", i + 1, icn->width, icn->height, name); curry += icn->height; dsttga = std::fopen(name, "w"); if (dsttga == NULL) { std::fprintf(stderr, "%s: could not open ouptut TGA file \"%s\" for writing (std::fopen: %s)\n", argv[0], name, std::strerror(errno)); } else { if (write_tga(dsttga, icn) < 0) { std::fprintf(stderr, "Error: Writing to TGA failed.\n"); } else { int tag = bni->icons->icon[i].tag; if (bni->icons->icon[i].id == 0) { std::fprintf(indexfile, "icon !%c%c%c%c %d %d %08x\n", ((tag >> 24) & 0xff), ((tag >> 16) & 0xff), ((tag >> 8) & 0xff), ((tag >> 0) & 0xff), bni->icons->icon[i].x, bni->icons->icon[i].y, bni->icons->icon[i].unknown); } else { std::fprintf(indexfile, "icon #%08x %d %d %08x\n", bni->icons->icon[i].id, bni->icons->icon[i].x, bni->icons->icon[i].y, bni->icons->icon[i].unknown); } } if (std::fclose(dsttga) < 0) std::fprintf(stderr, "%s: could not close TGA file \"%s\" after writing (std::fclose: %s)\n", argv[0], name, std::strerror(errno)); } xfree(name); destroy_img(icn); } destroy_img(iconimg); if (std::fclose(indexfile) < 0) { std::fprintf(stderr, "%s: could not close index file \"%s\" after writing (std::fclose: %s)\n", argv[0], indexfilename, std::strerror(errno)); return EXIT_FAILURE; } xfree(indexfilename); } if (bnifile != dash && std::fclose(fbni) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bniutils/bnilist.cpp000066400000000000000000000113711462640461400207730ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech (mmz@gmx.net) Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include #include #include #include "common/version.h" #include "tga.h" #include "fileio.h" #include "common/setup_after.h" using namespace pvpgn::bni; namespace { void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [--] []\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { char const * bnifile = NULL; std::FILE * fp; int a; int forcefile = 0; char dash[] = "-"; /* unique address used as flag */ if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (forcefile && !bnifile) bnifile = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !bnifile) bnifile = dash; else if (argv[a][0] != '-' && !bnifile) bnifile = argv[a]; else if (forcefile || argv[a][0] != '-' || std::strcmp(argv[a], "-") == 0) { std::fprintf(stderr, "%s: extra file argument \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "--") == 0) forcefile = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (!bnifile) bnifile = dash; if (bnifile == dash) fp = stdin; else if (!(fp = std::fopen(bnifile, "r"))) { std::fprintf(stderr, "%s: could not open BNI file \"%s\" for reading (std::fopen: %s)\n", argv[0], bnifile, std::strerror(errno)); std::exit(EXIT_FAILURE); } { t_tgaimg * tgaimg; int i; int bniid, unknown, icons, datastart; int expected_width, expected_height; file_rpush(fp); bniid = file_readd_le(); unknown = file_readd_le(); icons = file_readd_le(); datastart = file_readd_le(); std::fprintf(stderr, "BNIHeader: id=0x%08x unknown=0x%08x icons=0x%08x datastart=0x%08x\n", bniid, unknown, icons, datastart); expected_width = 0; expected_height = 0; for (i = 0; i < icons; i++) { int id, x, y, flags, tag; id = file_readd_le(); x = file_readd_le(); y = file_readd_le(); if (id == 0) { tag = file_readd_le(); } else { tag = 0; } flags = file_readd_le(); std::fprintf(stderr, "Icon[%d]: id=0x%08x x=%d y=%d tag=0x%08x(\"%c%c%c%c\") flags=0x%08x\n", i, id, x, y, tag, ((unsigned char)((tag >> 24) & 0xff)), ((unsigned char)((tag >> 16) & 0xff)), ((unsigned char)((tag >> 8) & 0xff)), ((unsigned char)((tag)& 0xff)), flags); if (x > expected_width) expected_width = x; expected_height += y; } if (std::ftell(fp) != datastart) { std::fprintf(stderr, "Warning: garbage after header (pos=0x%lx-datastart=0x%lx) = %ld bytes of garbage! \n", (unsigned long)std::ftell(fp), (unsigned long)datastart, (long)(std::ftell(fp) - datastart)); } tgaimg = load_tgaheader(); print_tga_info(tgaimg, stdout); std::fprintf(stderr, "\n"); std::fprintf(stderr, "Check: Expected %dx%d TGA, got %ux%u. %s\n", expected_width, expected_height, tgaimg->width, tgaimg->height, ((tgaimg->width == expected_width) && (tgaimg->height == expected_height)) ? "OK." : "FAIL."); std::fprintf(stderr, "Check: Expected 24bit color depth TGA, got %dbit. %s\n", tgaimg->bpp, (tgaimg->bpp == 24) ? "OK." : "FAIL."); std::fprintf(stderr, "Check: Expected ImageType 10, got %d. %s\n", tgaimg->imgtype, (tgaimg->imgtype == 10) ? "OK." : "FAIL."); file_rpop(); } if (bnifile != dash && std::fclose(fp) < 0) std::fprintf(stderr, "%s: could not close BNI file \"%s\" after reading (std::fclose: %s)\n", argv[0], bnifile, std::strerror(errno)); return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bniutils/fileio.cpp000066400000000000000000000120211462640461400205670ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech (mmz@gmx.net) Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include "fileio.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { namespace bni { typedef struct t_file { std::FILE *f; struct t_file *next; } t_file; namespace { t_file *r_file = NULL; t_file *w_file = NULL; } /* ----------------------------------------------------------------- */ extern void file_rpush(std::FILE *f) { t_file *tf = (t_file*)xmalloc(sizeof(t_file)); tf->next = r_file; tf->f = f; r_file = tf; return; } extern void file_rpop(void) { t_file *tf; tf = r_file; r_file = tf->next; xfree(tf); return; } /* ----------------------------------------------------------------- */ extern void file_wpush(std::FILE *f) { t_file *tf = (t_file*)xmalloc(sizeof(t_file)); tf->next = w_file; tf->f = f; w_file = tf; return; } extern void file_wpop(void) { t_file *tf; tf = w_file; w_file = tf->next; xfree(tf); return; } /* ----------------------------------------------------------------- */ extern std::uint8_t file_readb(void) { unsigned char buff[1]; if (std::fread(buff, 1, sizeof(buff), r_file->f) < sizeof(buff)) { if (std::ferror(r_file->f)) std::perror("file_readb: std::fread"); return 0; } return (((std::uint8_t)buff[0])); } extern std::uint16_t file_readw_le(void) { unsigned char buff[2]; if (std::fread(buff, 1, sizeof(buff), r_file->f) < sizeof(buff)) { if (std::ferror(r_file->f)) std::perror("file_readw_le: std::fread"); return 0; } return (((std::uint16_t)buff[0])) | (((std::uint16_t)buff[1]) << 8); } extern std::uint16_t file_readw_be(void) { unsigned char buff[2]; if (std::fread(buff, 1, sizeof(buff), r_file->f) < sizeof(buff)) { if (std::ferror(r_file->f)) std::perror("file_readw_be: std::fread"); return 0; } return (((std::uint16_t)buff[0]) << 8) | (((std::uint16_t)buff[1])); } extern std::uint32_t file_readd_le(void) { unsigned char buff[4]; if (std::fread(buff, 1, sizeof(buff), r_file->f) < sizeof(buff)) { if (std::ferror(r_file->f)) std::perror("file_readd_le: std::fread"); return 0; } return (((std::uint32_t)buff[0])) | (((std::uint32_t)buff[1]) << 8) | (((std::uint32_t)buff[2]) << 16) | (((std::uint32_t)buff[3]) << 24); } extern std::uint32_t file_readd_be(void) { unsigned char buff[4]; if (std::fread(buff, 1, sizeof(buff), r_file->f) < sizeof(buff)) { if (std::ferror(r_file->f)) std::perror("file_readd_be: std::fread"); return 0; } return (((std::uint32_t)buff[0]) << 24) | (((std::uint32_t)buff[1]) << 16) | (((std::uint32_t)buff[2]) << 8) | (((std::uint32_t)buff[3])); } extern int file_writeb(std::uint8_t u) { unsigned char buff[1]; buff[0] = (u); if (std::fwrite(buff, 1, sizeof(buff), w_file->f) < sizeof(buff)) { if (std::ferror(w_file->f)) std::perror("file_writeb: std::fwrite"); return -1; } return 0; } extern int file_writew_le(std::uint16_t u) { unsigned char buff[2]; buff[0] = (u); buff[1] = (u >> 8); if (std::fwrite(buff, 1, sizeof(buff), w_file->f) < sizeof(buff)) { if (std::ferror(w_file->f)) std::perror("file_writew_le: std::fwrite"); return -1; } return 0; } extern int file_writew_be(std::uint16_t u) { unsigned char buff[2]; buff[0] = (u >> 8); buff[1] = (u); if (std::fwrite(buff, 1, sizeof(buff), w_file->f) < sizeof(buff)) { if (std::ferror(w_file->f)) std::perror("file_writew_be: std::fwrite"); return -1; } return 0; } extern int file_writed_le(std::uint32_t u) { unsigned char buff[4]; buff[0] = (u); buff[1] = (u >> 8); buff[2] = (u >> 16); buff[3] = (u >> 24); if (std::fwrite(buff, 1, sizeof(buff), w_file->f) < sizeof(buff)) { if (std::ferror(w_file->f)) std::perror("file_writed_le: std::fwrite"); return -1; } return 0; } extern int file_writed_be(std::uint32_t u) { unsigned char buff[4]; buff[0] = (u >> 24); buff[1] = (u >> 16); buff[2] = (u >> 8); buff[3] = (u); if (std::fwrite(buff, 1, sizeof(buff), w_file->f) < sizeof(buff)) { if (std::ferror(w_file->f)) std::perror("file_writed_be: std::fwrite"); return -1; } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/bniutils/fileio.h000066400000000000000000000030171462640461400202410ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_FILEIO_PROTOS #define INCLUDED_FILEIO_PROTOS #include #include namespace pvpgn { namespace bni { extern void file_rpush(std::FILE *f); extern void file_rpop(void); extern void file_wpush(std::FILE *f); extern void file_wpop(void); extern std::uint8_t file_readb(void); extern std::uint16_t file_readw_le(void); extern std::uint16_t file_readw_be(void); extern std::uint32_t file_readd_le(void); extern std::uint32_t file_readd_be(void); extern int file_writeb(std::uint8_t u); extern int file_writew_le(std::uint16_t u); extern int file_writew_be(std::uint16_t u); extern int file_writed_le(std::uint32_t u); extern int file_writed_be(std::uint32_t u); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bniutils/tga.cpp000066400000000000000000000406761462640461400201140ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech (mmz@gmx.net) Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is xfree software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include "tga.h" #include #include #include #include "common/xalloc.h" #include "fileio.h" #include "common/setup_after.h" namespace pvpgn { namespace bni { namespace { int rotate_updown(t_tgaimg *img) { unsigned char *ndata; int pixelsize; int y; if (img == NULL) return -1; if (img->data == NULL) return -1; pixelsize = getpixelsize(img); if (pixelsize == 0) return -1; ndata = (unsigned char *)xmalloc(img->width*img->height*pixelsize); for (y = 0; y < img->height; y++) { std::memcpy(ndata + (y*img->width*pixelsize), img->data + ((img->width*img->height*pixelsize) - ((y + 1)*img->width*pixelsize)), img->width*pixelsize); } xfree(img->data); img->data = ndata; return 0; } int rotate_leftright(t_tgaimg *img) { unsigned char *ndata, *datap; int pixelsize; int y, x; std::fprintf(stderr, "WARNING: rotate_leftright: this function is untested!\n"); if (img == NULL) return -1; if (img->data == NULL) return -1; pixelsize = getpixelsize(img); if (pixelsize == 0) return -1; ndata = (unsigned char *)xmalloc(img->width*img->height*pixelsize); datap = img->data; for (y = 0; y < img->height; y++) { unsigned char *linep = (ndata + (((y + 1)*img->width*pixelsize) - pixelsize)); for (x = 0; x < img->width; x++) { std::memcpy(linep, datap, pixelsize); linep -= pixelsize; datap += pixelsize; } } xfree(img->data); img->data = ndata; return 0; } int RLE_decompress(std::FILE *f, void *buf, int bufsize, int pixelsize) { unsigned char pt; unsigned char *bufp; unsigned char temp[8]; /* MAXPIXELSIZE */ int bufi; int count; file_rpush(f); bufp = (unsigned char*)buf; for (bufi = 0; bufibufsize) { std::fprintf(stderr, "RLE_decompress: buffer too short for next packet (need %d bytes, have %d)\n", bufi + count*pixelsize, bufsize); file_rpop(); return -1; } if ((pt & 0x80) == 0) { /* RAW PACKET */ if (std::fread(bufp, pixelsize, count, f) < (unsigned)count) { if (std::feof(f)) std::fprintf(stderr, "RLE_decompress: short RAW packet (expected %d bytes) (EOF)\n", pixelsize*count); else std::fprintf(stderr, "RLE_decompress: short RAW packet (expected %d bytes) (std::fread: %s)\n", pixelsize*count, std::strerror(errno)); #if 0 file_rpop(); return -1; #endif } bufp += count*pixelsize; bufi += count*pixelsize; } else { /* RLE PACKET */ if (std::fread(temp, pixelsize, 1, f) < 1) { if (std::feof(f)) std::fprintf(stderr, "RLE_decompress: short RLE packet (expected %d bytes) (EOF)\n", pixelsize); else std::fprintf(stderr, "RLE_decompress: short RLE packet (expected %d bytes) (std::fread: %s)\n", pixelsize, std::strerror(errno)); #if 0 file_rpop(); return -1; #endif } if (count<2) { std::fprintf(stderr, "RLE_decompress: suspicious RLE repetition count %d\n", count); } for (; count > 0; count--) { std::memcpy(bufp, temp, pixelsize); bufp += pixelsize; bufi += pixelsize; } } } file_rpop(); return 0; } void RLE_write_pkt(std::FILE *f, t_tgapkttype pkttype, int len, void *data, int pixelsize) { unsigned char count; if (len<1 || len>128) { std::fprintf(stderr, "RLE_write_pkt: packet has bad length (%d bytes)\n", len); return; } if (pkttype == RLE) { if (len < 2) { std::fprintf(stderr, "RLE_write_pkt: RLE packet has bad length (%d bytes)\n", len); return; } count = (unsigned char)(0x80 | (len - 1)); if (std::fwrite(&count, 1, 1, f) < 1) std::fprintf(stderr, "RLE_write_pkt: could not write RLE pixel count (std::fwrite: %s)\n", std::strerror(errno)); if (std::fwrite(data, pixelsize, 1, f) < 1) std::fprintf(stderr, "RLE_write_pkt: could not write RLE pixel value (std::fwrite: %s)\n", std::strerror(errno)); } else { count = (unsigned char)(len - 1); if (std::fwrite(&count, 1, 1, f) < 1) std::fprintf(stderr, "RLE_write_pkt: could not write RAW pixel count (std::fwrite: %s)\n", std::strerror(errno)); if (std::fwrite(data, pixelsize, len, f) < (unsigned)len) std::fprintf(stderr, "RLE_write_pkt: could not write %d RAW pixels (std::fwrite: %s)\n", len, std::strerror(errno)); } } int RLE_compress(std::FILE *f, t_tgaimg const *img) { int pixelsize; unsigned char const *datap; unsigned char *pktdata; unsigned int pktlen; t_tgapkttype pkttype; unsigned char *pktdatap; unsigned int actual = 0, perceived = 0; int i; pkttype = RAW; pktdatap = NULL; if (img == NULL) return -1; if (img->data == NULL) return -1; pixelsize = getpixelsize(img); if (pixelsize == 0) return -1; datap = img->data; pktdata = (unsigned char*)xmalloc(img->width*img->height*pixelsize); pktlen = 0; for (i = 0; i < img->width*img->height;) { if (pktlen == 0) { pktdatap = pktdata; std::memcpy(pktdatap, datap, pixelsize); pktlen++; i++; pktdatap += pixelsize; datap += pixelsize; pkttype = RAW; continue; } if (pktlen == 1) { if (std::memcmp(datap - pixelsize, datap, pixelsize) == 0) { pkttype = RLE; } } if (pkttype == RLE) { if (std::memcmp(datap - pixelsize, datap, pixelsize) != 0 || pktlen >= 128) { RLE_write_pkt(f, pkttype, pktlen, pktdata, pixelsize); actual += 1 + pixelsize; perceived += pixelsize*pktlen; pktlen = 0; } else { pktlen++; i++; datap += pixelsize; } } else { if (std::memcmp(datap - pixelsize, datap, pixelsize) == 0 || pktlen >= 129) { datap -= pixelsize; /* push back last pixel */ i--; if (i < 0) std::fprintf(stderr, "BUG!\n"); pktlen--; RLE_write_pkt(f, pkttype, pktlen, pktdata, pixelsize); actual += 1 + pixelsize*pktlen; perceived += pixelsize*pktlen; pktlen = 0; } else { std::memcpy(pktdatap, datap, pixelsize); pktlen++; i++; pktdatap += pixelsize; datap += pixelsize; } } } if (pktlen) { RLE_write_pkt(f, pkttype, pktlen, pktdata, pixelsize); if (pkttype == RLE) { actual += 1 + pixelsize; perceived += pixelsize*pktlen; } else { actual += 1 + pixelsize*pktlen; perceived += pixelsize*pktlen; } pktlen = 0; } std::fprintf(stderr, "RLE_compress: wrote %u bytes (%u uncompressed)\n", actual, perceived); xfree((void*)pktdata); return 0; } } extern int getpixelsize(t_tgaimg const *img) { switch (img->bpp) { case 8: return 1; case 15: case 16: return 2; case 24: return 3; case 32: return 4; default: std::fprintf(stderr, "load_tga: color depth %u is not supported!\n", img->bpp); return 0; } } extern t_tgaimg * new_tgaimg(unsigned int width, unsigned int height, unsigned int bpp, t_tgaimgtype imgtype) { t_tgaimg *img; img = (t_tgaimg*)xmalloc(sizeof(t_tgaimg)); img->idlen = 0; img->cmaptype = tgacmap_none; img->imgtype = imgtype; img->cmapfirst = 0; img->cmaplen = 0; img->cmapes = 0; img->xorigin = 0; img->yorigin = 0; img->width = width; img->height = height; img->bpp = bpp; img->desc = 0; /* no attribute bits, top, left, and zero reserved */ img->data = NULL; img->extareaoff = 0; img->devareaoff = 0; return img; } extern t_tgaimg * load_tgaheader(void) { t_tgaimg *img; img = (t_tgaimg*)xmalloc(sizeof(t_tgaimg)); img->idlen = file_readb(); img->cmaptype = file_readb(); img->imgtype = file_readb(); img->cmapfirst = file_readw_le(); img->cmaplen = file_readw_le(); img->cmapes = file_readb(); img->xorigin = file_readw_le(); img->yorigin = file_readw_le(); img->width = file_readw_le(); img->height = file_readw_le(); img->bpp = file_readb(); img->desc = file_readb(); img->data = NULL; img->extareaoff = 0; /* ignored when reading */ img->devareaoff = 0; /* ignored when reading */ return img; } extern t_tgaimg * load_tga(std::FILE *f) { t_tgaimg *img; int pixelsize; file_rpush(f); img = load_tgaheader(); /* make sure we understand the header fields */ if (img->cmaptype != tgacmap_none) { std::fprintf(stderr, "load_tga: Color-mapped images are not (yet?) supported!\n"); xfree(img); return NULL; } if (img->imgtype != tgaimgtype_uncompressed_truecolor && img->imgtype != tgaimgtype_rlecompressed_truecolor) { std::fprintf(stderr, "load_tga: imagetype %u is not supported. (only 2 and 10 are supported)\n", img->imgtype); xfree(img); return NULL; } pixelsize = getpixelsize(img); if (pixelsize == 0) { xfree(img); return NULL; } /* Skip the ID if there is one */ if (img->idlen > 0) { std::fprintf(stderr, "load_tga: ID present, skipping %d bytes\n", img->idlen); if (std::fseek(f, img->idlen, SEEK_CUR) < 0) std::fprintf(stderr, "load_tga: could not seek %u bytes forward (std::fseek: %s)\n", img->idlen, std::strerror(errno)); } /* Now, we can alloc img->data */ img->data = (std::uint8_t*)xmalloc(img->width*img->height*pixelsize); if (img->imgtype == tgaimgtype_uncompressed_truecolor) { if (std::fread(img->data, pixelsize, img->width*img->height, f) < (unsigned)(img->width*img->height)) { std::fprintf(stderr, "load_tga: error while reading data!\n"); xfree(img->data); xfree(img); return NULL; } } else { /* == tgaimgtype_rlecompressed_truecolor */ if (RLE_decompress(f, img->data, img->width*img->height*pixelsize, pixelsize) < 0) { std::fprintf(stderr, "load_tga: error while decompressing data!\n"); xfree(img->data); xfree(img); return NULL; } } file_rpop(); if ((img->desc & tgadesc_horz) == 1) { /* right, want left */ if (rotate_leftright(img) < 0) { std::fprintf(stderr, "ERROR: rotate_leftright failed!\n"); } } if ((img->desc & tgadesc_vert) == 0) { /* bottom, want top */ if (rotate_updown(img) < 0) { std::fprintf(stderr, "ERROR: rotate_updown failed!\n"); } } return img; } extern int write_tga(std::FILE *f, t_tgaimg *img) { if (f == NULL) return -1; if (img == NULL) return -1; if (img->data == NULL) return -1; if (img->idlen != 0) return -1; if (img->cmaptype != tgacmap_none) return -1; if (img->imgtype != tgaimgtype_uncompressed_truecolor && img->imgtype != tgaimgtype_rlecompressed_truecolor) return -1; file_wpush(f); file_writeb(img->idlen); file_writeb(img->cmaptype); file_writeb(img->imgtype); file_writew_le(img->cmapfirst); file_writew_le(img->cmaplen); file_writeb(img->cmapes); file_writew_le(img->xorigin); file_writew_le(img->yorigin); file_writew_le(img->width); file_writew_le(img->height); file_writeb(img->bpp); file_writeb(img->desc); if ((img->desc&tgadesc_horz) == 1) { /* right, want left */ std::fprintf(stderr, "write_tga: flipping horizontally\n"); if (rotate_leftright(img) < 0) { std::fprintf(stderr, "ERROR: rotate_updown failed!\n"); } } if ((img->desc&tgadesc_vert) == 0) { /* bottom, want top */ std::fprintf(stderr, "write_tga: flipping vertically\n"); if (rotate_updown(img) < 0) { std::fprintf(stderr, "ERROR: rotate_updown failed!\n"); } } if (img->imgtype == tgaimgtype_uncompressed_truecolor) { int pixelsize; pixelsize = getpixelsize(img); if (pixelsize == 0) return -1; if (std::fwrite(img->data, pixelsize, img->width*img->height, f) < (unsigned)(img->width*img->height)) { std::fprintf(stderr, "write_tga: could not write %d pixels (std::fwrite: %s)\n", img->width*img->height, std::strerror(errno)); file_wpop(); return -1; } } else if (img->imgtype == tgaimgtype_rlecompressed_truecolor) { std::fprintf(stderr, "write_tga: using RLE compression\n"); if (RLE_compress(f, img) < 0) { std::fprintf(stderr, "write_tga: RLE compression failed.\n"); } } /* Write the file-footer */ file_writed_le(img->extareaoff); file_writed_le(img->devareaoff); if (std::fwrite(TGAMAGIC, std::strlen(TGAMAGIC) + 1, 1, f) < 1) std::fprintf(stderr, "write_tga: could not write TGA footer magic (std::fwrite: %s)\n", std::strerror(errno)); /* Ready */ file_wpop(); return 0; } extern void destroy_img(t_tgaimg * img) { if (img == NULL) return; if (img->data) xfree(img->data); xfree(img); } extern void print_tga_info(t_tgaimg const * img, std::FILE * fp) { unsigned int interleave; unsigned int attrbits; char const * typestr; char const * cmapstr; char const * horzstr; char const * vertstr; char const * intlstr; if (!img || !fp) return; interleave = ((img->desc&tgadesc_interleave1) != 0) * 2 + ((img->desc&tgadesc_interleave2) != 0); attrbits = img->desc&(tgadesc_attrbits0 | tgadesc_attrbits1 | tgadesc_attrbits2 | tgadesc_attrbits3); switch (img->imgtype) { case tgaimgtype_empty: typestr = "No Image Data Included"; break; case tgaimgtype_uncompressed_mapped: typestr = "Uncompressed, Color-mapped Image"; break; case tgaimgtype_uncompressed_truecolor: typestr = "Uncompressed, True-color Image"; break; case tgaimgtype_uncompressed_monochrome: typestr = "Uncompressed, Black-and-white image"; break; case tgaimgtype_rlecompressed_mapped: typestr = "Run-length encoded, Color-mapped Image"; break; case tgaimgtype_rlecompressed_truecolor: typestr = "Run-length encoded, True-color Image"; break; case tgaimgtype_rlecompressed_monochrome: typestr = "Run-length encoded, Black-and-white image"; break; case tgaimgtype_huffman_mapped: typestr = "Huffman encoded, Color-mapped image"; break; case tgaimgtype_huffman_4pass_mapped: typestr = "Four-pass Huffman encoded, Color-mapped image"; break; default: typestr = "unknown"; } switch (img->cmaptype) { case tgacmap_none: cmapstr = "None"; break; case tgacmap_included: cmapstr = "Included"; break; default: cmapstr = "Unknown"; } if ((img->desc&tgadesc_horz) == 0) { horzstr = "left"; } else { horzstr = "right"; } if ((img->desc&tgadesc_vert) == 0) { vertstr = "bottom"; } else { vertstr = "top"; } switch (interleave) { case 0: intlstr = "none"; break; case 2: intlstr = "two way"; break; case 3: intlstr = "four way"; break; case 4: default: intlstr = "unknown"; break; } std::fprintf(fp, "TGAHeader: IDLength=%u ColorMapType=%u(%s)\n", img->idlen, img->cmaptype, cmapstr); std::fprintf(fp, "TGAHeader: ImageType=%u(%s)\n", img->imgtype, typestr); std::fprintf(fp, "TGAHeader: ColorMap: FirstEntryIndex=%u ColorMapLength=%u\n", img->cmapfirst, img->cmaplen); std::fprintf(fp, "TGAHeader: ColorMap: ColorMapEntrySize=%ubits\n", img->cmapes); std::fprintf(fp, "TGAHeader: X-origin=%u Y-origin=%u Width=%u(0x%x) Height=%u(0x%x)\n", img->xorigin, img->yorigin, img->width, img->width, img->height, img->height); std::fprintf(fp, "TGAHeader: PixelDepth=%ubits ImageDescriptor=0x%02x(%u attribute bits, origin is %s %s, interleave=%s)\n", img->bpp, img->desc, attrbits, vertstr, horzstr, intlstr); } } } pvpgn-1.99.7.2.1+dfsg/src/bniutils/tga.h000066400000000000000000000071501462640461400175470ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef INCLUDED_TGA_H #define INCLUDED_TGA_H #include #include namespace pvpgn { namespace bni { typedef struct { std::uint8_t idlen; /* number of bytes in Field 6: 0==no id */ std::uint8_t cmaptype; /* colormap: 0==none, 1==included, <128==TrueVision, >=128==developer */ std::uint8_t imgtype; /* pixel format: <128==TrueVision, >=128==developer */ std::uint16_t cmapfirst; /* first entry offset */ std::uint16_t cmaplen; /* number of colormap entries */ std::uint8_t cmapes; /* size of a single colormap entry in bits, 15 forces 1 attribute bit, 32 forces 8 */ std::uint16_t xorigin; /* x coordinate of lower left hand corner with origin at left of screen */ std::uint16_t yorigin; /* y coordinate of lower left hand corner with origin at bottom of screen */ std::uint16_t width; /* width in pixels */ std::uint16_t height; /* height in pixels */ std::uint8_t bpp; /* bits per pixel, including attributes and alpha channel */ std::uint8_t desc; /* image descriptor: bits 0,1,2,3==num attribute bits per pixel, bit 4==horizontal order, bit 5==vertical order, bits 6,7==interleaving */ /* field 6, optional */ /* field 7, colormap data in ARGB, optional, entries are (min(cmapes/3,8)*3+7)/8 bits wide */ std::uint8_t* data; /* field 9, developer area, optional */ /* field 10, extension area, optional */ std::uint32_t extareaoff; /* extension area offset, 0==none */ std::uint32_t devareaoff; /* developer area offset, 0==none */ /* magic, null terminated */ } t_tgaimg; typedef enum { tgaimgtype_empty = 0, tgaimgtype_uncompressed_mapped = 1, tgaimgtype_uncompressed_truecolor = 2, tgaimgtype_uncompressed_monochrome = 3, tgaimgtype_rlecompressed_mapped = 9, tgaimgtype_rlecompressed_truecolor = 10, tgaimgtype_rlecompressed_monochrome = 11, tgaimgtype_huffman_mapped = 32, tgaimgtype_huffman_4pass_mapped = 33 } t_tgaimgtype; typedef enum { tgadesc_attrbits0 = 1, tgadesc_attrbits1 = 2, tgadesc_attrbits2 = 4, tgadesc_attrbits3 = 8, tgadesc_horz = 16, tgadesc_vert = 32, tgadesc_interleave1 = 64, tgadesc_interleave2 = 128 } t_tgadesc; typedef enum { tgacmap_none = 0, tgacmap_included = 1 } t_tgacmap; typedef enum { RLE, RAW } t_tgapkttype; /* "new" style TGA allowing for developer and extension areas must have this magic at the end of the file */ #define TGAMAGIC "TRUEVISION-XFILE." extern t_tgaimg * new_tgaimg(unsigned int width, unsigned int height, unsigned int bpp, t_tgaimgtype imgtype); extern int getpixelsize(t_tgaimg const *img); extern t_tgaimg * load_tgaheader(void); extern t_tgaimg * load_tga(std::FILE *f); extern int write_tga(std::FILE *f, t_tgaimg *img); extern void destroy_img(t_tgaimg * img); extern void print_tga_info(t_tgaimg const * img, std::FILE * fp); } } #endif pvpgn-1.99.7.2.1+dfsg/src/bniutils/tgainfo.cpp000066400000000000000000000064521462640461400207620ustar00rootroot00000000000000/* Copyright (C) 2000 Marco Ziech (mmz@gmx.net) Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/setup_before.h" #include #include #include #include "common/version.h" #include "tga.h" #include "fileio.h" #include "common/setup_after.h" using namespace pvpgn::bni; namespace { void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [--] []\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { char const * tgafile = NULL; std::FILE * fp; int a; int forcefile = 0; char dash[] = "-"; /* unique address used as flag */ if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (forcefile && !tgafile) tgafile = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !tgafile) tgafile = dash; else if (argv[a][0] != '-' && !tgafile) tgafile = argv[a]; else if (forcefile || argv[a][0] != '-' || std::strcmp(argv[a], "-") == 0) { std::fprintf(stderr, "%s: extra file argument \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "--") == 0) forcefile = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (!tgafile) tgafile = dash; if (tgafile == dash) fp = stdin; else if (!(fp = std::fopen(tgafile, "r"))) { std::fprintf(stderr, "%s: could not open TGA file \"%s\" for reading (std::fopen: %s)\n", argv[0], tgafile, std::strerror(errno)); return EXIT_FAILURE; } { t_tgaimg * tgaimg; file_rpush(fp); if (!(tgaimg = load_tgaheader())) { std::fprintf(stderr, "%s: could not load TGA header\n", argv[0]); if (tgafile != dash && std::fclose(fp) < 0) std::fprintf(stderr, "%s: could not close file \"%s\" after reading (std::fclose: %s)\n", argv[0], tgafile, std::strerror(errno)); return EXIT_FAILURE; } print_tga_info(tgaimg, stdout); file_rpop(); } if (tgafile != dash && std::fclose(fp) < 0) std::fprintf(stderr, "%s: could not close file \"%s\" after reading (std::fclose: %s)\n", argv[0], tgafile, std::strerror(errno)); return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bnpass/000077500000000000000000000000001462640461400162555ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/bnpass/CMakeLists.txt000066400000000000000000000003331462640461400210140ustar00rootroot00000000000000add_executable(bnpass bnpass.cpp) target_link_libraries(bnpass common compat) add_executable(sha1hash sha1hash.cpp) target_link_libraries(sha1hash common compat) install(TARGETS bnpass sha1hash DESTINATION ${BINDIR}) pvpgn-1.99.7.2.1+dfsg/src/bnpass/bnpass.cpp000066400000000000000000000057261462640461400202610ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include "common/eventlog.h" #include "common/bnethash.h" #include "common/version.h" #include "common/xstring.h" #include "common/setup_after.h" using namespace pvpgn; namespace { void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [--] []\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { char const * pass = NULL; int a; int forcepass = 0; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (forcepass && !pass) pass = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !pass) pass = argv[a]; else if (argv[a][0] != '-' && !pass) pass = argv[a]; else if (forcepass || argv[a][0] != '-' || std::strcmp(argv[a], "-") == 0) { std::fprintf(stderr, "%s: extra password argument \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "--") == 0) forcepass = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } { char buff[256]; t_hash hash; eventlog_set(stderr); /* bnet_hash() and friends use eventlog */ if (!pass) { std::printf("Enter password to hash: "); std::fflush(stdout); std::fgets(buff, 256, stdin); if (buff[0] != '\0') buff[std::strlen(buff) - 1] = '\0'; } else { std::strncpy(buff, pass, sizeof(buff)); buff[sizeof(buff)-1] = '\0'; } /* FIXME: what is the max password length? */ strtolower(buff); bnet_hash(&hash, std::strlen(buff), buff); std::printf("\"BNET\\\\acct\\\\passhash1\"=\"%s\"\n", hash_get_str(hash)); } return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bnpass/sha1hash.cpp000066400000000000000000000055371462640461400204730ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include "common/eventlog.h" #include "common/bnethash.h" #include "common/version.h" #include "common/setup_after.h" using namespace pvpgn; namespace { void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [--] []\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { char const * pass = NULL; int a; int forcepass = 0; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (forcepass && !pass) pass = argv[a]; else if (std::strcmp(argv[a], "-") == 0 && !pass) pass = argv[a]; else if (argv[a][0] != '-' && !pass) pass = argv[a]; else if (forcepass || argv[a][0] != '-' || std::strcmp(argv[a], "-") == 0) { std::fprintf(stderr, "%s: extra password argument \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "--") == 0) forcepass = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } { char buff[256]; t_hash hash; eventlog_set(stderr); /* sha1_hash() and friends use eventlog */ if (!pass) { std::printf("Enter password to hash: "); std::fflush(stdout); std::fgets(buff, 256, stdin); if (buff[0] != '\0') buff[std::strlen(buff) - 1] = '\0'; } else { std::strncpy(buff, pass, sizeof(buff)); buff[sizeof(buff)-1] = '\0'; } sha1_hash(&hash, std::strlen(buff), buff); std::printf("sha1 hash = %s\n", hash_get_str(hash)); } return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bnproxy/000077500000000000000000000000001462640461400164705ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/bnproxy/bnproxy.c000066400000000000000000001013731462640461400203420ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #ifdef HAVE_STDDEF_H # include #else # ifndef NULL # define NULL ((void *)0) # endif #endif #ifdef STDC_HEADERS # include #endif #include "compat/exitstatus.h" #ifdef HAVE_UNISTD_H # include #endif #include "compat/stdfileno.h" #ifdef HAVE_FCNTL_H # include #else # ifdef HAVE_SYS_FILE_H # include # endif #endif #ifdef HAVE_STRING_H # include #else # ifdef HAVE_STRINGS_H # include # endif #endif #ifdef HAVE_MEMORY_H # include #endif #include "compat/memset.h" #include "compat/memcpy.h" #include #include #include "compat/strerror.h" #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #include "compat/socket.h" #include "compat/recv.h" #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #include "compat/netinet_in.h" #ifdef HAVE_ARPA_INET_H # include #endif #include "compat/inet_ntoa.h" #ifdef HAVE_NETDB_H # include #endif #include "compat/psock.h" #include "common/packet.h" #include "common/init_protocol.h" #include "common/hexdump.h" #include "common/eventlog.h" #include "common/bn_type.h" #include "common/queue.h" #include "common/network.h" #include "common/list.h" #include "common/util.h" #include "virtconn.h" #include "common/version.h" #include "common/setup_after.h" FILE * hexstrm = NULL; /* FIXME: This code is horribly unreadable. The UDP stuff is a hack for now. */ #define PROXY_FLAG_UDP 1 static int init_virtconn(t_virtconn * vc, struct sockaddr_in servaddr); static int proxy_process(unsigned short server_listen_port, struct sockaddr_in servaddr); static void usage(char const * progname); static int init_virtconn(t_virtconn * vc, struct sockaddr_in servaddr) { int addlen; char connect_type; /* determine connection type by first character sent by client */ addlen = psock_recv(virtconn_get_client_socket(vc), &connect_type, sizeof(char), 0); if (addlen < 0 && (psock_errno() == PSOCK_EINTR || psock_errno() == PSOCK_EAGAIN || psock_errno() == PSOCK_EWOULDBLOCK)) return 0; /* error occurred or connection lost */ if (addlen < 1) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not get virtconn class (closing connection) (psock_recv: %s)", virtconn_get_client_socket(vc), pstrerror(psock_errno())); return -1; } switch (connect_type) { case CLIENT_INITCONN_CLASS_BNET: eventlog(eventlog_level_info, __FUNCTION__, "[%d] client initiated normal connection", virtconn_get_client_socket(vc)); virtconn_set_class(vc, virtconn_class_bnet); break; case CLIENT_INITCONN_CLASS_FILE: eventlog(eventlog_level_info, __FUNCTION__, "[%d] client initiated file download connection", virtconn_get_client_socket(vc)); virtconn_set_class(vc, virtconn_class_file); break; case CLIENT_INITCONN_CLASS_BOT: eventlog(eventlog_level_info, __FUNCTION__, "[%d] client initiated chat bot connection", virtconn_get_client_socket(vc)); virtconn_set_class(vc, virtconn_class_bot); break; default: eventlog(eventlog_level_error, __FUNCTION__, "[%d] client initiated unknown connection type 0x%02hx (length %d) (closing connection)", virtconn_get_client_socket(vc), (unsigned short)connect_type, addlen); return -1; } /* now connect to the real server */ if (psock_connect(virtconn_get_server_socket(vc), (struct sockaddr *)&servaddr, (psock_t_socklen)sizeof(servaddr)) < 0) { if (psock_errno() != PSOCK_EINPROGRESS) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not connect to server (psock_connect: %s)\n", virtconn_get_client_socket(vc), pstrerror(psock_errno())); return -1; } virtconn_set_state(vc, virtconn_state_connecting); } else virtconn_set_state(vc, virtconn_state_connected); { t_packet * packet; if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not create packet", virtconn_get_client_socket(vc)); return -1; } packet_append_data(packet, &connect_type, 1); queue_push_packet(virtconn_get_serverout_queue(vc), packet); packet_del_ref(packet); } return 0; } static int proxy_process(unsigned short server_listen_port, struct sockaddr_in servaddr) { int lsock; struct sockaddr_in laddr; t_psock_fd_set rfds, wfds; int highest_fd; int udpsock; t_virtconn * vc; t_elem const * curr; int csocket; int ssocket; if ((udpsock = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_DGRAM, PSOCK_IPPROTO_UDP)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create UDP socket (psock_socket: %s)", pstrerror(psock_errno())); return -1; } if (psock_ctl(udpsock, PSOCK_NONBLOCK) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set UDP listen socket to non-blocking mode (psock_ctl: %s)", pstrerror(psock_errno())); if ((lsock = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create listening socket (psock_socket: %s)", pstrerror(psock_errno())); psock_close(udpsock); return -1; } { int val = 1; if (psock_setsockopt(lsock, PSOCK_SOL_SOCKET, PSOCK_SO_REUSEADDR, &val, (psock_t_socklen)sizeof(int)) < 0) eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not set socket option SO_REUSEADDR (psock_setsockopt: %s)", lsock, pstrerror(psock_errno())); /* not a fatal error... */ } memset(&laddr, 0, sizeof(laddr)); laddr.sin_family = PSOCK_AF_INET; laddr.sin_port = htons(server_listen_port); laddr.sin_addr.s_addr = htonl(INADDR_ANY); if (psock_bind(lsock, (struct sockaddr *)&laddr, (psock_t_socklen)sizeof(laddr)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not bind socket to address 0.0.0.0:%hu TCP (psock_bind: %s)", server_listen_port, pstrerror(psock_errno())); psock_close(udpsock); psock_close(lsock); return -1; } memset(&laddr, 0, sizeof(laddr)); laddr.sin_family = PSOCK_AF_INET; laddr.sin_port = htons(server_listen_port); laddr.sin_addr.s_addr = htonl(INADDR_ANY); if (psock_bind(udpsock, (struct sockaddr *)&laddr, (psock_t_socklen)sizeof(laddr)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not bind socket to address 0.0.0.0:%hu UDP (psock_bind: %s)", server_listen_port, pstrerror(psock_errno())); psock_close(udpsock); psock_close(lsock); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "bound to UDP port %hu", server_listen_port); /* tell socket to listen for connections */ if (psock_listen(lsock, LISTEN_QUEUE) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not listen (psock_listen: %s)", pstrerror(psock_errno())); psock_close(udpsock); psock_close(lsock); return -1; } if (psock_ctl(lsock, PSOCK_NONBLOCK) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not set TCP listen socket to non-blocking mode (psock_ctl: %s)", pstrerror(psock_errno())); eventlog(eventlog_level_info, __FUNCTION__, "listening on TCP port %hu", server_listen_port); for (;;) { /* loop over all connections to create the sets for select() */ PSOCK_FD_ZERO(&rfds); PSOCK_FD_ZERO(&wfds); highest_fd = lsock; PSOCK_FD_SET(lsock, &rfds); if (udpsock > highest_fd) highest_fd = udpsock; PSOCK_FD_SET(udpsock, &rfds); LIST_TRAVERSE_CONST(virtconnlist(), curr) { vc = elem_get_data(curr); csocket = virtconn_get_client_socket(vc); if (queue_get_length((t_queue const * const *)virtconn_get_clientout_queue(vc)) > 0) PSOCK_FD_SET(csocket, &wfds); /* pending output, also check for writeability */ PSOCK_FD_SET(csocket, &rfds); if (csocket > highest_fd) highest_fd = csocket; switch (virtconn_get_state(vc)) { case virtconn_state_connecting: eventlog(eventlog_level_debug, __FUNCTION__, "waiting for %d to finish connecting", ssocket); ssocket = virtconn_get_server_socket(vc); PSOCK_FD_SET(ssocket, &wfds); /* wait for connect to complete */ if (ssocket > highest_fd) highest_fd = ssocket; break; case virtconn_state_connected: eventlog(eventlog_level_debug, __FUNCTION__, "checking for reading on connected socket %d", ssocket); ssocket = virtconn_get_server_socket(vc); if (queue_get_length((t_queue const * const *)virtconn_get_serverout_queue(vc)) > 0) PSOCK_FD_SET(ssocket, &wfds); /* pending output, also check for writeability */ PSOCK_FD_SET(ssocket, &rfds); if (ssocket > highest_fd) highest_fd = ssocket; break; default: /* avoid warning */ break; } } /* find which sockets need servicing */ if (psock_select(highest_fd + 1, &rfds, &wfds, NULL, NULL) < 0) { if (errno != PSOCK_EINTR) eventlog(eventlog_level_error, __FUNCTION__, "select failed (select: %s)", pstrerror(errno)); continue; } /* check for incoming connection */ if (PSOCK_FD_ISSET(lsock, &rfds)) { int asock; struct sockaddr_in caddr; psock_t_socklen caddr_len; /* accept the connection */ caddr_len = sizeof(caddr); if ((asock = psock_accept(lsock, (struct sockaddr *)&caddr, &caddr_len)) < 0) { if (psock_errno() == PSOCK_EWOULDBLOCK || psock_errno() == PSOCK_ECONNABORTED) /* BSD, POSIX error for aborted connections, SYSV often uses EAGAIN */ eventlog(eventlog_level_error, __FUNCTION__, "client aborted connection (psock_accept: %s)", pstrerror(psock_errno())); else /* EAGAIN can mean out of resources _or_ connection aborted */ if (psock_errno() != PSOCK_EINTR) eventlog(eventlog_level_error, __FUNCTION__, "could not accept new connection (psock_accept: %s)", pstrerror(psock_errno())); } else { int ssd; int val = 1; eventlog(eventlog_level_info, __FUNCTION__, "[%d] accepted connection from %s:%hu", asock, inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); if (psock_setsockopt(asock, PSOCK_SOL_SOCKET, PSOCK_SO_KEEPALIVE, &val, (psock_t_socklen)sizeof(val)) < 0) eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not set socket option SO_KEEPALIVE (psock_setsockopt: %s)", asock, pstrerror(psock_errno())); /* not a fatal error */ if (psock_ctl(asock, PSOCK_NONBLOCK) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not set TCP socket to non-blocking mode (closing connection) (psock_ctl: %s)", asock, pstrerror(psock_errno())); psock_close(asock); } else if ((ssd = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] could create TCP socket (closing connection) (psock_socket: %s)", asock, pstrerror(psock_errno())); psock_close(asock); } else if (psock_ctl(ssd, PSOCK_NONBLOCK) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not set TCP socket to non-blocking mode (closing connection) (psock_ctl: %s)", asock, pstrerror(psock_errno())); psock_close(ssd); psock_close(asock); } else if (!(vc = virtconn_create(asock, ssd, ntohl(caddr.sin_addr.s_addr), BNETD_MIN_TEST_PORT))) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] unable to create new connection (closing connection)", asock); psock_close(ssd); psock_close(asock); } else { memset(&caddr, 0, sizeof(caddr)); caddr.sin_family = PSOCK_AF_INET; caddr.sin_port = htons(virtconn_get_udpport(vc)); caddr.sin_addr.s_addr = htonl(virtconn_get_udpaddr(vc)); eventlog(eventlog_level_info, __FUNCTION__, "[%d] addr now %s:%hu", asock, inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); } } } eventlog(eventlog_level_debug, __FUNCTION__, "checking for incoming UDP"); if (PSOCK_FD_ISSET(udpsock, &rfds)) { t_packet * upacket; struct sockaddr_in toaddr; struct sockaddr_in fromaddr; psock_t_socklen fromlen; int len; if (!(upacket = packet_create(packet_class_raw))) eventlog(eventlog_level_error, __FUNCTION__, "could not allocate raw packet for input"); else { /* packet_set_flags(upacket,PROXY_FLAG_UDP);*/ fromlen = sizeof(fromaddr); if ((len = psock_recvfrom(udpsock, packet_get_raw_data_build(upacket, 0), MAX_PACKET_SIZE, 0, (struct sockaddr *)&fromaddr, &fromlen)) < 0) { if (psock_errno() != PSOCK_EINTR && psock_errno() != PSOCK_EAGAIN && psock_errno() != PSOCK_EWOULDBLOCK) eventlog(eventlog_level_error, __FUNCTION__, "could not recv UDP datagram (psock_recvfrom: %s)", pstrerror(psock_errno())); } else { if (fromaddr.sin_family != PSOCK_AF_INET) eventlog(eventlog_level_error, __FUNCTION__, "got UDP datagram with bad address family %d", fromaddr.sin_family); else { char tempa[32]; char tempb[32]; packet_set_size(upacket, len); if (fromaddr.sin_addr.s_addr == servaddr.sin_addr.s_addr) /* from server */ { if ((curr = list_get_first_const(virtconnlist()))) /* hack.. find proper client */ { vc = elem_get_data(curr); memset(&toaddr, 0, sizeof(toaddr)); toaddr.sin_family = PSOCK_AF_INET; toaddr.sin_port = htons(virtconn_get_udpport(vc)); toaddr.sin_addr.s_addr = htonl(virtconn_get_udpaddr(vc)); eventlog(eventlog_level_info, __FUNCTION__, "[%d] addr by UDP send is %s:%hu", virtconn_get_client_socket(vc), inet_ntoa(toaddr.sin_addr), ntohs(toaddr.sin_port)); if (hexstrm) { strcpy(tempa, inet_ntoa(fromaddr.sin_addr)); strcpy(tempb, inet_ntoa(toaddr.sin_addr)); fprintf(hexstrm, "%d: srv prot=UDP from=%s:%hu to=%s:%hu length=%d\n", udpsock, tempa, ntohs(fromaddr.sin_port), tempb, ntohs(toaddr.sin_port), len); hexdump(hexstrm, packet_get_raw_data(upacket, 0), len); } /*queue_push_packet(virtconn_get_clientout_queue(__));*/ /* where to queue ... */ for (;;) /* hack.. just block for now */ { if (psock_sendto(udpsock, packet_get_raw_data_const(upacket, 0), len, 0, (struct sockaddr *)&toaddr, (psock_t_socklen)sizeof(toaddr)) < len) { if (psock_errno() == PSOCK_EINTR || psock_errno() == PSOCK_EAGAIN || psock_errno() == PSOCK_EWOULDBLOCK) continue; eventlog(eventlog_level_error, __FUNCTION__, "could not send UDP datagram to client (psock_sendto: %s)", pstrerror(psock_errno())); } break; } } } else /* from client */ { if (hexstrm) { strcpy(tempa, inet_ntoa(fromaddr.sin_addr)); strcpy(tempb, inet_ntoa(servaddr.sin_addr)); fprintf(hexstrm, "%d: clt prot=UDP from=%s:%hu to=%s:%hu length=%d\n", udpsock, tempa, ntohs(fromaddr.sin_port), tempb, ntohs(servaddr.sin_port), len); hexdump(hexstrm, packet_get_raw_data(upacket, 0), len); } /*queue_push_packet(virtconn_get_serverout_queue(vc));*/ for (;;) /* hack.. just block for now */ { if (psock_sendto(udpsock, packet_get_raw_data_const(upacket, 0), len, 0, (struct sockaddr *)&servaddr, (psock_t_socklen)sizeof(servaddr)) < len) { if (psock_errno() == PSOCK_EINTR || psock_errno() == PSOCK_EAGAIN || psock_errno() == PSOCK_EWOULDBLOCK) continue; eventlog(eventlog_level_error, __FUNCTION__, "could not send UDP datagram to server (psock_sendto: %s)", pstrerror(psock_errno())); } break; } } } } packet_del_ref(upacket); } } /* search connections for sockets that need service */ eventlog(eventlog_level_debug, __FUNCTION__, "checking for sockets that need service"); LIST_TRAVERSE_CONST(virtconnlist(), curr) { unsigned int currsize; t_packet * packet; vc = elem_get_data(curr); csocket = virtconn_get_client_socket(vc); if (virtconn_get_state(vc) == virtconn_state_connected || virtconn_get_state(vc) == virtconn_state_connecting) ssocket = virtconn_get_server_socket(vc); else ssocket = -1; eventlog(eventlog_level_debug, __FUNCTION__, "checking %d for client readability", csocket); if (PSOCK_FD_ISSET(csocket, &rfds)) { if (virtconn_get_state(vc) == virtconn_state_initial) { if (init_virtconn(vc, servaddr) < 0) { virtconn_destroy(vc); continue; } } else { currsize = virtconn_get_clientin_size(vc); if (!queue_get_length(virtconn_get_clientin_queue(vc))) { switch (virtconn_get_class(vc)) { case virtconn_class_bnet: if (!(packet = packet_create(packet_class_bnet))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate normal packet for input"); continue; } break; case virtconn_class_file: if (!(packet = packet_create(packet_class_file))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate file packet for input"); continue; } break; case virtconn_class_bot: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate raw packet for input"); continue; } packet_set_size(packet, 1); /* start by only reading one char */ break; default: eventlog(eventlog_level_error, __FUNCTION__, "[%d] connection has bad type (closing connection)", virtconn_get_client_socket(vc)); virtconn_destroy(vc); continue; } queue_push_packet(virtconn_get_clientin_queue(vc), packet); packet_del_ref(packet); if (!queue_get_length(virtconn_get_clientin_queue(vc))) continue; /* push failed */ currsize = 0; } packet = queue_peek_packet((t_queue const * const *)virtconn_get_clientin_queue(vc)); /* avoid warning */ switch (net_recv_packet(csocket, packet, &currsize)) { case -1: virtconn_destroy(vc); continue; case 0: /* still working on it */ virtconn_set_clientin_size(vc, currsize); break; case 1: /* done reading */ if (virtconn_get_class(vc) == virtconn_class_bot && currsize < MAX_PACKET_SIZE) { char const * const temp = packet_get_raw_data_const(packet, 0); if (temp[currsize - 1] != '\r' && temp[currsize - 1] != '\n') { virtconn_set_clientin_size(vc, currsize); packet_set_size(packet, currsize + 1); break; /* no end of line, get another char */ } /* got a complete line... fall through */ } packet = queue_pull_packet(virtconn_get_clientin_queue(vc)); if (hexstrm) { fprintf(hexstrm, "%d: cli class=%s[0x%04hx] type=%s[0x%04hx] length=%hu\n", csocket, packet_get_class_str(packet), packet_get_class(packet), packet_get_type_str(packet, packet_dir_from_client), packet_get_type(packet), packet_get_size(packet)); hexdump(hexstrm, packet_get_raw_data_const(packet, 0), packet_get_size(packet)); } queue_push_packet(virtconn_get_serverout_queue(vc), packet); packet_del_ref(packet); virtconn_set_clientin_size(vc, 0); } } } eventlog(eventlog_level_debug, __FUNCTION__, "checking %d for server readability", ssocket); if (ssocket != -1 && PSOCK_FD_ISSET(ssocket, &rfds)) { currsize = virtconn_get_serverin_size(vc); if (!queue_get_length(virtconn_get_serverin_queue(vc))) { switch (virtconn_get_class(vc)) { case virtconn_class_bnet: if (!(packet = packet_create(packet_class_bnet))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate normal packet for input"); continue; } break; case virtconn_class_file: { unsigned int fileleft; if ((fileleft = virtconn_get_fileleft(vc)) > 0) { if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate raw file packet for input"); continue; } if (fileleft > MAX_PACKET_SIZE) packet_set_size(packet, MAX_PACKET_SIZE); else packet_set_size(packet, fileleft); } else { if (!(packet = packet_create(packet_class_file))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate file packet for input"); continue; } } } break; case virtconn_class_bot: if (!(packet = packet_create(packet_class_raw))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate raw packet for input"); continue; } packet_set_size(packet, MAX_PACKET_SIZE); /* read as much as possible */ break; default: eventlog(eventlog_level_error, __FUNCTION__, "[%d] connection has bad type (closing connection)", virtconn_get_client_socket(vc)); virtconn_destroy(vc); continue; } queue_push_packet(virtconn_get_serverin_queue(vc), packet); packet_del_ref(packet); if (!queue_get_length(virtconn_get_serverin_queue(vc))) continue; /* push failed */ currsize = 0; } packet = queue_peek_packet((t_queue const * const *)virtconn_get_serverin_queue(vc)); /* avoid warning */ switch (net_recv_packet(ssocket, packet, &currsize)) { case -1: virtconn_destroy(vc); continue; case 0: /* still working on it */ virtconn_set_serverin_size(vc, currsize); if (virtconn_get_class(vc) != virtconn_class_bot || currsize < 1) break; else packet_set_size(packet, currsize); /* fallthough... we take what we can get with the bot data */ case 1: /* done reading */ packet = queue_pull_packet(virtconn_get_serverin_queue(vc)); if (virtconn_get_class(vc) == virtconn_class_file) { unsigned int len = virtconn_get_fileleft(vc); if (len) virtconn_set_fileleft(vc, len - currsize); else if (packet_get_type(packet) == SERVER_FILE_REPLY && packet_get_size(packet) >= sizeof(t_server_file_reply)) virtconn_set_fileleft(vc, bn_int_get(packet->u.server_file_reply.filelen)); } queue_push_packet(virtconn_get_clientout_queue(vc), packet); packet_del_ref(packet); virtconn_set_serverin_size(vc, 0); } } eventlog(eventlog_level_debug, __FUNCTION__, "checking %d for client writeability", csocket); if (PSOCK_FD_ISSET(csocket, &wfds)) { currsize = virtconn_get_clientout_size(vc); switch (net_send_packet(csocket, queue_peek_packet((t_queue const * const *)virtconn_get_clientout_queue(vc)), &currsize)) /* avoid warning */ { case -1: virtconn_destroy(vc); continue; case 0: /* still working on it */ virtconn_set_clientout_size(vc, currsize); break; case 1: /* done sending */ packet = queue_pull_packet(virtconn_get_clientout_queue(vc)); if (hexstrm) { fprintf(hexstrm, "%d: srv class=%s[0x%04hx] type=%s[0x%04hx] length=%hu\n", csocket, packet_get_class_str(packet), packet_get_class(packet), packet_get_type_str(packet, packet_dir_from_server), packet_get_type(packet), packet_get_size(packet)); hexdump(hexstrm, packet_get_raw_data(packet, 0), packet_get_size(packet)); } packet_del_ref(packet); virtconn_set_clientout_size(vc, 0); } } eventlog(eventlog_level_debug, __FUNCTION__, "checking %d for server writeability", ssocket); if (ssocket != -1 && PSOCK_FD_ISSET(ssocket, &wfds)) { if (virtconn_get_state(vc) == virtconn_state_connecting) { int err; psock_t_socklen errlen; err = 0; errlen = sizeof(err); if (psock_getsockopt(ssocket, PSOCK_SOL_SOCKET, PSOCK_SO_ERROR, &err, &errlen) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "[%d] unable to read socket error (psock_getsockopt[psock_connect]: %s)", virtconn_get_client_socket(vc), pstrerror(psock_errno())); virtconn_destroy(vc); continue; } if (errlen == 0 || err == 0) virtconn_set_state(vc, virtconn_state_connected); else { eventlog(eventlog_level_error, __FUNCTION__, "[%d] could not connect to server (psock_getsockopt[psock_connect]: %s)", virtconn_get_client_socket(vc), pstrerror(err)); virtconn_destroy(vc); continue; } } else { currsize = virtconn_get_serverout_size(vc); switch (net_send_packet(ssocket, queue_peek_packet((t_queue const * const *)virtconn_get_serverout_queue(vc)), &currsize)) /* avoid warning */ { case -1: virtconn_destroy(vc); continue; case 0: /* still working on it */ virtconn_set_serverout_size(vc, currsize); break; case 1: /* done sending */ packet = queue_pull_packet(virtconn_get_serverout_queue(vc)); packet_del_ref(packet); virtconn_set_serverout_size(vc, 0); } } } } eventlog(eventlog_level_debug, __FUNCTION__, "done checking"); } return 0; } static void usage(char const * progname) { fprintf(stderr, "usage: %s [] []\n" " -d FILE, --hexdump=FILE do hex dump of packets into FILE\n" " -l FILE, --logfile=FILE save eventlog lines into FILE\n" " -p PORT, --port=PORT listen for connections on port PORT\n" #ifdef DO_DAEMONIZE " -f, --foreground don't daemonize\n" #else " -f, --foreground don't daemonize (default)\n" #endif " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); exit(STATUS_FAILURE); } extern int main(int argc, char * argv[]) { int a; char const * logfile = NULL; char const * hexfile = NULL; int foreground = 0; unsigned short port = 0; char const * servname = NULL; unsigned short servport = 0; struct hostent * host; struct sockaddr_in servaddr; if (argc < 1 || !argv || !argv[0]) { fprintf(stderr, "bad arguments\n"); return STATUS_FAILURE; } for (a = 1; a < argc; a++) if (servname && isdigit((int)argv[a][0]) && a + 1 >= argc) { if (str_to_ushort(argv[a], &servport) < 0) { fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (!servname && argv[a][0] != '-' && a + 2 >= argc) servname = argv[a]; else if (strncmp(argv[a], "--hexdump=", 10) == 0) { if (hexfile) { fprintf(stderr, "%s: hexdump file was already specified as \"%s\"\n", argv[0], hexfile); usage(argv[0]); } hexfile = &argv[a][10]; } else if (strcmp(argv[a], "-d") == 0) { if (a + 1 >= argc) { fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (hexfile) { fprintf(stderr, "%s: hexdump file was already specified as \"%s\"\n", argv[0], hexfile); usage(argv[0]); } a++; hexfile = argv[a]; } else if (strncmp(argv[a], "--logfile=", 10) == 0) { if (logfile) { fprintf(stderr, "%s: eventlog file was already specified as \"%s\"\n", argv[0], logfile); usage(argv[0]); } logfile = &argv[a][10]; } else if (strcmp(argv[a], "-l") == 0) { if (a + 1 >= argc) { fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (logfile) { fprintf(stderr, "%s: eventlog file was already specified as \"%s\"\n", argv[0], logfile); usage(argv[0]); } a++; logfile = argv[a]; } else if (strncmp(argv[a], "--port=", 7) == 0) { if (port > 0) { fprintf(stderr, "%s: listen port was already specified as \"%hu\"\n", argv[0], port); usage(argv[0]); } if (str_to_ushort(&argv[a][7], &port) < 0) { fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (strcmp(argv[a], "-p") == 0) { if (a + 1 >= argc) { fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (port > 0) { fprintf(stderr, "%s: eventlog file was already specified as \"%hu\"\n", argv[0], port); usage(argv[0]); } a++; if (str_to_ushort(argv[a], &port) < 0) { fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (strcmp(argv[a], "-f") == 0 || strcmp(argv[a], "--foreground") == 0) foreground = 1; else if (strcmp(argv[a], "-h") == 0 || strcmp(argv[a], "--help") == 0 || strcmp(argv[a], "--usage") == 0) usage(argv[0]); else if (strcmp(argv[a], "-v") == 0 || strcmp(argv[a], "--version") == 0) { printf("version "PVPGN_VERSION"\n"); return STATUS_SUCCESS; } else if (strcmp(argv[a], "--hexdump") == 0 || strcmp(argv[a], "--logfile") == 0 || strcmp(argv[a], "--port") == 0) { fprintf(stderr, "%s: option \"%s\" requires and argument.\n", argv[0], argv[a]); usage(argv[0]); } else { fprintf(stderr, "%s: bad option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (port == 0) port = BNETD_SERV_PORT; if (servport == 0) servport = BNETD_SERV_PORT; if (!servname) servname = BNETD_DEFAULT_HOST; if (psock_init() < 0) { fprintf(stderr, "%s: could not initialize socket functions\n", argv[0]); return STATUS_FAILURE; } if (!(host = gethostbyname(servname))) { fprintf(stderr, "%s: unknown host \"%s\"\n", argv[0], servname); return STATUS_FAILURE; } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = PSOCK_AF_INET; servaddr.sin_port = htons(servport); memcpy(&servaddr.sin_addr.s_addr, host->h_addr_list[0], host->h_length); eventlog_set(stderr); /* errors to eventlog from here on... */ if (logfile) { eventlog_clear_level(); eventlog_add_level("error"); eventlog_add_level("warn"); eventlog_add_level("info"); eventlog_add_level("debug"); if (eventlog_open(logfile) < 0) { eventlog(eventlog_level_fatal, __FUNCTION__, "could not use file \"%s\" for the eventlog (exiting)", logfile); return STATUS_FAILURE; } } #ifdef DO_DAEMONIZE if (!foreground) { if (chdir("/") < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not change working directory to / (chdir: %s)", pstrerror(errno)); return STATUS_FAILURE; } switch (fork()) { case -1: eventlog(eventlog_level_error, __FUNCTION__, "could not fork (fork: %s)", pstrerror(errno)); return STATUS_FAILURE; case 0: /* child */ break; default: /* parent */ return STATUS_SUCCESS; } close(STDINFD); close(STDOUTFD); close(STDERRFD); # ifdef HAVE_SETPGID if (setpgid(0, 0) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgid: %s)", pstrerror(errno)); return STATUS_FAILURE; } # else # ifdef HAVE_SETPGRP # ifdef SETPGRP_VOID if (setpgrp() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgrp: %s)", pstrerror(errno)); return STATUS_FAILURE; } # else if (setpgrp(0, 0) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgrp: %s)", pstrerror(errno)); return STATUS_FAILURE; } # endif # else # ifdef HAVE_SETSID if (setsid() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setsid: %s)", pstrerror(errno)); return STATUS_FAILURE; } # else # error "One of setpgid(), setpgrp(), or setsid() is required" # endif # endif # endif } #endif if (hexfile) if (!(hexstrm = fopen(hexfile, "w"))) eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"%s\" for writing the hexdump (fopen: %s)", hexfile, pstrerror(errno)); if (proxy_process(port, servaddr) < 0) { eventlog(eventlog_level_fatal, __FUNCTION__, "failed to initialize network (exiting)"); return STATUS_FAILURE; } if (hexstrm) if (fclose(hexstrm) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close hexdump file \"%s\" after writing (fclose: %s)", hexfile, pstrerror(errno)); return STATUS_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/bnproxy/virtconn.c000066400000000000000000000171161462640461400205040ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define VIRTCONN_INTERNAL_ACCESS #include "common/setup_before.h" #ifdef HAVE_STDDEF_H # include #else # ifndef NULL # define NULL ((void *)0) # endif #endif #ifdef STDC_HEADERS # include #else # ifdef HAVE_MALLOC_H # include # endif #endif #ifdef HAVE_UNISTD_H # include #endif #include "compat/socket.h" #include "compat/psock.h" #include "common/queue.h" #include "common/list.h" #include "common/eventlog.h" #include "virtconn.h" #include "common/setup_after.h" static t_list * virtconn_head = NULL; extern t_virtconn * virtconn_create(int csd, int ssd, unsigned int udpaddr, unsigned short udpport) { t_virtconn * temp = NULL; if (csd < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad client socket"); return NULL; } if (ssd < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad server socket"); return NULL; } if (!(temp = malloc(sizeof(t_virtconn)))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for temp"); return NULL; } temp->csd = csd; temp->ssd = ssd; temp->udpport = udpport; temp->udpaddr = udpaddr; temp->class = virtconn_class_none; temp->state = virtconn_state_initial; temp->coutqueue = NULL; temp->coutsize = 0; temp->cinqueue = NULL; temp->cinsize = 0; temp->soutqueue = NULL; temp->soutsize = 0; temp->sinqueue = NULL; temp->sinsize = 0; temp->fileleft = 0; if (list_prepend_data(virtconn_head, temp) < 0) { free(temp); eventlog(eventlog_level_error, __FUNCTION__, "could not prepend temp"); return NULL; } return temp; } extern void virtconn_destroy(t_virtconn * vc) { t_elem * curr; if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return; } if (list_remove_data(virtconn_head, vc, &curr) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove item from list"); vc->state = virtconn_state_empty; psock_close(vc->ssd); psock_close(vc->csd); /* clear out the packet queues */ queue_clear(&vc->sinqueue); queue_clear(&vc->soutqueue); queue_clear(&vc->cinqueue); queue_clear(&vc->coutqueue); eventlog(eventlog_level_info, __FUNCTION__, "[%d] closed server connection (%d) class=%d", vc->ssd, vc->csd, (int)vc->class); eventlog(eventlog_level_info, __FUNCTION__, "[%d] closed client connection (%d) class=%d", vc->csd, vc->ssd, (int)vc->class); free(vc); } extern t_virtconn_class virtconn_get_class(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return virtconn_class_none; } return vc->class; } extern void virtconn_set_class(t_virtconn * vc, t_virtconn_class class) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return; } vc->class = class; } extern t_virtconn_state virtconn_get_state(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return virtconn_state_empty; } return vc->state; } extern void virtconn_set_state(t_virtconn * vc, t_virtconn_state state) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return; } vc->state = state; } extern unsigned int virtconn_get_udpaddr(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return 0; } return vc->udpaddr; } extern unsigned short virtconn_get_udpport(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return 0; } return vc->udpport; } extern t_queue * * virtconn_get_clientin_queue(t_virtconn * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return &vc->cinqueue; } extern int virtconn_get_clientin_size(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return vc->cinsize; } extern void virtconn_set_clientin_size(t_virtconn * vc, unsigned int size) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } vc->cinsize = size; } extern t_queue * * virtconn_get_clientout_queue(t_virtconn * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return &vc->coutqueue; } extern int virtconn_get_clientout_size(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return vc->coutsize; } extern void virtconn_set_clientout_size(t_virtconn * vc, unsigned int size) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } vc->coutsize = size; } extern int virtconn_get_client_socket(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return -1; } return vc->csd; } extern t_queue * * virtconn_get_serverin_queue(t_virtconn * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return &vc->sinqueue; } extern int virtconn_get_serverin_size(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return vc->sinsize; } extern void virtconn_set_serverin_size(t_virtconn * vc, unsigned int size) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } vc->sinsize = size; } extern t_queue * * virtconn_get_serverout_queue(t_virtconn * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return &vc->soutqueue; } extern int virtconn_get_serverout_size(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } return vc->soutsize; } extern void virtconn_set_serverout_size(t_virtconn * vc, unsigned int size) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return; } vc->soutsize = size; } extern int virtconn_get_server_socket(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return -1; } return vc->ssd; } extern void virtconn_set_fileleft(t_virtconn * vc, unsigned int size) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return; } vc->fileleft = size; } extern unsigned int virtconn_get_fileleft(t_virtconn const * vc) { if (!vc) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL virtconn"); return 0; } return vc->fileleft; } extern int virtconnlist_create(void) { if (!(virtconn_head = list_create())) return -1; return 0; } extern int virtconnlist_destroy(void) { if (list_destroy(virtconn_head) < 0) return -1; virtconn_head = NULL; return 0; } extern t_list * virtconnlist(void) { return virtconn_head; } pvpgn-1.99.7.2.1+dfsg/src/bnproxy/virtconn.h000066400000000000000000000105241462640461400205050ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_VIRTCONN_TYPES #define INCLUDED_VIRTCONN_TYPES typedef enum { virtconn_class_bnet, virtconn_class_file, virtconn_class_bot, virtconn_class_none } t_virtconn_class; typedef enum { virtconn_state_empty, virtconn_state_initial, virtconn_state_connected, virtconn_state_connecting } t_virtconn_state; #ifdef VIRTCONN_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES #include "common/queue.h" #else #define JUST_NEED_TYPES #include "common/queue.h" #undef JUST_NEED_TYPES #endif #endif typedef struct virtconn #ifdef VIRTCONN_INTERNAL_ACCESS { int csd; /* client side socket descriptor */ int ssd; /* server side socket descriptor */ t_virtconn_class class; /* normal, file, or bot */ t_virtconn_state state; /* initial, connected, etc */ unsigned short udpport; /* real port # to send UDP to */ unsigned int udpaddr; /* real IP # to send UDP to */ t_queue * coutqueue; /* client packets waiting to be sent */ unsigned int coutsize; /* client amount sent from the current output packet */ t_queue * cinqueue; /* client packet waiting to be processed */ unsigned int cinsize; /* client amount received into the current input packet */ t_queue * soutqueue; /* server packets waiting to be sent */ unsigned int soutsize; /* server amount sent from the current output packet */ t_queue * sinqueue; /* server packet waiting to be processed */ unsigned int sinsize; /* server amount received into the current input packet */ unsigned int fileleft; /* number of bytes in file download from server left */ } #endif t_virtconn; #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_VIRTCONN_PROTOS #define INCLUDED_VIRTCONN_PROTOS #define JUST_NEED_TYPES #include "common/packet.h" #include "common/queue.h" #include "common/list.h" #undef JUST_NEED_TYPES extern t_virtconn * virtconn_create(int csd, int ssd, unsigned int udpaddr, unsigned short udpport); extern void virtconn_destroy(t_virtconn * vc); extern t_virtconn_class virtconn_get_class(t_virtconn const * vc); extern void virtconn_set_class(t_virtconn * vc, t_virtconn_class class); extern t_virtconn_state virtconn_get_state(t_virtconn const * vc); extern void virtconn_set_state(t_virtconn * vc, t_virtconn_state state); extern unsigned int virtconn_get_udpaddr(t_virtconn const * vc); extern unsigned short virtconn_get_udpport(t_virtconn const * vc); extern t_queue * * virtconn_get_clientin_queue(t_virtconn * vc); extern int virtconn_get_clientin_size(t_virtconn const * vc); extern void virtconn_set_clientin_size(t_virtconn * vc, unsigned int size); extern t_queue * * virtconn_get_clientout_queue(t_virtconn * vc); extern int virtconn_get_clientout_size(t_virtconn const * vc); extern void virtconn_set_clientout_size(t_virtconn * vc, unsigned int size); extern int virtconn_get_client_socket(t_virtconn const * vc); extern t_queue * * virtconn_get_serverin_queue(t_virtconn * vc); extern int virtconn_get_serverin_size(t_virtconn const * vc); extern void virtconn_set_serverin_size(t_virtconn * vc, unsigned int size); extern t_queue * * virtconn_get_serverout_queue(t_virtconn * vc); extern int virtconn_get_serverout_size(t_virtconn const * vc); extern void virtconn_set_serverout_size(t_virtconn * vc, unsigned int size); extern int virtconn_get_server_socket(t_virtconn const * vc); extern void virtconn_set_fileleft(t_virtconn * vc, unsigned int size); extern unsigned int virtconn_get_fileleft(t_virtconn const * vc); extern int virtconnlist_create(void); extern int virtconnlist_destroy(void); extern t_list * virtconnlist(void); #endif #endif pvpgn-1.99.7.2.1+dfsg/src/bntrackd/000077500000000000000000000000001462640461400165575ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/bntrackd/CMakeLists.txt000066400000000000000000000002321462640461400213140ustar00rootroot00000000000000add_executable(bntrackd bntrackd.cpp) target_link_libraries(bntrackd common compat ${NETWORK_LIBRARIES}) install(TARGETS bntrackd DESTINATION ${SBINDIR}) pvpgn-1.99.7.2.1+dfsg/src/bntrackd/bntrackd.cpp000066400000000000000000000571131462640461400210620ustar00rootroot00000000000000/* * Copyright (C) 1999 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define TRACKER_INTERNAL_ACCESS #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include "compat/stdfileno.h" #include "compat/psock.h" #include "compat/pgetpid.h" #include "common/tracker.h" #include "common/eventlog.h" #include "common/list.h" #include "common/xalloc.h" #include "common/util.h" #include "common/version.h" #include "common/bn_type.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" using namespace pvpgn; namespace { /****************************************************************************** * TYPES *****************************************************************************/ typedef struct { struct in_addr address; std::time_t updated; t_trackpacket info; } t_server; typedef struct { int foreground; int debug; int XML_mode; unsigned int expire; unsigned int update; unsigned short port; char const * outfile; char const * pidfile; char const * logfile; char const * process; } t_prefs; /****************************************************************************** * STATIC FUNCTION PROTOTYPES *****************************************************************************/ int server_process(int sockfd); void usage(char const * progname); void getprefs(int argc, char * argv[]); void fixup_str(bn_byte * str, unsigned int size); /****************************************************************************** * GLOBAL VARIABLES *****************************************************************************/ t_prefs prefs; t_list * serverlist_head; } extern int main(int argc, char * argv[]) { int sockfd; int result; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } getprefs(argc, argv); if (!prefs.debug) eventlog_del_level("debug"); if (prefs.logfile) { eventlog_set(stderr); if (eventlog_open(prefs.logfile) < 0) { eventlog(eventlog_level_fatal, __FUNCTION__, "could not use file \"{}\" for the eventlog (exiting)", prefs.logfile); return EXIT_FAILURE; } } #ifdef DO_DAEMONIZE if (!prefs.foreground) { switch (fork()) { case -1: eventlog(eventlog_level_error, __FUNCTION__, "could not fork (fork: {})", std::strerror(errno)); return EXIT_FAILURE; case 0: /* child */ break; default: /* parent */ return EXIT_SUCCESS; } close(STDINFD); close(STDOUTFD); close(STDERRFD); # ifdef HAVE_SETPGID if (setpgid(0, 0) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgid: {})\n", std::strerror(errno)); return EXIT_FAILURE; } # else # ifdef HAVE_SETPGRP # ifdef SETPGRP_VOID if (setpgrp() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgrp: {})\n", std::strerror(errno)); return EXIT_FAILURE; } # else if (setpgrp(0, 0) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setpgrp: {})\n", std::strerror(errno)); return EXIT_FAILURE; } # endif # else # ifdef HAVE_SETSID if (setsid() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create new process group (setsid: {})\n", std::strerror(errno)); return EXIT_FAILURE; } # else # error "One of setpgid(), setpgrp(), or setsid() is required" # endif # endif # endif } #endif if (prefs.pidfile) { #ifdef HAVE_GETPID std::FILE * fp; if (!(fp = std::fopen(prefs.pidfile, "w"))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to open pid file \"{}\" for writing (std::fopen: {})", prefs.pidfile, std::strerror(errno)); prefs.pidfile = NULL; } else { std::fprintf(fp, "%u", (unsigned int)getpid()); if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close pid file \"{}\" after writing (std::fclose: {})", prefs.pidfile, std::strerror(errno)); } #else eventlog(eventlog_level_warn, __FUNCTION__, "no getpid() system call, do not use the -P or the --pidfile option"); prefs.pidfile = NULL; #endif } #ifdef HAVE_GETPID eventlog(eventlog_level_info, __FUNCTION__, "bntrackd version " PVPGN_VERSION " process {}", getpid()); #else eventlog(eventlog_level_info, __FUNCTION__, "bntrackd version " PVPGN_VERSION); #endif if (psock_init() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not initialize socket functions"); return EXIT_FAILURE; } /* create the socket */ if ((sockfd = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_DGRAM, PSOCK_IPPROTO_UDP)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not create UDP listen socket (psock_socket: {})\n", std::strerror(psock_errno())); return EXIT_FAILURE; } { struct sockaddr_in servaddr; /* bind the socket to correct port and interface */ std::memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = PSOCK_AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(prefs.port); if (psock_bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not bind to UDP port {} (psock_bind: {})\n", prefs.port, std::strerror(psock_errno())); return EXIT_FAILURE; } } if (!(serverlist_head = list_create())) { eventlog(eventlog_level_error, __FUNCTION__, "could not create server list"); return EXIT_FAILURE;; } result = server_process(sockfd); if (serverlist_head != NULL){ list_destroy(serverlist_head); } if (result < 0) return EXIT_FAILURE; return EXIT_SUCCESS; } namespace { int server_process(int sockfd) { t_elem * curr; t_server * server; struct sockaddr_in cliaddr; t_psock_fd_set rfds; struct timeval tv; std::time_t last; std::FILE * outfile; psock_t_socklen len; t_trackpacket packet; /* the main loop */ last = std::time(NULL) - prefs.update; for (;;) { /* time to dump our list to disk and call the process command */ /* (I'm making the assumption that this won't take very long.) */ if (last + (signed)prefs.update < std::time(NULL)) { last = std::time(NULL); if (!(outfile = std::fopen(prefs.outfile, "w"))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to open file \"{}\" for writing (std::fopen: {})", prefs.outfile, std::strerror(errno)); continue; } LIST_TRAVERSE(serverlist_head, curr) { server = (t_server*)elem_get_data(curr); if (server->updated + (signed)prefs.expire < last) { list_remove_elem(serverlist_head, &curr); xfree(server); } else { char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(server->address), addrstr, sizeof(addrstr)); if (prefs.XML_mode == 1) { std::fprintf(outfile, "\n\t
%s
\n", addrstr); std::fprintf(outfile, "\t%" PRIu16 "\n", bn_short_nget(server->info.port)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.server_location)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.software)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.version)); std::fprintf(outfile, "\t%" PRIu32 "\n", bn_int_nget(server->info.users)); std::fprintf(outfile, "\t%" PRIu32 "\n", bn_int_nget(server->info.channels)); std::fprintf(outfile, "\t%" PRIu32 "\n", bn_int_nget(server->info.games)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.server_desc)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.platform)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.server_url)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.contact_name)); std::fprintf(outfile, "\t%s\n", reinterpret_cast(server->info.contact_email)); std::fprintf(outfile, "\t%" PRIu32 "\n", bn_int_nget(server->info.uptime)); std::fprintf(outfile, "\t%" PRIu32 "\n", bn_int_nget(server->info.total_games)); std::fprintf(outfile, "\t%" PRIu32 "\n", bn_int_nget(server->info.total_logins)); std::fprintf(outfile, "
\n"); } else { std::fprintf(outfile, "%s\n##\n", addrstr); std::fprintf(outfile, "%" PRIu16 "\n##\n", bn_short_nget(server->info.port)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.server_location)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.software)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.version)); std::fprintf(outfile, "%" PRIu32 "\n##\n", bn_int_nget(server->info.users)); std::fprintf(outfile, "%" PRIu32 "\n##\n", bn_int_nget(server->info.channels)); std::fprintf(outfile, "%" PRIu32 "\n##\n", bn_int_nget(server->info.games)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.server_desc)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.platform)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.server_url)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.contact_name)); std::fprintf(outfile, "%s\n##\n", reinterpret_cast(server->info.contact_email)); std::fprintf(outfile, "%" PRIu32 "\n##\n", bn_int_nget(server->info.uptime)); std::fprintf(outfile, "%" PRIu32 "\n##\n", bn_int_nget(server->info.total_games)); std::fprintf(outfile, "%" PRIu32 "\n##\n", bn_int_nget(server->info.total_logins)); std::fprintf(outfile, "###\n"); } } } if (std::fclose(outfile) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close output file \"{}\" after writing (std::fclose: {})", prefs.outfile, std::strerror(errno)); if (prefs.process[0] != '\0') std::system(prefs.process); } /* select socket to operate on */ PSOCK_FD_ZERO(&rfds); PSOCK_FD_SET(sockfd, &rfds); tv.tv_sec = BNTRACKD_GRANULARITY; tv.tv_usec = 0; switch (psock_select(sockfd + 1, &rfds, NULL, NULL, &tv)) { case -1: /* error */ if ( #ifdef PSOCK_EINTR errno != PSOCK_EINTR && #endif 1) eventlog(eventlog_level_error, __FUNCTION__, "select failed (select: {})", std::strerror(errno)); case 0: /* timeout and no sockets ready */ continue; } /* New tracking packet */ if (PSOCK_FD_ISSET(sockfd, &rfds)) { len = sizeof(cliaddr); if (psock_recvfrom(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&cliaddr, &len) >= 0) { if (bn_short_nget(packet.packet_version) >= TRACK_VERSION) { bn_byte_set(&packet.software[sizeof(packet.software) - 1], '\0'); fixup_str(packet.software, sizeof(packet.software)); bn_byte_set(&packet.version[sizeof(packet.version) - 1], '\0'); fixup_str(packet.version, sizeof(packet.version)); bn_byte_set(&packet.platform[sizeof(packet.platform) - 1], '\0'); fixup_str(packet.platform, sizeof(packet.platform)); bn_byte_set(&packet.server_desc[sizeof(packet.server_desc) - 1], '\0'); fixup_str(packet.server_desc, sizeof(packet.server_desc)); bn_byte_set(&packet.server_location[sizeof(packet.server_location) - 1], '\0'); fixup_str(packet.server_location, sizeof(packet.server_location)); bn_byte_set(&packet.server_url[sizeof(packet.server_url) - 1], '\0'); fixup_str(packet.server_url, sizeof(packet.server_url)); bn_byte_set(&packet.contact_name[sizeof(packet.contact_name) - 1], '\0'); fixup_str(packet.contact_name, sizeof(packet.contact_name)); bn_byte_set(&packet.contact_email[sizeof(packet.contact_email) - 1], '\0'); fixup_str(packet.contact_email, sizeof(packet.contact_email)); /* Find this server's slot */ LIST_TRAVERSE(serverlist_head, curr) { server = (t_server*)elem_get_data(curr); if (!std::memcmp(&server->address, &cliaddr.sin_addr, sizeof(struct in_addr))) { if (bn_int_nget(packet.flags)&TF_SHUTDOWN) { list_remove_elem(serverlist_head, &curr); xfree(server); } else { /* update in place */ server->info = packet; server->updated = std::time(NULL); } break; } } /* Not found? Make a new slot */ if (!(bn_int_nget(packet.flags)&TF_SHUTDOWN) && !curr) { server = (t_server*)xmalloc(sizeof(t_server)); server->address = cliaddr.sin_addr; server->info = packet; server->updated = std::time(NULL); list_append_data(serverlist_head, server); } char addrstr2[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(cliaddr.sin_addr), addrstr2, sizeof(addrstr2)); eventlog(eventlog_level_debug, __FUNCTION__, "Packet received from {}:" " packet_version={}" " flags=0x{:08}" " port={}" " software=\"{}\"" " version=\"{}\"" " platform=\"{}\"" " server_desc=\"{}\"" " server_location=\"{}\"" " server_url=\"{}\"" " contact_name=\"{}\"" " contact_email=\"{}\"" " uptime={}" " total_games={}" " total_logins={}", addrstr2, bn_short_nget(packet.packet_version), bn_int_nget(packet.flags), bn_short_nget(packet.port), reinterpret_cast(packet.software), reinterpret_cast(packet.version), reinterpret_cast(packet.platform), reinterpret_cast(packet.server_desc), reinterpret_cast(packet.server_location), reinterpret_cast(packet.server_url), reinterpret_cast(packet.contact_name), reinterpret_cast(packet.contact_email), bn_int_nget(packet.uptime), bn_int_nget(packet.total_games), bn_int_nget(packet.total_logins)); } } } } } void usage(char const * progname) { std::fprintf(stderr, "usage: %s []\n", progname); std::fprintf(stderr, " -c COMMAND, --command=COMMAND execute COMMAND update\n" " -d, --debug turn on debug mode\n" " -e SECS, --expire SECS forget a list entry after SEC seconds\n" #ifdef DO_DAEMONIZE " -f, --foreground don't daemonize\n" #else " -f, --foreground don't daemonize (default)\n" #endif " -l FILE, --logfile=FILE write event messages to FILE\n" " -o FILE, --outfile=FILE write server list to FILE\n"); std::fprintf(stderr, " -p PORT, --port=PORT listen for announcments on UDP port PORT\n" " -P FILE, --pidfile=FILE write pid to FILE\n" " -u SECS, --update SECS write output file every SEC seconds\n" " -x, --XML write output file in XML format\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n"); std::exit(EXIT_FAILURE); } void getprefs(int argc, char * argv[]) { int a; prefs.foreground = 0; prefs.debug = 0; prefs.expire = 0; prefs.update = 0; prefs.port = 0; prefs.XML_mode = 0; prefs.outfile = NULL; prefs.pidfile = NULL; prefs.process = NULL; prefs.logfile = NULL; for (a = 1; a < argc; a++) if (std::strncmp(argv[a], "--command=", 10) == 0) { if (prefs.process) { std::fprintf(stderr, "%s: processing command was already specified as \"%s\"\n", argv[0], prefs.process); usage(argv[0]); } prefs.process = &argv[a][10]; } else if (std::strcmp(argv[a], "-c") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (prefs.process) { std::fprintf(stderr, "%s: processing command was already specified as \"%s\"\n", argv[0], prefs.process); usage(argv[0]); } a++; prefs.process = argv[a]; } else if (std::strcmp(argv[a], "-d") == 0 || std::strcmp(argv[a], "--debug") == 0) prefs.debug = 1; else if (std::strncmp(argv[a], "--expire=", 9) == 0) { if (prefs.expire) { std::fprintf(stderr, "%s: expiration period was already specified as \"%u\"\n", argv[0], prefs.expire); usage(argv[0]); } if (str_to_uint(&argv[a][9], &prefs.expire) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], &argv[a][9]); usage(argv[0]); } } else if (std::strcmp(argv[a], "-e") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (prefs.expire) { std::fprintf(stderr, "%s: expiration period was already specified as \"%u\"\n", argv[0], prefs.expire); usage(argv[0]); } a++; if (str_to_uint(argv[a], &prefs.expire) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (std::strcmp(argv[a], "-f") == 0 || std::strcmp(argv[a], "--foreground") == 0) prefs.foreground = 1; else if (std::strcmp(argv[a], "-x") == 0 || std::strcmp(argv[a], "--XML") == 0) prefs.XML_mode = 1; else if (std::strncmp(argv[a], "--logfile=", 10) == 0) { if (prefs.logfile) { std::fprintf(stderr, "%s: eventlog file was already specified as \"%s\"\n", argv[0], prefs.logfile); usage(argv[0]); } prefs.logfile = &argv[a][10]; } else if (std::strcmp(argv[a], "-l") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (prefs.logfile) { std::fprintf(stderr, "%s: eventlog file was already specified as \"%s\"\n", argv[0], prefs.logfile); usage(argv[0]); } a++; prefs.logfile = argv[a]; } else if (std::strncmp(argv[a], "--outfile=", 10) == 0) { if (prefs.outfile) { std::fprintf(stderr, "%s: output file was already specified as \"%s\"\n", argv[0], prefs.outfile); usage(argv[0]); } prefs.outfile = &argv[a][10]; } else if (std::strcmp(argv[a], "-o") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (prefs.outfile) { std::fprintf(stderr, "%s: output file was already specified as \"%s\"\n", argv[0], prefs.outfile); usage(argv[0]); } a++; prefs.outfile = argv[a]; } else if (std::strncmp(argv[a], "--pidfile=", 10) == 0) { if (prefs.pidfile) { std::fprintf(stderr, "%s: pid file was already specified as \"%s\"\n", argv[0], prefs.pidfile); usage(argv[0]); } prefs.pidfile = &argv[a][10]; } else if (std::strncmp(argv[a], "--port=", 7) == 0) { if (prefs.port) { std::fprintf(stderr, "%s: port number was already specified as \"%hu\"\n", argv[0], prefs.port); usage(argv[0]); } if (str_to_ushort(&argv[a][7], &prefs.port) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], &argv[a][7]); usage(argv[0]); } } else if (std::strcmp(argv[a], "-p") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (prefs.port) { std::fprintf(stderr, "%s: port number was already specified as \"%hu\"\n", argv[0], prefs.port); usage(argv[0]); } a++; if (str_to_ushort(argv[a], &prefs.port) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (std::strcmp(argv[a], "-P") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (prefs.pidfile) { std::fprintf(stderr, "%s: pid file was already specified as \"%s\"\n", argv[0], prefs.pidfile); usage(argv[0]); } a++; prefs.pidfile = argv[a]; } else if (std::strncmp(argv[a], "--update=", 9) == 0) { if (prefs.update) { std::fprintf(stderr, "%s: update period was already specified as \"%u\"\n", argv[0], prefs.expire); usage(argv[0]); } if (str_to_uint(&argv[a][9], &prefs.update) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], &argv[a][9]); usage(argv[0]); } } else if (std::strcmp(argv[a], "-u") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (prefs.update) { std::fprintf(stderr, "%s: update period was already specified as \"%u\"\n", argv[0], prefs.expire); usage(argv[0]); } a++; if (str_to_uint(argv[a], &prefs.update) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); std::exit(0); } else if (std::strcmp(argv[a], "--command") == 0 || std::strcmp(argv[a], "--expire") == 0 || std::strcmp(argv[a], "--logfile") == 0 || std::strcmp(argv[a], "--outfile") == 0 || std::strcmp(argv[a], "--port") == 0 || std::strcmp(argv[a], "--pidfile") == 0 || std::strcmp(argv[a], "--update") == 0) { std::fprintf(stderr, "%s: option \"%s\" requires and argument.\n", argv[0], argv[a]); usage(argv[0]); } else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (!prefs.process) prefs.process = BNTRACKD_PROCESS; if (prefs.expire == 0) prefs.expire = BNTRACKD_EXPIRE; if (!prefs.logfile) prefs.logfile = BNTRACKD_LOGFILE; if (!prefs.outfile) prefs.outfile = BNTRACKD_OUTFILE; if (prefs.port == 0) prefs.port = BNTRACKD_SERVER_PORT; if (!prefs.pidfile) prefs.pidfile = BNTRACKD_PIDFILE; if (prefs.expire == 0) prefs.update = BNTRACKD_UPDATE; if (prefs.logfile[0] == '\0') prefs.logfile = NULL; if (prefs.pidfile[0] == '\0') prefs.pidfile = NULL; } void fixup_str(bn_byte * str, unsigned int size) { bn_byte prev; unsigned int i; bn_byte_set(&prev, '\0'); for (i = 0; i < size, bn_byte_get(str[i]) != '\0'; bn_byte_set(&prev, bn_byte_get(str[i])), i++) if (bn_byte_get(prev) == '#' && bn_byte_get(str[i]) == '#') bn_byte_set(&str[i], '%'); } } pvpgn-1.99.7.2.1+dfsg/src/bntrackd/servers.xsl000066400000000000000000000035661462640461400210120ustar00rootroot00000000000000 PvPGN XML Server List

Download(bntrackd.c,XSL,PHP)servers.xsl

AddressLocationDescriptionURLUptimeContactSoftwareUsersGamesChannels
:     
pvpgn-1.99.7.2.1+dfsg/src/client/000077500000000000000000000000001462640461400162455ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/client/CMakeLists.txt000066400000000000000000000012371462640461400210100ustar00rootroot00000000000000add_executable(bnchat bnchat.cpp client.cpp client.h client_connect.cpp client_connect.h udptest.cpp udptest.h ansi_term.h) target_link_libraries(bnchat common compat ${NETWORK_LIBRARIES}) add_executable(bnftp bnftp.cpp client.cpp client.h) target_link_libraries(bnftp common compat ${NETWORK_LIBRARIES}) add_executable(bnbot bnbot.cpp client.cpp client.h) target_link_libraries(bnbot common compat ${NETWORK_LIBRARIES}) add_executable(bnstat bnstat.cpp client.cpp client.h client_connect.cpp client_connect.h udptest.cpp udptest.h) target_link_libraries(bnstat common compat ${NETWORK_LIBRARIES}) install(TARGETS bnchat bnftp bnbot bnstat DESTINATION ${BINDIR}) pvpgn-1.99.7.2.1+dfsg/src/client/ansi_term.h000066400000000000000000000043551462640461400204060ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ANSI_TERM_PROTOS #define INCLUDED_ANSI_TERM_PROTOS #include #define ansi_beep()\ std::printf("\007"); #define ansi_screen_clear()\ std::printf("\033[2J"); #define ansi_line_clear()\ std::printf("\033[K"); #define ansi_cursor_move_up(lines)\ std::printf("\033[%dA", lines); #define ansi_cursor_move_down(lines)\ std::printf("\033[%dB", lines); #define ansi_cursor_move_left(chars)\ std::printf("\033[%dD", chars); #define ansi_cursor_move_right(chars)\ std::printf("\033[%dC", chars); #define ansi_cursor_move_home()\ std::printf("\033[1;0H"); #define ansi_cursor_move(y,x)\ std::printf("\033[%d;%dH", x + 1, y); #define ansi_cursor_save()\ std::printf("\033[u"); #define ansi_cursor_load()\ std::printf("\033[s"); #define ansi_text_reset()\ std::printf("\033[0m"); #define ansi_text_style(style)\ std::printf("\033[%dm", style); #define ansi_text_color_fore(color)\ std::printf("\033[3%dm", color); #define ansi_text_color_back(color)\ std::printf("\033[4%dm", color); #define ansi_text_style_bold 1 #define ansi_text_style_underline 4 #define ansi_text_style_blink 5 #define ansi_text_style_inverse 7 #define ansi_text_color_black 0 #define ansi_text_color_red 1 #define ansi_text_color_green 2 #define ansi_text_color_yellow 3 #define ansi_text_color_blue 4 #define ansi_text_color_magenta 5 #define ansi_text_color_cyan 6 #define ansi_text_color_white 7 #endif #endif pvpgn-1.99.7.2.1+dfsg/src/client/bnbot.cpp000066400000000000000000000204161462640461400200600ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #ifdef HAVE_TERMIOS_H # include #endif #include "compat/termios.h" #include "compat/psock.h" #include "common/packet.h" #include "common/field_sizes.h" #include "common/util.h" #include "common/init_protocol.h" #include "common/bn_type.h" #include "common/network.h" #ifdef CLIENTDEBUG # include "common/eventlog.h" #endif #include "client.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" using namespace pvpgn; using namespace pvpgn::client; namespace { void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [ []]\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n", progname); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { int a; int sd; struct sockaddr_in saddr; t_packet * packet; t_packet * rpacket; char const * servname = NULL; unsigned short servport = 0; char text[MAX_MESSAGE_LEN]; struct hostent * host; unsigned int commpos; struct termios in_attr_old; struct termios in_attr_new; int changed_in; unsigned int currsize; int fd_stdin; unsigned int screen_width, screen_height; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (servname && std::isdigit((int)argv[a][0]) && a + 1 >= argc) { if (str_to_ushort(argv[a], &servport) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (!servname && argv[a][0] != '-' && a + 2 >= argc) servname = argv[a]; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (servport == 0) servport = BNETD_SERV_PORT; if (!servname) servname = BNETD_DEFAULT_HOST; if (psock_init() < 0) { std::fprintf(stderr, "%s: could not inialialize socket functions\n", argv[0]); return EXIT_FAILURE; } if (!(host = gethostbyname(servname))) { std::fprintf(stderr, "%s: unknown host \"%s\"\n", argv[0], servname); return EXIT_FAILURE; } fd_stdin = fileno(stdin); if (tcgetattr(fd_stdin, &in_attr_old) >= 0) { in_attr_new = in_attr_old; in_attr_new.c_lflag &= ~(ECHO | ICANON); /* turn off ECHO and ICANON */ in_attr_new.c_cc[VMIN] = 0; /* don't require reads to return data */ in_attr_new.c_cc[VTIME] = 1; /* timeout = .1 second */ tcsetattr(fd_stdin, TCSANOW, &in_attr_new); changed_in = 1; } else { std::fprintf(stderr, "%s: could not set terminal attributes for stdin\n", argv[0]); changed_in = 0; } if (client_get_termsize(fd_stdin, &screen_width, &screen_height) < 0) { std::fprintf(stderr, "%s: could not determine screen size\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } if ((sd = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP)) < 0) { std::fprintf(stderr, "%s: could not create socket (psock_socket: %s)\n", argv[0], std::strerror(psock_errno())); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } std::memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = PSOCK_AF_INET; saddr.sin_port = htons(servport); std::memcpy(&saddr.sin_addr.s_addr, host->h_addr_list[0], host->h_length); if (psock_connect(sd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { std::fprintf(stderr, "%s: could not connect to server \"%s\" port %hu (psock_connect: %s)\n", argv[0], servname, servport, std::strerror(psock_errno())); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } if (psock_ctl(sd, PSOCK_NONBLOCK) < 0) { std::fprintf(stderr, "%s: could not set TCP socket to non-blocking mode (psock_ctl: %s)\n", argv[0], std::strerror(psock_errno())); psock_close(sd); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(saddr.sin_addr), addrstr, sizeof(addrstr)); std::printf("Connected to %s:%hu.\n", addrstr, servport); #ifdef CLIENTDEBUG eventlog_set(stderr); #endif if (!(packet = packet_create(packet_class_init))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } bn_byte_set(&packet->u.client_initconn.cclass, CLIENT_INITCONN_CLASS_BOT); client_blocksend_packet(sd, packet); packet_del_ref(packet); if (!(rpacket = packet_create(packet_class_raw))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } if (!(packet = packet_create(packet_class_raw))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } packet_append_ntstring(packet, "\004"); client_blocksend_packet(sd, packet); packet_del_ref(packet); { int highest_fd; t_psock_fd_set rfds; PSOCK_FD_ZERO(&rfds); highest_fd = fd_stdin; if (sd > highest_fd) highest_fd = sd; commpos = 0; text[0] = '\0'; for (;;) { PSOCK_FD_SET(fd_stdin, &rfds); PSOCK_FD_SET(sd, &rfds); if (psock_select(highest_fd + 1, &rfds, NULL, NULL, NULL) < 0) { if (errno != PSOCK_EINTR) std::fprintf(stderr, "%s: select failed (select: %s)\n", argv[0], std::strerror(errno)); continue; } if (PSOCK_FD_ISSET(sd, &rfds)) /* got network data */ { packet_set_size(rpacket, MAX_PACKET_SIZE - 1); currsize = 0; if (net_recv_packet(sd, rpacket, &currsize)<0) { psock_close(sd); sd = -1; } if (currsize>0) { char * str = (char*)packet_get_raw_data(rpacket, 0); str[currsize] = '\0'; std::printf("%s", str); std::fflush(stdout); } if (sd == -1) /* if connection was closed */ { std::printf("Connection closed by server.\n"); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); packet_del_ref(rpacket); return EXIT_SUCCESS; } } if (PSOCK_FD_ISSET(fd_stdin, &rfds)) /* got keyboard data */ { switch (client_get_comm("", text, sizeof(text), &commpos, -1, 0, screen_width)) { case -1: /* cancel */ if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; case 0: /* timeout */ break; case 1: if (!(packet = packet_create(packet_class_raw))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); packet_del_ref(rpacket); return EXIT_FAILURE; } packet_append_ntstring(packet, text); packet_append_ntstring(packet, "\r\n"); client_blocksend_packet(sd, packet); packet_del_ref(packet); commpos = 0; text[0] = '\0'; } } } } /* not reached */ } pvpgn-1.99.7.2.1+dfsg/src/client/bnchat.cpp000066400000000000000000002013451462640461400202150ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Oleg Drokin (green@ccssu.ccssu.crimea.ua) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #ifdef WIN32 # include #endif #ifdef HAVE_TERMIOS_H # include #endif #ifdef HAVE_SIGACTION # include #endif #include "compat/psock.h" #include "compat/termios.h" #include "compat/strcasecmp.h" #include "common/field_sizes.h" #include "common/bnet_protocol.h" #include "common/packet.h" #include "common/tag.h" #include "common/file_protocol.h" #include "common/bn_type.h" #include "common/util.h" #include "common/init_protocol.h" #include "common/bnethash.h" #include "common/bnethashconv.h" #include "common/xalloc.h" #include "common/network.h" #include "common/hexdump.h" #include "common/version.h" #include "common/xstring.h" #ifdef CLIENTDEBUG # include "common/eventlog.h" #endif #include "ansi_term.h" #include "client_connect.h" #include "client.h" #include "common/setup_after.h" #ifdef CLIENTDEBUG # define dprintf printf #else # define dprintf if (0) printf #endif #define CHANNEL_BNCHATBOT "Chat" #define CHANNEL_STARCRAFT "Starcraft" #define CHANNEL_BROODWARS "Brood War" #define CHANNEL_SHAREWARE "Starcraft Shareware" #define CHANNEL_DIABLORTL "Diablo Retail" #define CHANNEL_DIABLOSHR "Diablo Shareware" /* FIXME: is this one right? */ #define CHANNEL_WARCIIBNE "War2BNE" #define CHANNEL_DIABLO2DV "Diablo II" #define CHANNEL_DIABLO2XP "Diablo II" #define CHANNEL_WARCRAFT3 "W3" #define CHANNEL_WAR3XP "W3" using namespace pvpgn; using namespace pvpgn::client; namespace { volatile int handle_winch = 0; typedef enum { mode_chat, mode_command, mode_waitstat, mode_waitgames, mode_waitladder, mode_gamename, mode_gamepass, mode_gamewait, mode_gamestop, mode_claninvite, mode_clancreateinvite } t_mode; typedef struct _client_state { int useansi; int sd; struct sockaddr_in saddr; unsigned int sessionkey; unsigned int sessionnum; unsigned int currsize; unsigned int commpos; struct termios in_attr_old; struct termios in_attr_new; int changed_in; int fd_stdin; unsigned int screen_width, screen_height; int munged; t_mode mode; char text[MAX_MESSAGE_LEN]; } t_client_state; typedef struct _user_info { char const * clienttag; char const * archtag; char const * gamelang; char player[MAX_MESSAGE_LEN]; char const * cdowner; char const * cdkey; char const * channel; char curr_gamename[MAX_GAMENAME_LEN]; char curr_gamepass[MAX_GAMEPASS_LEN]; int count, clantag; char const * inviter; int ignoreversion; } t_user_info; char const * mflags_get_str(unsigned int flags); char const * cflags_get_str(unsigned int flags); char const * mode_get_prompt(t_mode mode); int print_file(struct sockaddr_in * saddr, char const * filename, char const * progname, char const * clienttag); void usage(char const * progname); #ifdef HAVE_SIGACTION void winch_sig_handle(int unused); #endif #ifdef HAVE_SIGACTION void winch_sig_handle(int unused) { handle_winch = 1; } #endif char const * mflags_get_str(unsigned int flags) { static char buffer[32]; buffer[0] = buffer[1] = '\0'; if (flags&MF_BLIZZARD) std::strcat(buffer, ",Blizzard"); if (flags&MF_GAVEL) std::strcat(buffer, ",Gavel"); if (flags&MF_VOICE) std::strcat(buffer, ",Megaphone"); if (flags&MF_BNET) std::strcat(buffer, ",BNET"); if (flags&MF_PLUG) std::strcat(buffer, ",Plug"); if (flags&MF_X) std::strcat(buffer, ",X"); if (flags&MF_SHADES) std::strcat(buffer, ",Shades"); if (flags&MF_PGLPLAY) std::strcat(buffer, ",PGL_Player"); if (flags&MF_PGLOFFL) std::strcat(buffer, ",PGL_Official"); buffer[0] = '['; std::strcat(buffer, "]"); return buffer; } char const * cflags_get_str(unsigned int flags) { static char buffer[32]; buffer[0] = buffer[1] = '\0'; if (flags&CF_PUBLIC) std::strcat(buffer, ",Public"); if (flags&CF_MODERATED) std::strcat(buffer, ",Moderated"); if (flags&CF_RESTRICTED) std::strcat(buffer, ",Restricted"); if (flags&CF_THEVOID) std::strcat(buffer, ",The Void"); if (flags&CF_SYSTEM) std::strcat(buffer, ",System"); if (flags&CF_OFFICIAL) std::strcat(buffer, ",Official"); buffer[0] = '['; std::strcat(buffer, "]"); return buffer; } char const * mode_get_prompt(t_mode mode) { switch (mode) { case mode_chat: return "] "; case mode_command: return "command> "; case mode_waitstat: case mode_waitgames: case mode_waitladder: case mode_gamewait: return "*please wait* "; case mode_gamename: return "Name: "; case mode_gamepass: return "Password: "; case mode_gamestop: return "[Return to kill game] "; default: return "? "; } } int print_file(struct sockaddr_in * saddr, char const * filename, char const * progname, char const * clienttag) { int sd; t_packet * ipacket; t_packet * packet; t_packet * rpacket; t_packet * fpacket; unsigned int currsize; unsigned int filelen; if (!saddr || !filename || !progname) return -1; if ((sd = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP)) < 0) { std::fprintf(stderr, "%s: could not create socket (psock_socket: %s)\n", progname, std::strerror(psock_errno())); return -1; } if (psock_connect(sd, (struct sockaddr *)saddr, sizeof(*saddr)) < 0) { std::fprintf(stderr, "%s: could not connect to server (psock_connect: %s)\n", progname, std::strerror(psock_errno())); return -1; } if (!(ipacket = packet_create(packet_class_init))) { std::fprintf(stderr, "%s: could not create packet\n", progname); return -1; } bn_byte_set(&ipacket->u.client_initconn.cclass, CLIENT_INITCONN_CLASS_FILE); client_blocksend_packet(sd, ipacket); packet_del_ref(ipacket); if (!(rpacket = packet_create(packet_class_file))) { std::fprintf(stderr, "%s: could not create packet\n", progname); return -1; } if (!(fpacket = packet_create(packet_class_raw))) { std::fprintf(stderr, "%s: could not create packet\n", progname); return -1; } if (!(packet = packet_create(packet_class_file))) { std::fprintf(stderr, "%s: could not create packet\n", progname); return -1; } packet_set_size(packet, sizeof(t_client_file_req)); packet_set_type(packet, CLIENT_FILE_REQ); bn_int_tag_set(&packet->u.client_file_req.archtag, ARCHTAG_WINX86); bn_int_tag_set(&packet->u.client_file_req.clienttag, clienttag); bn_int_set(&packet->u.client_file_req.adid, 0); bn_int_set(&packet->u.client_file_req.extensiontag, 0); bn_int_set(&packet->u.client_file_req.startoffset, 0); bn_long_set_a_b(&packet->u.client_file_req.timestamp, 0x00000000, 0x00000000); packet_append_string(packet, filename); client_blocksend_packet(sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed file connection\n", progname); packet_del_ref(fpacket); packet_del_ref(rpacket); return -1; } while (packet_get_type(rpacket) != SERVER_FILE_REPLY); filelen = bn_int_get(rpacket->u.server_file_reply.filelen); packet_del_ref(rpacket); for (currsize = 0; currsize + MAX_PACKET_SIZE <= filelen; currsize += MAX_PACKET_SIZE) { if (client_blockrecv_raw_packet(sd, fpacket, MAX_PACKET_SIZE) < 0) { std::fflush(stdout); std::fprintf(stderr, "%s: server closed file connection\n", progname); packet_del_ref(fpacket); return -1; } str_print_term(stdout, (const char*)packet_get_raw_data_const(fpacket, 0), MAX_PACKET_SIZE, 1); } filelen -= currsize; if (filelen) { if (client_blockrecv_raw_packet(sd, fpacket, filelen) < 0) { std::fflush(stdout); std::fprintf(stderr, "%s: server closed file connection\n", progname); packet_del_ref(fpacket); return -1; } str_print_term(stdout, (const char*)packet_get_raw_data_const(fpacket, 0), filelen, 1); } std::fflush(stdout); psock_close(sd); packet_del_ref(fpacket); return 0; } void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [ []]\n", progname); std::fprintf(stderr, " -a, --ansi-color use ANSI colors\n" " -n, --new-account create a new account\n" " -c, --change-password change account password\n" " --client=CHAT report client as a chat bot\n" " -b, --client=SEXP report client as Brood Wars\n" " -d, --client=DRTL report client as Diablo Retail\n" " --client=DSHR report client as Diablo Shareware\n"); std::fprintf(stderr, " -s, --client=STAR report client as Starcraft (default)\n" " --client=SSHR report client as Starcraft Shareware\n" " -w, --client=W2BN report client as Warcraft II BNE\n" " --client=D2DV report client as Diablo II\n" " --client=D2XP report client as Diablo II: LoD\n" " --client=WAR3 report client as Warcraft III\n" " --client=W3XP report client as Warcraft III Frozen Throne\n" " --arch=IX86 report architecture as Intel x86 (default)\n" " --arch=PMAC report architecture as PowerPC MacOS\n" " --arch=XMAC report architecture as PowerPC MacOSX\n"); std::fprintf(stderr, " -o NAME, --owner=NAME report CD owner as NAME\n" " -k KEY, --cdkey=KEY report CD key as KEY\n" " -l LANG --lang=LANG report language as LANG (default \"enUS\")\n" " -i, --ignore-version ignore version request (do not send game version, CD owner/key)\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n"); std::exit(EXIT_FAILURE); } int read_commandline(int argc, char * * argv, char const * * servname, unsigned short * servport, char const * * clienttag, char const * * archtag, int * changepass, int * newacct, char const * * channel, char const * * cdowner, char const * * cdkey, char const * * gamelang, int * ignoreversion, int * useansi) { int a; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (*servname && std::isdigit((int)argv[a][0]) && a + 1 >= argc) { if (str_to_ushort(argv[a], servport) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (!(*servname) && argv[a][0] != '-' && a + 2 >= argc) *servname = argv[a]; else if (std::strcmp(argv[a], "-a") == 0 || std::strcmp(argv[a], "--use-ansi") == 0) *useansi = 1; else if (std::strcmp(argv[a], "-n") == 0 || std::strcmp(argv[a], "--new-account") == 0) { if (*changepass) { std::fprintf(stderr, "%s: can not create new account when changing passwords\n", argv[0]); usage(argv[0]); } *newacct = 1; } else if (std::strcmp(argv[a], "-c") == 0 || std::strcmp(argv[a], "--change-password") == 0) { if (*newacct) { std::fprintf(stderr, "%s: can not change passwords when creating a new account\n", argv[0]); usage(argv[0]); } *changepass = 1; } else if (std::strcmp(argv[a], "--client=CHAT") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_BNCHATBOT; *channel = CHANNEL_BNCHATBOT; } else if (std::strcmp(argv[a], "-b") == 0 || std::strcmp(argv[a], "--client=SEXP") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_BROODWARS; *channel = CHANNEL_BROODWARS; } else if (std::strcmp(argv[a], "-d") == 0 || std::strcmp(argv[a], "--client=DRTL") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_DIABLORTL; *channel = CHANNEL_DIABLORTL; } else if (std::strcmp(argv[a], "--client=DSHR") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_DIABLOSHR; *channel = CHANNEL_DIABLOSHR; } else if (std::strcmp(argv[a], "-s") == 0 || std::strcmp(argv[a], "--client=STAR") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_STARCRAFT; *channel = CHANNEL_STARCRAFT; } else if (std::strcmp(argv[a], "--client=SSHR") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_SHAREWARE; *channel = CHANNEL_SHAREWARE; } else if (std::strcmp(argv[a], "-w") == 0 || std::strcmp(argv[a], "--client=W2BN") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_WARCIIBNE; *channel = CHANNEL_WARCIIBNE; } else if (std::strcmp(argv[a], "--client=D2DV") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_DIABLO2DV; *channel = CHANNEL_DIABLO2DV; } else if (std::strcmp(argv[a], "--client=D2XP") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_DIABLO2XP; *channel = CHANNEL_DIABLO2XP; } else if (std::strcmp(argv[a], "--client=WAR3") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_WARCRAFT3; *channel = CHANNEL_WARCRAFT3; } else if (std::strcmp(argv[a], "--client=W3XP") == 0) { if (*clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], *clienttag); usage(argv[0]); } *clienttag = CLIENTTAG_WAR3XP; *channel = CHANNEL_WAR3XP; } else if (std::strncmp(argv[a], "--client=", 9) == 0) { std::fprintf(stderr, "%s: unknown client tag \"%s\"\n", argv[0], &argv[a][9]); usage(argv[0]); } else if (std::strcmp(argv[a], "--arch=IX86") == 0) { *archtag = ARCHTAG_WINX86; } else if (std::strcmp(argv[a], "--arch=PMAC") == 0) { *archtag = ARCHTAG_MACPPC; } else if (std::strcmp(argv[a], "--arch=XMAC") == 0) { *archtag = ARCHTAG_OSXPPC; } else if (std::strncmp(argv[a], "--arch=", 7) == 0) { std::fprintf(stderr, "%s: unknown architecture tag \"%s\"\n", argv[0], &argv[a][7]); usage(argv[0]); } else if (std::strcmp(argv[a], "-o") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (*cdowner) { std::fprintf(stderr, "%s: CD owner was already specified as \"%s\"\n", argv[0], *cdowner); usage(argv[0]); } *cdowner = argv[++a]; } else if (std::strncmp(argv[a], "--owner=", 8) == 0) { if (*cdowner) { std::fprintf(stderr, "%s: CD owner was already specified as \"%s\"\n", argv[0], *cdowner); usage(argv[0]); } *cdowner = &argv[a][8]; } else if (std::strcmp(argv[a], "-k") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (*cdkey) { std::fprintf(stderr, "%s: CD key was already specified as \"%s\"\n", argv[0], *cdkey); usage(argv[0]); } *cdkey = argv[++a]; } else if (std::strncmp(argv[a], "--cdkey=", 8) == 0) { if (*cdkey) { std::fprintf(stderr, "%s: CD key was already specified as \"%s\"\n", argv[0], *cdkey); usage(argv[0]); } *cdkey = &argv[a][8]; } else if (std::strcmp(argv[a], "-l") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (std::strlen(argv[a + 1]) != 4) { std::fprintf(stderr, "%s: language has to be 4 characters long\n", argv[0]); usage(argv[0]); } *gamelang = argv[++a]; } else if (std::strncmp(argv[a], "--lang=", 7) == 0) { if (std::strlen(argv[a] + 7) != 4) { std::fprintf(stderr, "%s: language has to be 4 characters long\n", argv[0]); usage(argv[0]); } *gamelang = &argv[a][7]; } else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else if (std::strcmp(argv[a], "--client") == 0 || std::strcmp(argv[a], "--owner") == 0 || std::strcmp(argv[a], "--cdkey") == 0) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } else if (std::strcmp(argv[a], "-i") == 0 || std::strcmp(argv[a], "--ignore-version") == 0) { *ignoreversion = 1; } else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (*servport == 0) *servport = BNETD_SERV_PORT; if (!(*cdowner)) *cdowner = BNETD_DEFAULT_OWNER; if (!(*cdkey)) *cdkey = BNETD_DEFAULT_KEY; if (!(*clienttag)) { *clienttag = CLIENTTAG_STARCRAFT; *channel = CHANNEL_STARCRAFT; } if (!(*servname)) *servname = BNETD_DEFAULT_HOST; return 0; } void munge(t_client_state * client) { if (!client->munged) { std::printf("\r"); size_t client_modelen = std::strlen(mode_get_prompt(client->mode)); for (unsigned i = 0; i < client_modelen; i++) std::printf(" "); size_t client_textlen = std::strlen(client->text); for (unsigned i = 0; i < client_textlen && i < client->screen_width - client_modelen; i++) std::printf(" "); std::printf("\r"); client->munged = 1; } } void ansi_printf(t_client_state * client, int color, char const * fmt, ...) { std::va_list args; char buffer[2048]; if (!(client)) return; if (!(fmt)) return; if (client->useansi) ansi_text_color_fore(color); va_start(args, fmt); std::vsnprintf(buffer, 2048, fmt, args); va_end(args); str_print_term(stdout, buffer, 0, 1); if (client->useansi) ansi_text_reset(); std::fflush(stdout); } } extern int main(int argc, char * argv[]) { int newacct = 0; int changepass = 0; t_client_state client; t_user_info user; t_packet * packet; t_packet * rpacket; char const * servname = NULL; unsigned short servport = 0; char const * * channellist; unsigned int statsmatch = 24; /* any random number that is rare in uninitialized fields */ std::memset(&user, 0, sizeof(t_user_info)); std::memset(&client, 0, sizeof(t_client_state)); /* default values */ user.archtag = ARCHTAG_WINX86; user.gamelang = CLIENT_AUTH_INFO_GAMELANG; user.ignoreversion = 0; read_commandline(argc, argv, &servname, &servport, &user.clienttag, &user.archtag, &changepass, &newacct, &user.channel, &user.cdowner, &user.cdkey, &user.gamelang, &user.ignoreversion, &client.useansi); client.fd_stdin = fileno(stdin); if (tcgetattr(client.fd_stdin, &client.in_attr_old) >= 0) { client.in_attr_new = client.in_attr_old; client.in_attr_new.c_lflag &= ~(ECHO | ICANON); /* turn off ECHO and ICANON */ client.in_attr_new.c_cc[VMIN] = 0; /* don't require reads to return data */ client.in_attr_new.c_cc[VTIME] = 1; /* timeout = .1 seconds */ tcsetattr(client.fd_stdin, TCSANOW, &client.in_attr_new); client.changed_in = 1; } else { std::fprintf(stderr, "%s: could not set terminal attributes for stdin\n", argv[0]); client.changed_in = 0; } #ifdef HAVE_SIGACTION { struct sigaction winch_action; winch_action.sa_handler = winch_sig_handle; sigemptyset(&winch_action.sa_mask); winch_action.sa_flags = SA_RESTART; sigaction(SIGWINCH, &winch_action, NULL); } #endif if (client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height) < 0) { std::fprintf(stderr, "%s: could not determine screen size\n", argv[0]); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } if (client.useansi) { ansi_text_reset(); ansi_screen_clear(); ansi_cursor_move_home(); std::fflush(stdout); } if ((client.sd = client_connect(argv[0], servname, servport, user.cdowner, user.cdkey, user.clienttag, user.ignoreversion, &client.saddr, &client.sessionkey, &client.sessionnum, user.archtag, user.gamelang)) < 0) { std::fprintf(stderr, "%s: fatal error during handshake\n", argv[0]); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } /* reuse this same packet over and over */ if (!(rpacket = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_fileinforeq)); packet_set_type(packet, CLIENT_FILEINFOREQ); bn_int_set(&packet->u.client_fileinforeq.type, CLIENT_FILEINFOREQ_TYPE_TOS); bn_int_set(&packet->u.client_fileinforeq.unknown2, CLIENT_FILEINFOREQ_UNKNOWN2); if (strcasecmp(user.clienttag, CLIENTTAG_DIABLO2DV) == 0 || strcasecmp(user.clienttag, CLIENTTAG_DIABLO2XP) == 0) packet_append_string(packet, CLIENT_FILEINFOREQ_FILE_TOSUNICODEUSA); else packet_append_string(packet, CLIENT_FILEINFOREQ_FILE_TOSUSA); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(client.sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } while (packet_get_type(rpacket) != SERVER_FILEINFOREPLY); /* real client would also send statsreq on past logins here */ std::printf("----\n"); if (newacct) { if (client.useansi) ansi_text_color_fore(ansi_text_color_red); std::printf("###### Terms Of Service ######\n"); print_file(&client.saddr, packet_get_str_const(rpacket, sizeof(t_server_fileinforeply), 1024), argv[0], user.clienttag); std::printf("##############################\n\n"); if (client.useansi) ansi_text_reset(); } for (;;) { char password[MAX_MESSAGE_LEN]; int status; if (newacct) { char passwordvrfy[MAX_MESSAGE_LEN]; t_hash passhash1; std::printf("Enter information for your new account\n"); client.munged = 1; client.commpos = 0; user.player[0] = '\0'; while ((status = client_get_comm("Username: ", user.player, MAX_MESSAGE_LEN, &client.commpos, 1, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; if (std::strchr(user.player, ' ') || std::strchr(user.player, '\t') || std::strchr(user.player, '\r') || std::strchr(user.player, '\n')) { std::printf("Spaces are not allowed in usernames. Try again.\n"); continue; } /* we could use std::strcspn() but it doesn't exist everywhere */ if (std::strchr(user.player, '#') || std::strchr(user.player, '%') || std::strchr(user.player, '&') || std::strchr(user.player, '*') || std::strchr(user.player, '\\') || std::strchr(user.player, '"') || std::strchr(user.player, ',') || std::strchr(user.player, '<') || std::strchr(user.player, '/') || std::strchr(user.player, '>') || std::strchr(user.player, '?')) { std::printf("The special characters #%%&*\\\",? are not allowed in usernames. Try again.\n"); } if (std::strlen(user.player) >= MAX_USERNAME_LEN) { std::printf("Usernames must not be more than %u characters long. Try again.\n", MAX_USERNAME_LEN - 1); continue; } client.munged = 1; client.commpos = 0; password[0] = '\0'; while ((status = client_get_comm("Password: ", password, MAX_MESSAGE_LEN, &client.commpos, 0, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status<0) continue; if (std::strlen(password)>MAX_USERPASS_LEN) { std::printf("password must not be more than %u characters long. Try again.\n", MAX_USERPASS_LEN); continue; } strtolower(password); client.munged = 1; client.commpos = 0; passwordvrfy[0] = '\0'; while ((status = client_get_comm("Retype password: ", passwordvrfy, MAX_MESSAGE_LEN, &client.commpos, 0, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; strtolower(passwordvrfy); if (std::strcmp(password, passwordvrfy) != 0) { std::printf("Passwords do not match. Try again.\n"); continue; } bnet_hash(&passhash1, std::strlen(password), password); /* do the single hash */ if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_createacctreq1)); packet_set_type(packet, CLIENT_CREATEACCTREQ1); hash_to_bnhash((t_hash const *)&passhash1, packet->u.client_createacctreq1.password_hash1); /* avoid warning */ packet_append_string(packet, user.player); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(client.sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } while (packet_get_type(rpacket) != SERVER_CREATEACCTREPLY1); dprintf("Got CREATEACCTREPLY1\n"); if (bn_int_get(rpacket->u.server_createacctreply1.result) == SERVER_CREATEACCTREPLY1_RESULT_NO) { std::printf("Could not create an account under that name. Try another one.\n"); continue; } std::printf("Account created.\n"); } else if (changepass) { char passwordprev[MAX_MESSAGE_LEN]; char passwordvrfy[MAX_MESSAGE_LEN]; struct { bn_int ticks; bn_int sessionkey; bn_int passhash1[5]; } temp; t_hash oldpasshash1; t_hash oldpasshash2; t_hash newpasshash1; unsigned int ticks; std::printf("Enter your old and new login information\n"); client.munged = 1; client.commpos = 0; user.player[0] = '\0'; while ((status = client_get_comm("Username: ", user.player, MAX_MESSAGE_LEN, &client.commpos, 1, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; if (std::strchr(user.player, ' ')) { std::printf("Spaces not allowed in username. Try again.\n"); continue; } if (std::strlen(user.player) >= MAX_USERNAME_LEN) { std::printf("Usernames must not be more than %u characters long. Try again.\n", MAX_USERNAME_LEN - 1); continue; } client.munged = 1; client.commpos = 0; passwordprev[0] = '\0'; while ((status = client_get_comm("Old password: ", passwordprev, MAX_MESSAGE_LEN, &client.commpos, 0, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; strtolower(passwordprev); client.munged = 1; client.commpos = 0; password[0] = '\0'; while ((status = client_get_comm("New password: ", password, MAX_MESSAGE_LEN, &client.commpos, 0, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; strtolower(password); client.munged = 1; client.commpos = 0; passwordvrfy[0] = '\0'; while ((status = client_get_comm("Retype new password: ", passwordvrfy, MAX_MESSAGE_LEN, &client.commpos, 0, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; strtolower(passwordvrfy); if (std::strcmp(password, passwordvrfy) != 0) { std::printf("New passwords do not match. Try again.\n"); continue; } ticks = 0; /* FIXME: what to use here? */ bn_int_set(&temp.ticks, ticks); bn_int_set(&temp.sessionkey, client.sessionkey); bnet_hash(&oldpasshash1, std::strlen(passwordprev), passwordprev); /* do the single hash for old */ hash_to_bnhash((t_hash const *)&oldpasshash1, temp.passhash1); /* avoid warning */ bnet_hash(&oldpasshash2, sizeof(temp), &temp); /* do the double hash for old */ bnet_hash(&newpasshash1, std::strlen(password), password); /* do the single hash for new */ if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_changepassreq)); packet_set_type(packet, CLIENT_CHANGEPASSREQ); bn_int_set(&packet->u.client_changepassreq.ticks, ticks); bn_int_set(&packet->u.client_changepassreq.sessionkey, client.sessionkey); hash_to_bnhash((t_hash const *)&oldpasshash2, packet->u.client_changepassreq.oldpassword_hash2); /* avoid warning */ hash_to_bnhash((t_hash const *)&newpasshash1, packet->u.client_changepassreq.newpassword_hash1); /* avoid warning */ packet_append_string(packet, user.player); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(client.sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } while (packet_get_type(rpacket) != SERVER_CHANGEPASSACK); dprintf("Got CHANGEPASSACK\n"); if (bn_int_get(rpacket->u.server_changepassack.message) == SERVER_CHANGEPASSACK_MESSAGE_FAIL) { std::printf("Could not change password. Try again.\n"); continue; } std::printf("Password changed.\n"); } else { std::printf("Enter your login information\n"); client.munged = 1; client.commpos = 0; user.player[0] = '\0'; while ((status = client_get_comm("Username: ", user.player, MAX_MESSAGE_LEN, &client.commpos, 1, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; if (std::strchr(user.player, ' ')) { std::printf("Spaces not allowed in username. Try again.\n"); continue; } if (std::strlen(user.player) >= MAX_USERNAME_LEN) { std::printf("Usernames must not be more than %u characters long. Try again.\n", MAX_USERNAME_LEN - 1); continue; } client.munged = 1; client.commpos = 0; password[0] = '\0'; while ((status = client_get_comm("Password: ", password, MAX_MESSAGE_LEN, &client.commpos, 0, client.munged, client.screen_width)) == 0) if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); std::printf(" \r"); client.munged = 1; handle_winch = 0; } else client.munged = 0; std::printf("\n"); if (status < 0) continue; strtolower(password); } /* now login */ { struct { bn_int ticks; bn_int sessionkey; bn_int passhash1[5]; } temp; t_hash passhash1; t_hash passhash2; unsigned int ticks; ticks = 0; /* FIXME: what to use here? */ bn_int_set(&temp.ticks, ticks); bn_int_set(&temp.sessionkey, client.sessionkey); bnet_hash(&passhash1, std::strlen(password), password); /* do the single hash */ hash_to_bnhash((t_hash const *)&passhash1, temp.passhash1); /* avoid warning */ bnet_hash(&passhash2, sizeof(temp), &temp); /* do the double hash */ if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_loginreq1)); packet_set_type(packet, CLIENT_LOGINREQ1); bn_int_set(&packet->u.client_loginreq1.ticks, ticks); bn_int_set(&packet->u.client_loginreq1.sessionkey, client.sessionkey); hash_to_bnhash((t_hash const *)&passhash2, packet->u.client_loginreq1.password_hash2); /* avoid warning */ packet_append_string(packet, user.player); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); } do if (client_blockrecv_packet(client.sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } while (packet_get_type(rpacket) != SERVER_LOGINREPLY1); if (bn_int_get(rpacket->u.server_loginreply1.message) == SERVER_LOGINREPLY1_MESSAGE_SUCCESS) break; std::fprintf(stderr, "Login incorrect.\n"); } std::fprintf(stderr, "Logged in.\n"); std::printf("----\n"); if (newacct && (std::strcmp(user.clienttag, CLIENTTAG_DIABLORTL) == 0 || std::strcmp(user.clienttag, CLIENTTAG_DIABLOSHR) == 0)) { if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); } else { packet_set_size(packet, sizeof(t_client_playerinforeq)); packet_set_type(packet, CLIENT_PLAYERINFOREQ); packet_append_string(packet, user.player); packet_append_string(packet, "LTRD 1 2 0 20 25 15 20 100 0"); /* FIXME: don't hardcode */ client_blocksend_packet(client.sd, packet); packet_del_ref(packet); } } if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_progident2)); packet_set_type(packet, CLIENT_PROGIDENT2); bn_int_tag_set(&packet->u.client_progident2.clienttag, user.clienttag); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(client.sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } while (packet_get_type(rpacket) != SERVER_CHANNELLIST); { unsigned int i; unsigned int chann_off; char const * chann; channellist = (const char**)xmalloc(sizeof(char*)* 1); for (i = 0, chann_off = sizeof(t_server_channellist); (chann = packet_get_str_const(rpacket, chann_off, 128)); i++, chann_off += std::strlen(chann) + 1) { if (chann[0] == '\0') break; /* channel list ends with a "" */ channellist = (const char**)xrealloc(channellist, sizeof(char*)*(i + 2)); channellist[i] = xstrdup(chann); } channellist[i] = NULL; } if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_joinchannel)); packet_set_type(packet, CLIENT_JOINCHANNEL); bn_int_set(&packet->u.client_joinchannel.channelflag, CLIENT_JOINCHANNEL_GENERIC); packet_append_string(packet, user.channel); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); if (psock_ctl(client.sd, PSOCK_NONBLOCK) < 0) std::fprintf(stderr, "%s: could not set TCP socket to non-blocking mode (psock_ctl: %s)\n", argv[0], std::strerror(psock_errno())); client.mode = mode_chat; { unsigned int i; int highest_fd; t_psock_fd_set rfds; #ifdef WIN32 static struct timeval tv; tv.tv_sec = 50 / 1000; tv.tv_usec = 50 % 1000; #endif PSOCK_FD_ZERO(&rfds); #ifndef WIN32 highest_fd = client.fd_stdin; if (client.sd > highest_fd) #endif highest_fd = client.sd; client.currsize = 0; client.munged = 1; /* == need to draw prompt */ client.commpos = 0; client.text[0] = '\0'; for (;;) { if (handle_winch) { client_get_termsize(client.fd_stdin, &client.screen_width, &client.screen_height); handle_winch = 0; std::printf(" \r"); client.munged = 1; } if (client.munged) { std::printf("%s%s", mode_get_prompt(client.mode), client.text + ((client.screen_width <= std::strlen(mode_get_prompt(client.mode)) + client.commpos) ? std::strlen(mode_get_prompt(client.mode)) + client.commpos + 1 - client.screen_width : 0)); std::fflush(stdout); client.munged = 0; } PSOCK_FD_ZERO(&rfds); #ifndef WIN32 PSOCK_FD_SET(client.fd_stdin, &rfds); #endif PSOCK_FD_SET(client.sd, &rfds); errno = 0; #ifndef WIN32 if (psock_select(highest_fd + 1, &rfds, NULL, NULL, NULL) < 0) #else if (psock_select(highest_fd + 1, &rfds, NULL, NULL, &tv) < 0) #endif { if (psock_errno() != PSOCK_EINTR) { munge(&client); std::printf("Select failed (select: %s)\n", std::strerror(psock_errno())); } continue; } #ifndef WIN32 if (PSOCK_FD_ISSET(client.fd_stdin, &rfds)) /* got keyboard data */ #else if (kbhit()) #endif { client.munged = 0; switch (client_get_comm(mode_get_prompt(client.mode), client.text, MAX_MESSAGE_LEN, &client.commpos, 1, 0, client.screen_width)) { case -1: /* cancel */ munge(&client); if (client.mode == mode_command) client.mode = mode_chat; else client.mode = mode_command; client.commpos = 0; client.text[0] = '\0'; break; case 0: /* timeout */ break; case 1: switch (client.mode) { case mode_claninvite: std::printf("\n"); client.munged = 1; if ((client.text[0] != '\0') && strcasecmp(client.text, "yes") && strcasecmp(client.text, "no")) { std::printf("Do you want to accept invitation (yes/no) ? [yes] "); break; } if (!(packet = packet_create(packet_class_bnet))) { std::printf("Packet creation failed.\n"); } else { char result; packet_set_size(packet, sizeof(t_client_clan_invitereply)); packet_set_type(packet, CLIENT_CLAN_INVITEREPLY); bn_int_set(&packet->u.client_clan_invitereply.count, user.count); bn_int_set(&packet->u.client_clan_invitereply.clantag, user.clantag); packet_append_string(packet, user.inviter); if (!strcasecmp(client.text, "no")) result = CLAN_RESPONSE_DECLINED; else result = CLAN_RESPONSE_ACCEPT; packet_append_data(packet, &result, 1); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); if (user.inviter) xfree((void *)user.inviter); user.inviter = NULL; } case mode_clancreateinvite: std::printf("\n"); client.munged = 1; if ((client.text[0] != '\0') && strcasecmp(client.text, "yes") && strcasecmp(client.text, "no")) { std::printf("Do you want to accept invitation (yes/no) ? [yes] "); break; } if (!(packet = packet_create(packet_class_bnet))) { std::printf("Packet creation failed.\n"); } else { char result; packet_set_size(packet, sizeof(t_client_clan_createinvitereply)); packet_set_type(packet, CLIENT_CLAN_CREATEINVITEREPLY); bn_int_set(&packet->u.client_clan_createinvitereply.count, user.count); bn_int_set(&packet->u.client_clan_createinvitereply.clantag, user.clantag); packet_append_string(packet, user.inviter); if (!strcasecmp(client.text, "no")) result = CLAN_RESPONSE_DECLINED; else result = CLAN_RESPONSE_ACCEPT; packet_append_data(packet, &result, 1); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); if (user.inviter) xfree((void *)user.inviter); user.inviter = NULL; } client.mode = mode_chat; break; case mode_gamename: if (client.text[0] == '\0') { munge(&client); std::printf("Games must have a name.\n"); break; } std::printf("\n"); client.munged = 1; std::strncpy(user.curr_gamename, client.text, sizeof(user.curr_gamename)); user.curr_gamename[sizeof(user.curr_gamename) - 1] = '\0'; client.mode = mode_gamepass; break; case mode_gamepass: std::printf("\n"); client.munged = 1; std::strncpy(user.curr_gamepass, client.text, sizeof(user.curr_gamepass)); user.curr_gamepass[sizeof(user.curr_gamepass) - 1] = '\0'; if (!(packet = packet_create(packet_class_bnet))) { std::printf("Packet creation failed.\n"); client.mode = mode_command; } else { packet_set_size(packet, sizeof(t_client_startgame4)); packet_set_type(packet, CLIENT_STARTGAME4); bn_short_set(&packet->u.client_startgame4.status, CLIENT_STARTGAME4_STATUS_INIT); bn_short_set(&packet->u.client_startgame4.flag, 0x0000); bn_int_set(&packet->u.client_startgame4.unknown2, CLIENT_STARTGAME4_UNKNOWN2); bn_short_set(&packet->u.client_startgame4.gametype, CLIENT_GAMELISTREQ_MELEE); bn_short_set(&packet->u.client_startgame4.option, CLIENT_STARTGAME4_OPTION_MELEE_NORMAL); bn_int_set(&packet->u.client_startgame4.unknown4, CLIENT_STARTGAME4_UNKNOWN4); bn_int_set(&packet->u.client_startgame4.unknown5, CLIENT_STARTGAME4_UNKNOWN5); packet_append_string(packet, user.curr_gamename); packet_append_string(packet, user.curr_gamepass); packet_append_string(packet, ",,,,1,3,1,3e37a84c,7,Player\rAshrigo\r"); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); client.mode = mode_gamewait; } break; case mode_gamestop: std::printf("\n"); client.munged = 1; if (!(packet = packet_create(packet_class_bnet))) std::printf("Packet creation failed.\n"); else { packet_set_size(packet, sizeof(t_client_closegame)); packet_set_type(packet, CLIENT_CLOSEGAME); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); std::printf("Game closed.\n"); client.mode = mode_command; } break; case mode_command: if (client.text[0] == '\0') break; std::printf("\n"); client.munged = 1; if (strstart(client.text, "channel") == 0) { std::printf("Available channels:\n"); if (client.useansi) ansi_text_color_fore(ansi_text_color_yellow); for (i = 0; channellist[i]; i++) std::printf(" %s\n", channellist[i]); if (client.useansi) ansi_text_reset(); } else if (strstart(client.text, "create") == 0) { std::printf("Enter new game information\n"); client.mode = mode_gamename; } else if (strstart(client.text, "join") == 0) { std::printf("Not implemented yet.\n"); } else if (strstart(client.text, "ladder") == 0) { std::printf("Not implemented yet.\n"); } else if (strstart(client.text, "stats") == 0) { std::printf("Not implemented yet.\n"); } else if (strstart(client.text, "help") == 0 || std::strcmp(client.text, "?") == 0) { std::printf("Available commands:\n" " channel - join or create a channel\n" " create - create a new game\n" " join - list current games\n" " ladder - list ladder rankings\n" " help - show this text\n" " info - print a player's profile\n" " chinfo - modify your profile\n" " quit - exit bnchat\n" " stats - print a player's game record\n" " invite - invite a player to your clan\n" "Use the escape key to toggle between chat and command modes.\n"); } else if (strstart(client.text, "invite") == 0) { for (i = 6; client.text[i] == ' ' || client.text[i] == '\t'; i++); if (client.text[i] == '\0') { ansi_printf(&client, ansi_text_color_red, "You must specify the player.\n"); } else { if (!(packet = packet_create(packet_class_bnet))) std::fprintf(stderr, "%s: could not create packet\n", argv[0]); else { std::printf("Inviting: %s\n", &client.text[i]); packet_set_size(packet, sizeof(t_client_clan_invitereq)); packet_set_type(packet, CLIENT_CLAN_INVITEREQ); bn_int_set(&packet->u.client_clan_invitereq.count, 1); packet_append_string(packet, &client.text[i]); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); } } } else if (strstart(client.text, "info") == 0) { for (i = 4; client.text[i] == ' ' || client.text[i] == '\t'; i++); if (client.text[i] == '\0') { ansi_printf(&client, ansi_text_color_red, "You must specify the player.\n"); } else { if (!(packet = packet_create(packet_class_bnet))) std::fprintf(stderr, "%s: could not create packet\n", argv[0]); else { std::printf("Profile info for %s:\n", &client.text[i]); packet_set_size(packet, sizeof(t_client_statsreq)); packet_set_type(packet, CLIENT_STATSREQ); bn_int_set(&packet->u.client_statsreq.name_count, 1); bn_int_set(&packet->u.client_statsreq.key_count, 4); statsmatch = (unsigned int)std::time(NULL); bn_int_set(&packet->u.client_statsreq.requestid, statsmatch); packet_append_string(packet, &client.text[i]); #if 0 packet_append_string(packet, "BNET\\acct\\username"); packet_append_string(packet, "BNET\\acct\\userid"); #endif packet_append_string(packet, "profile\\sex"); packet_append_string(packet, "profile\\age"); packet_append_string(packet, "profile\\location"); packet_append_string(packet, "profile\\description"); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); client.mode = mode_waitstat; } } } else if (strstart(client.text, "chinfo") == 0) { std::printf("Not implemented yet.\n"); } else if (strstart(client.text, "quit") == 0) { psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_SUCCESS; } else { ansi_printf(&client, ansi_text_color_red, "Unknown local command \"%s\".\n", client.text); } break; case mode_chat: if (client.text[0] == '\0') break; if (client.text[0] == '/') { munge(&client); } else { ansi_printf(&client, ansi_text_color_blue, "\r<%s>", user.player); std::printf(" "); str_print_term(stdout, client.text, 0, 0); std::printf("\n"); client.munged = 1; } if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_message)); packet_set_type(packet, CLIENT_MESSAGE); packet_append_string(packet, client.text); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); break; default: /* one of the wait states; erase what they typed */ munge(&client); break; } client.commpos = 0; client.text[0] = '\0'; break; } } if (PSOCK_FD_ISSET(client.sd, &rfds)) /* got network data */ { /* rpacket is from server, packet is from client */ switch (net_recv_packet(client.sd, rpacket, &client.currsize)) { case 0: /* nothing */ break; case 1: switch (packet_get_type(rpacket)) { case SERVER_ECHOREQ: /* might as well support it */ if (packet_get_size(rpacket) < sizeof(t_server_echoreq)) { munge(&client); std::printf("Got bad SERVER_ECHOREQ packet (expected %lu bytes, got %u)\n", sizeof(t_server_echoreq), packet_get_size(rpacket)); break; } if (!(packet = packet_create(packet_class_bnet))) { munge(&client); std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_echoreply)); packet_set_type(packet, CLIENT_ECHOREPLY); bn_int_set(&packet->u.client_echoreply.ticks, bn_int_get(rpacket->u.server_echoreq.ticks)); client_blocksend_packet(client.sd, packet); packet_del_ref(packet); break; case SERVER_STARTGAME4_ACK: if (client.mode == mode_gamewait) { munge(&client); if (bn_int_get(rpacket->u.server_startgame4_ack.reply) == SERVER_STARTGAME4_ACK_OK) { std::printf("Game created.\n"); client.mode = mode_gamestop; } else { std::printf("Game could not be created, try another name.\n"); client.mode = mode_gamename; } } break; case SERVER_CLAN_INVITEREQ: if (packet_get_size(rpacket) < sizeof(t_server_clan_invitereq)) { munge(&client); std::printf("Got bad SERVER_CLAN_INVITEREQ packet (expected %lu bytes, got %u)\n", sizeof(t_server_clan_invitereq), packet_get_size(rpacket)); break; } { char const * clan; char const * inviter; int offset; offset = sizeof(t_server_clan_invitereq); if (!(clan = packet_get_str_const(rpacket, offset, CLAN_NAME_MAX))) { munge(&client); std::printf("Got SERVER_CLAN_INVITEREQ with bad or missing clanname\n"); break; } offset += std::strlen(clan) + 1; if (!(inviter = packet_get_str_const(rpacket, offset, MAX_USERNAME_LEN))) { munge(&client); std::printf("Got SERVER_CLAN_INVITEREQ with bad or missing inviter\n"); break; } user.count = bn_int_get(rpacket->u.server_clan_invitereq.count); user.clantag = bn_int_get(rpacket->u.server_clan_invitereq.clantag); if (user.inviter) xfree((void *)user.inviter); user.inviter = xstrdup(inviter); munge(&client); std::printf("%s invited you to Clan %s\n", inviter, clan); std::printf("Do you want to accept invitation (yes/no) ? [yes] "); client.mode = mode_claninvite; break; } case SERVER_CLAN_INVITEREPLY: if (packet_get_size(rpacket) < sizeof(t_server_clan_invitereply)) { munge(&client); std::printf("Got bad SERVER_CLAN_INVITEREPLY packet (expected %lu bytes, got %u)\n", sizeof(t_server_clan_invitereply), packet_get_size(rpacket)); break; } { char result = bn_byte_get(rpacket->u.server_clan_invitereply.result); std::printf("Recieved result: %i\n", result); break; } case SERVER_CLAN_CREATEINVITEREQ: if (packet_get_size(rpacket) < sizeof(t_server_clan_createinvitereq)) { munge(&client); std::printf("Got bad SERVER_CLAN_CREATEINVITEREQ packet (expected %lu bytes, got %u)\n", sizeof(t_server_clan_createinvitereq), packet_get_size(rpacket)); break; } { char const * clan; char const * inviter; char invited_count; std::size_t offset = sizeof(t_server_clan_createinvitereq); if (!(clan = packet_get_str_const(rpacket, offset, CLAN_NAME_MAX))) { munge(&client); std::printf("Got SERVER_CLAN_CREATEINVITEREQ with bad or missing clanname\n"); break; } offset += std::strlen(clan) + 1; if (!(inviter = packet_get_str_const(rpacket, offset, MAX_USERNAME_LEN))) { munge(&client); std::printf("Got SERVER_CLAN_CREATEINVITEREQ with bad or missing inviter\n"); break; } offset += std::strlen(inviter) + 1; if (packet_get_size(rpacket) < offset + 4) { munge(&client); std::printf("Got SERVER_CLAN_CREATEINVITEREQ with missing invited count\n"); break; } invited_count = *((char *)packet_get_data_const(rpacket, offset, 1)); offset++; user.count = bn_int_get(rpacket->u.server_clan_invitereq.count); user.clantag = bn_int_get(rpacket->u.server_clan_invitereq.clantag); if (user.inviter) xfree((void *)user.inviter); user.inviter = xstrdup(inviter); munge(&client); std::printf("%s invited you to Clan %s along with %i other players\n", inviter, clan, invited_count); std::printf("Do you want to accept invitation (yes/no) ? [yes] "); client.mode = mode_clancreateinvite; break; } case SERVER_CLANMEMBERUPDATE: if (packet_get_size(rpacket) < sizeof(t_server_clanmemberupdate)) { munge(&client); std::printf("Got bad SERVER_CLANMEMBERUPDATE packet (expected %lu bytes, got %u)\n", sizeof(t_server_clanmemberupdate), packet_get_size(rpacket)); break; } if (client.mode == mode_claninvite) break; { char const * member; char rank, online; char const * rank_p; char const * online_p; char const * append_str; int offset; char const * rank_str; char const * online_str; offset = sizeof(t_server_clanmemberupdate); if (!(member = packet_get_str_const(rpacket, offset, MAX_USERNAME_LEN))) { munge(&client); std::printf("Got SERVER_CLANMEMBERUPDATE with bad or missing member\n"); break; } offset += std::strlen(member) + 1; if (!(rank_p = (char *)packet_get_data_const(rpacket, offset, 1))) { munge(&client); std::printf("Got SERVER_CLANMEMBERUPDATE with bad or missing rank\n"); break; } rank = *rank_p; offset += 1; if (!(online_p = (char *)packet_get_data_const(rpacket, offset, 1))) { munge(&client); std::printf("Got SERVER_CLAN_MEMBERUPDATE with bad or missing online status\n"); break; } online = *online_p; offset += 1; if (!(append_str = packet_get_str_const(rpacket, offset, MAX_USERNAME_LEN))) { munge(&client); std::printf("Got SERVER_CLANMEMBERUPDATE with bad or missing append_str\n"); break; } switch (rank) { case SERVER_CLAN_MEMBER_NEW: rank_str = "New clan member"; break; case SERVER_CLAN_MEMBER_PEON: rank_str = "Peon"; break; case SERVER_CLAN_MEMBER_GRUNT: rank_str = "Grunt"; break; case SERVER_CLAN_MEMBER_SHAMAN: rank_str = "Shaman"; break; case SERVER_CLAN_MEMBER_CHIEFTAIN: rank_str = "Chieftain"; break; default: rank_str = ""; } switch (online) { case SERVER_CLAN_MEMBER_OFFLINE: online_str = "offline"; break; case SERVER_CLAN_MEMBER_ONLINE: online_str = "online"; break; case SERVER_CLAN_MEMBER_CHANNEL: online_str = "in channel"; break; case SERVER_CLAN_MEMBER_GAME: online_str = "in game"; break; case SERVER_CLAN_MEMBER_PRIVATE_GAME: online_str = "in private game"; break; default: online_str = ""; } munge(&client); std::printf("%s %s is now %s %s\n", rank_str, member, online_str, append_str); } case SERVER_STATSREPLY: if (client.mode == mode_waitstat) { unsigned int names, keys; unsigned int match; unsigned int strpos; char const * temp; munge(&client); names = bn_int_get(rpacket->u.server_statsreply.name_count); keys = bn_int_get(rpacket->u.server_statsreply.key_count); match = bn_int_get(rpacket->u.server_statsreply.requestid); if (names != 1 || keys != 4 || match != statsmatch) std::printf("mangled reply (name_count=%u key_count=%u unknown1=%u)\n", names, keys, match); strpos = sizeof(t_server_statsreply); if ((temp = packet_get_str_const(rpacket, strpos, 256))) { std::printf(" Sex: "); ansi_printf(&client, ansi_text_color_yellow, "%s\n", temp); strpos += std::strlen(temp) + 1; } if ((temp = packet_get_str_const(rpacket, strpos, 256))) { std::printf(" Age: "); ansi_printf(&client, ansi_text_color_yellow, "%s\n", temp); strpos += std::strlen(temp) + 1; } if ((temp = packet_get_str_const(rpacket, strpos, 256))) { std::printf(" Location: "); ansi_printf(&client, ansi_text_color_yellow, "%s\n", temp); strpos += std::strlen(temp) + 1; } if ((temp = packet_get_str_const(rpacket, strpos, 256))) { char msgtemp[1024]; char * tok; std::printf(" Description: \n"); if (client.useansi) ansi_text_color_fore(ansi_text_color_yellow); std::strncpy(msgtemp, temp, sizeof(msgtemp)); msgtemp[sizeof(msgtemp)-1] = '\0'; for (tok = std::strtok(msgtemp, "\r\n"); tok; tok = std::strtok(NULL, "\r\n")) std::printf(" %s\n", tok); if (client.useansi) ansi_text_reset(); strpos += std::strlen(temp) + 1; } if (match == statsmatch) client.mode = mode_command; } break; case SERVER_PLAYERINFOREPLY: /* hmm. we didn't ask for one... */ break; case SERVER_MESSAGE: if (packet_get_size(rpacket) < sizeof(t_server_message)) { munge(&client); std::printf("Got bad SERVER_MESSAGE packet (expected %lu bytes, got %u)", sizeof(t_server_message), packet_get_size(rpacket)); break; } { char const * speaker; char const * message; if (!(speaker = packet_get_str_const(rpacket, sizeof(t_server_message), 32))) { munge(&client); std::printf("Got SERVER_MESSAGE packet with bad or missing speaker\n"); break; } if (!(message = packet_get_str_const(rpacket, sizeof(t_server_message)+std::strlen(speaker) + 1, 1024))) { munge(&client); std::printf("Got SERVER_MESSAGE packet with bad or missing message (speaker=\"%s\" start=%lu len=%u)\n", speaker, sizeof(t_server_message)+std::strlen(speaker) + 1, packet_get_size(rpacket)); break; } switch (bn_int_get(rpacket->u.server_message.type)) { case SERVER_MESSAGE_TYPE_JOIN: munge(&client); ansi_printf(&client, ansi_text_color_green, "\"%s\" %s enters\n", speaker, mflags_get_str(bn_int_get(rpacket->u.server_message.flags))); break; case SERVER_MESSAGE_TYPE_CHANNEL: munge(&client); ansi_printf(&client, ansi_text_color_green, "Joining channel :\"%s\" %s\n", message, cflags_get_str(bn_int_get(rpacket->u.server_message.flags))); break; case SERVER_MESSAGE_TYPE_ADDUSER: munge(&client); ansi_printf(&client, ansi_text_color_green, "\"%s\" %s is here\n", speaker, mflags_get_str(bn_int_get(rpacket->u.server_message.flags))); break; case SERVER_MESSAGE_TYPE_USERFLAGS: break; case SERVER_MESSAGE_TYPE_PART: munge(&client); ansi_printf(&client, ansi_text_color_green, "\"%s\" %s leaves\n", speaker, mflags_get_str(bn_int_get(rpacket->u.server_message.flags))); break; case SERVER_MESSAGE_TYPE_WHISPER: munge(&client); ansi_printf(&client, ansi_text_color_blue, "", speaker); std::printf(" "); str_print_term(stdout, message, 0, 0); std::printf("\n"); break; case SERVER_MESSAGE_TYPE_WHISPERACK: munge(&client); ansi_printf(&client, ansi_text_color_blue, "", speaker); std::printf(" "); str_print_term(stdout, message, 0, 0); std::printf("\n"); break; case SERVER_MESSAGE_TYPE_BROADCAST: munge(&client); ansi_printf(&client, ansi_text_color_yellow, " %s\n", speaker, message); break; case SERVER_MESSAGE_TYPE_ERROR: munge(&client); ansi_printf(&client, ansi_text_color_red, " %s\n", message); break; case SERVER_MESSAGE_TYPE_INFO: munge(&client); ansi_printf(&client, ansi_text_color_yellow, " %s\n", message); break; case SERVER_MESSAGE_TYPE_EMOTE: munge(&client); ansi_printf(&client, ansi_text_color_yellow, "<%s %s>\n", speaker, message); break; default: case SERVER_MESSAGE_TYPE_TALK: munge(&client); ansi_printf(&client, ansi_text_color_yellow, "<%s>", speaker); std::printf(" "); str_print_term(stdout, message, 0, 0); std::printf("\n"); } } break; default: munge(&client); std::printf("Unsupported server packet type 0x%04x\n", packet_get_type(rpacket)); hexdump(stdout, packet_get_data_const(rpacket, 0, packet_get_size(rpacket)), packet_get_size(rpacket)); std::printf("\n"); } client.currsize = 0; break; case -1: /* error (probably connection closed) */ default: munge(&client); std::printf("----\nConnection closed by server.\n"); psock_close(client.sd); if (client.changed_in) tcsetattr(client.fd_stdin, TCSAFLUSH, &client.in_attr_old); return EXIT_SUCCESS; } } } } /* not reached */ } pvpgn-1.99.7.2.1+dfsg/src/client/bnftp.cpp000066400000000000000000000637701462640461400200770ustar00rootroot00000000000000/* * Copyright (C) 1999,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_TERMIOS_H # include #endif #include "compat/termios.h" #include "compat/psock.h" #include "common/packet.h" #include "common/field_sizes.h" #include "common/bnettime.h" #include "common/hexdump.h" #include "common/util.h" #include "common/tag.h" #include "common/bn_type.h" #include "common/xalloc.h" #include "common/file_protocol.h" #include "common/init_protocol.h" #ifdef CLIENTDEBUG # include "common/eventlog.h" #endif #include "client.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" using namespace pvpgn; using namespace pvpgn::client; namespace { void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [ []]\n", progname); std::fprintf(stderr, " -b, --client=SEXP report client as Brood Wars\n" " -d, --client=DRTL report client as Diablo Retail\n" " --client=DSHR report client as Diablo Shareware\n" " -s, --client=STAR report client as Starcraft (default)\n"); std::fprintf(stderr, " --client=SSHR report client as Starcraft Shareware\n" " -w, --client=W2BN report client as Warcraft II BNE\n" " --client=D2DV report client as Diablo II\n" " --client=D2XP report client as Diablo II: LoD\n" " --client=WAR3 report client as Warcraft III\n" " --client=W3XP report client as Warcraft III: FT\n" " --hexdump=FILE do hex dump of packets into FILE\n"); std::fprintf(stderr, " --arch=IX86 report architecture as Windows (x86)\n" " --arch=PMAC report architecture as Macintosh\n" " --arch=XMAC report architecture as Macintosh OSX\n" ); std::fprintf(stderr, " --startoffset=OFFSET force offset to be OFFSET\n" " --exists=ACTION Ask/Overwrite/Backup/Resume if the file exists\n" " --file=FILENAME use FILENAME instead of asking\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n"); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { int a; int sd; struct sockaddr_in saddr; t_packet * packet; t_packet * rpacket; t_packet * fpacket; char const * clienttag = NULL; char const * archtag = NULL; char const * servname = NULL; unsigned short servport = 0; char const * hexfile = NULL; char text[MAX_MESSAGE_LEN]; char const * reqfile = NULL; struct hostent * host; unsigned int commpos; struct termios in_attr_old; struct termios in_attr_new; int changed_in; unsigned int currsize; unsigned int filelen; unsigned int startoffset; int startoffsetoverride = 0; #define EXIST_ACTION_UNSPEC -1 #define EXIST_ACTION_ASK 0 #define EXIST_ACTION_OVERWRITE 1 #define EXIST_ACTION_BACKUP 2 #define EXIST_ACTION_RESUME 3 int exist_action = EXIST_ACTION_UNSPEC; struct stat exist_buf; char const * filename; std::FILE * fp; std::FILE * hexstrm = NULL; int fd_stdin; t_bnettime bntime; std::time_t tm; char timestr[FILE_TIME_MAXLEN]; unsigned int screen_width, screen_height; int munged; unsigned int newproto = 0; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (servname && std::isdigit((int)argv[a][0]) && a + 1 >= argc) { if (str_to_ushort(argv[a], &servport) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (!servname && argv[a][0] != '-' && a + 2 >= argc) servname = argv[a]; else if (std::strcmp(argv[a], "-b") == 0 || std::strcmp(argv[a], "--client=SEXP") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_BROODWARS; } else if (std::strcmp(argv[a], "-d") == 0 || std::strcmp(argv[a], "--client=DRTL") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLORTL; } else if (std::strcmp(argv[a], "--client=DSHR") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLOSHR; } else if (std::strcmp(argv[a], "-s") == 0 || std::strcmp(argv[a], "--client=STAR") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_STARCRAFT; } else if (std::strcmp(argv[a], "--client=SSHR") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_SHAREWARE; } else if (std::strcmp(argv[a], "-w") == 0 || std::strcmp(argv[a], "--client=W2BN") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_WARCIIBNE; } else if (std::strcmp(argv[a], "--client=D2DV") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLO2DV; } else if (std::strcmp(argv[a], "--client=D2XP") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLO2XP; } else if (std::strcmp(argv[a], "--client=WAR3") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_WARCRAFT3; newproto = 1; } else if (std::strcmp(argv[a], "--client=W3XP") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_WAR3XP; newproto = 1; } else if (std::strncmp(argv[a], "--client=", 9) == 0) { std::fprintf(stderr, "%s: unknown client tag \"%s\"\n", argv[0], &argv[a][9]); usage(argv[0]); } else if (std::strcmp(argv[a], "--arch=IX86") == 0) { if (archtag) { std::fprintf(stderr, "%s: architecture type was already specified as \"%s\"\n", argv[0], archtag); usage(argv[0]); } archtag = ARCHTAG_WINX86; } else if (std::strcmp(argv[a], "--arch=PMAC") == 0) { if (archtag) { std::fprintf(stderr, "%s: architecture type was already specified as \"%s\"\n", argv[0], archtag); usage(argv[0]); } archtag = ARCHTAG_MACPPC; } else if (std::strcmp(argv[a], "--arch=XMAC") == 0) { if (archtag) { std::fprintf(stderr, "%s: architecture type was already specified as \"%s\"\n", argv[0], archtag); usage(argv[0]); } archtag = ARCHTAG_OSXPPC; } else if (std::strncmp(argv[a], "--arch=", 9) == 0) { std::fprintf(stderr, "%s: unknown architecture tag \"%s\"\n", argv[0], &argv[a][9]); usage(argv[0]); } else if (std::strncmp(argv[a], "--hexdump=", 10) == 0) { if (hexfile) { std::fprintf(stderr, "%s: hexdump file was already specified as \"%s\"\n", argv[0], hexfile); usage(argv[0]); } hexfile = &argv[a][10]; } else if (std::strncmp(argv[a], "--startoffset=", 14) == 0) { if (startoffsetoverride) { std::fprintf(stderr, "%s: startoffset was already specified as %u\n", argv[0], startoffset); usage(argv[0]); } if (str_to_uint(&argv[a][14], &startoffset) < 0) { std::fprintf(stderr, "%s: startoffset \"%s\" should be a positive integer\n", argv[0], &argv[a][14]); usage(argv[0]); } startoffsetoverride = 1; } else if (std::strncmp(argv[a], "--exists=", 9) == 0) { if (exist_action != EXIST_ACTION_UNSPEC) { std::fprintf(stderr, "%s: exists was already specified\n", argv[0]); usage(argv[0]); } if (argv[a][9] == 'o' || argv[a][9] == 'O') exist_action = EXIST_ACTION_OVERWRITE; else if (argv[a][9] == 'a' || argv[a][9] == 'A') exist_action = EXIST_ACTION_ASK; else if (argv[a][9] == 'b' || argv[a][9] == 'B') exist_action = EXIST_ACTION_BACKUP; else if (argv[a][9] == 'r' || argv[a][9] == 'R') exist_action = EXIST_ACTION_RESUME; else { std::fprintf(stderr, "%s: exists must begin with a,A,o,O,b,B,r or R", argv[0]); usage(argv[0]); } } else if (std::strncmp(argv[a], "--file=", 7) == 0) { if (reqfile) { std::fprintf(stderr, "%s: file was already specified as \"%s\"\n", argv[0], reqfile); usage(argv[0]); } reqfile = &argv[a][7]; } else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return 0; } else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else if (std::strcmp(argv[a], "--client") == 0 || std::strcmp(argv[a], "--hexdump") == 0) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (servport == 0) servport = BNETD_SERV_PORT; if (!clienttag) clienttag = CLIENTTAG_STARCRAFT; if (!archtag) archtag = ARCHTAG_WINX86; if (!servname) servname = BNETD_DEFAULT_HOST; if (exist_action == EXIST_ACTION_UNSPEC) exist_action = EXIST_ACTION_ASK; if (hexfile) { if (!(hexstrm = std::fopen(hexfile, "w"))) std::fprintf(stderr, "%s: could not open file \"%s\" for writing the hexdump (std::fopen: %s)", argv[0], hexfile, std::strerror(errno)); else { std::fprintf(hexstrm, "# dump generated by bnftp version " PVPGN_VERSION "\n"); } } if (psock_init() < 0) { std::fprintf(stderr, "%s: could not inialialize socket functions\n", argv[0]); return EXIT_FAILURE; } if (!(host = gethostbyname(servname))) { std::fprintf(stderr, "%s: unknown host \"%s\"\n", argv[0], servname); return EXIT_FAILURE; } fd_stdin = fileno(stdin); if (tcgetattr(fd_stdin, &in_attr_old) >= 0) { in_attr_new = in_attr_old; in_attr_new.c_lflag &= ~(ECHO | ICANON); /* turn off ECHO and ICANON */ in_attr_new.c_cc[VMIN] = 1; /* require reads to return at least one byte */ in_attr_new.c_cc[VTIME] = 0; /* no timeout */ tcsetattr(fd_stdin, TCSANOW, &in_attr_new); changed_in = 1; } else { std::fprintf(stderr, "%s: could not get terminal attributes for stdin\n", argv[0]); changed_in = 0; } if (client_get_termsize(fd_stdin, &screen_width, &screen_height) < 0) { std::fprintf(stderr, "%s: could not determine screen size\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } if ((sd = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP)) < 0) { std::fprintf(stderr, "%s: could not create socket (psock_socket: %s)\n", argv[0], std::strerror(psock_errno())); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } std::memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = PSOCK_AF_INET; saddr.sin_port = htons(servport); std::memcpy(&saddr.sin_addr.s_addr, host->h_addr_list[0], host->h_length); if (psock_connect(sd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { std::fprintf(stderr, "%s: could not connect to server \"%s\" port %hu (psock_connect: %s)\n", argv[0], servname, servport, std::strerror(psock_errno())); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(saddr.sin_addr), addrstr, sizeof(addrstr)); std::printf("Connected to %s:%hu.\n", addrstr, servport); #ifdef CLIENTDEBUG eventlog_set(stderr); #endif if (!(packet = packet_create(packet_class_init))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } bn_byte_set(&packet->u.client_initconn.cclass, CLIENT_INITCONN_CLASS_FILE); if (hexstrm) { std::fprintf(hexstrm, "%d: send class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(packet), (unsigned int)packet_get_class(packet), packet_get_type_str(packet, packet_dir_from_client), packet_get_type(packet), packet_get_size(packet)); hexdump(hexstrm, packet_get_raw_data(packet, 0), packet_get_size(packet)); } client_blocksend_packet(sd, packet); packet_del_ref(packet); if (!(rpacket = packet_create(packet_class_file))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } if (!(fpacket = packet_create(packet_class_raw))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); packet_del_ref(rpacket); return EXIT_FAILURE; } if (!reqfile) /* if not specified on the command line then prompt for it */ { munged = 1; commpos = 0; text[0] = '\0'; for (;;) { switch (client_get_comm("filename: ", text, sizeof(text), &commpos, 1, munged, screen_width)) { case -1: /* cancel or error */ std::printf("\n"); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; case 0: /* timeout */ munged = 0; continue; case 1: munged = 0; if (text[0] == '\0') continue; std::printf("\n"); } break; } reqfile = text; } if (stat(reqfile, &exist_buf) == 0) /* check if the file exists */ { char text2[MAX_MESSAGE_LEN]; munged = 1; commpos = 0; text2[0] = '\0'; while (exist_action == EXIST_ACTION_ASK) { switch (client_get_comm("File exists [O]verwrite, [B]ackup or [R]esume?: ", text2, sizeof(text2), &commpos, 1, munged, screen_width)) { case -1: /* cancel or error */ std::printf("\n"); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; case 0: /* timeout */ munged = 0; continue; case 1: munged = 0; if (text2[0] == '\0') continue; std::printf("\n"); break; } switch (text2[0]) { case 'o': case 'O': exist_action = EXIST_ACTION_OVERWRITE; break; case 'b': case 'B': exist_action = EXIST_ACTION_BACKUP; break; case 'r': case 'R': exist_action = EXIST_ACTION_RESUME; break; default: std::printf("Please answer with o,O,b,B,r or R.\n"); munged = 1; continue; } break; } switch (exist_action) { case EXIST_ACTION_OVERWRITE: if (!startoffsetoverride) startoffset = 0; break; case EXIST_ACTION_BACKUP: { char * bakfile; unsigned int bnr; int renamed = 0; bakfile = (char*)xmalloc(std::strlen(reqfile) + 1 + 2 + 1); /* assuming we go up to bnr 99 we need reqfile+'.'+'99'+'\0' */ for (bnr = 0; bnr < 100; bnr++) { std::sprintf(bakfile, "%s.%d", reqfile, bnr); if (stat(bakfile, &exist_buf) == 0) continue; /* backup exists */ /* backup does not exist */ if (std::rename(reqfile, bakfile) < 0) /* just std::rename the existing file to the backup */ std::fprintf(stderr, "%s: could not create backup file \"%s\" (std::rename: %s)\n", argv[0], bakfile, std::strerror(errno)); else { renamed = 1; std::printf("Renaming \"%s\" to \"%s\".\n", reqfile, bakfile); } break; } xfree(bakfile); if (!renamed) { std::fprintf(stderr, "%s: could not create backup for \"%s\".\n", argv[0], reqfile); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } if (!startoffsetoverride) startoffset = 0; } break; case EXIST_ACTION_RESUME: if (!startoffsetoverride) startoffset = exist_buf.st_size; break; } } else if (!startoffsetoverride) startoffset = 0; if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); if (!(packet = packet_create(packet_class_file))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } if (newproto) { /* first send ARCH/CTAG */ packet_set_size(packet, sizeof(t_client_file_req2)); packet_set_type(packet, CLIENT_FILE_REQ2); bn_int_tag_set(&packet->u.client_file_req2.archtag, archtag); bn_int_tag_set(&packet->u.client_file_req2.clienttag, clienttag); bn_long_set_a_b(&packet->u.client_file_req2.unknown1, 0, 0); } else { packet_set_size(packet, sizeof(t_client_file_req)); packet_set_type(packet, CLIENT_FILE_REQ); bn_int_tag_set(&packet->u.client_file_req.archtag, archtag); bn_int_tag_set(&packet->u.client_file_req.clienttag, clienttag); bn_int_set(&packet->u.client_file_req.adid, 0); bn_int_set(&packet->u.client_file_req.extensiontag, 0); bn_int_set(&packet->u.client_file_req.startoffset, startoffset); bn_long_set_a_b(&packet->u.client_file_req.timestamp, 0x00000000, 0x00000000); packet_append_string(packet, reqfile); } if (hexstrm) { std::fprintf(hexstrm, "%d: send class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(packet), (unsigned int)packet_get_class(packet), packet_get_type_str(packet, packet_dir_from_client), packet_get_type(packet), packet_get_size(packet)); hexdump(hexstrm, packet_get_raw_data(packet, 0), packet_get_size(packet)); } if (newproto) std::printf("\nSending ARCH/CTAG info..."); else std::printf("\nRequesting info..."); std::fflush(stdout); client_blocksend_packet(sd, packet); packet_del_ref(packet); if (newproto) { /* received the stupid 4 bytes unknown "packet" */ packet_set_size(fpacket, sizeof(t_server_file_unknown1)); if (client_blockrecv_packet(sd, fpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } if (hexstrm) { std::fprintf(hexstrm, "%d: recv class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(fpacket), (unsigned int)packet_get_class(fpacket), packet_get_type_str(fpacket, packet_dir_from_server), packet_get_type(fpacket), packet_get_size(fpacket)); hexdump(hexstrm, packet_get_raw_data(fpacket, 0), packet_get_size(fpacket)); } /* send file info request */ packet_set_size(fpacket, sizeof(t_client_file_req3)); bn_int_set(&fpacket->u.client_file_req3.unknown1, 0); bn_long_set_a_b(&fpacket->u.client_file_req3.timestamp, 0, 0); bn_long_set_a_b(&fpacket->u.client_file_req3.unknown2, 0, 0); bn_long_set_a_b(&fpacket->u.client_file_req3.unknown3, 0, 0); bn_long_set_a_b(&fpacket->u.client_file_req3.unknown4, 0, 0); bn_long_set_a_b(&fpacket->u.client_file_req3.unknown5, 0, 0); bn_long_set_a_b(&fpacket->u.client_file_req3.unknown6, 0, 0); packet_append_string(fpacket, reqfile); std::printf("\nRequesting info..."); std::fflush(stdout); client_blocksend_packet(sd, fpacket); if (hexstrm) { std::fprintf(hexstrm, "%d: send class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(fpacket), (unsigned int)packet_get_class(fpacket), packet_get_type_str(fpacket, packet_dir_from_client), packet_get_type(fpacket), packet_get_size(fpacket)); hexdump(hexstrm, packet_get_raw_data(fpacket, 0), packet_get_size(fpacket)); } } do { if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } if (hexstrm) { std::fprintf(hexstrm, "%d: recv class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(rpacket), (unsigned int)packet_get_class(rpacket), packet_get_type_str(rpacket, packet_dir_from_server), packet_get_type(rpacket), packet_get_size(rpacket)); hexdump(hexstrm, packet_get_raw_data(rpacket, 0), packet_get_size(rpacket)); } } while (packet_get_type(rpacket) != SERVER_FILE_REPLY); filelen = bn_int_get(rpacket->u.server_file_reply.filelen); bn_long_to_bnettime(rpacket->u.server_file_reply.timestamp, &bntime); tm = bnettime_to_time(bntime); std::strftime(timestr, FILE_TIME_MAXLEN, FILE_TIME_FORMAT, std::localtime(&tm)); filename = packet_get_str_const(rpacket, sizeof(t_server_file_reply), MAX_FILENAME_STR); if (exist_action == EXIST_ACTION_RESUME) { if (!(fp = std::fopen(reqfile, "ab"))) { std::fprintf(stderr, "%s: could not open file \"%s\" for appending (std::fopen: %s)\n", argv[0], reqfile, std::strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } } else { if (!(fp = std::fopen(reqfile, "wb"))) { std::fprintf(stderr, "%s: could not open file \"%s\" for writing (std::fopen: %s)\n", argv[0], reqfile, std::strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } } std::printf("\n name: \""); str_print_term(stdout, filename, 0, 0); std::printf("\"\n changed: %s\n length: %u bytes\n", timestr, filelen); std::fflush(stdout); if (startoffset > 0) { filelen -= startoffset; /* for resuming files */ std::printf("Resuming at position %u (%u bytes remaining).\n", startoffset, filelen); } std::printf("\nSaving to \"%s\"...", reqfile); for (currsize = 0; currsize + MAX_PACKET_SIZE <= filelen; currsize += MAX_PACKET_SIZE) { std::printf("."); std::fflush(stdout); if (client_blockrecv_raw_packet(sd, fpacket, MAX_PACKET_SIZE) < 0) { std::printf("error\n"); std::fprintf(stderr, "%s: server closed connection\n", argv[0]); if (std::fclose(fp) < 0) std::fprintf(stderr, "%s: could not close file \"%s\" after writing (std::fclose: %s)\n", argv[0], reqfile, std::strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } if (hexstrm) { std::fprintf(hexstrm, "%d: recv class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(fpacket), (unsigned int)packet_get_class(fpacket), packet_get_type_str(fpacket, packet_dir_from_server), packet_get_type(fpacket), packet_get_size(fpacket)); hexdump(hexstrm, packet_get_raw_data(fpacket, 0), packet_get_size(fpacket)); } if (std::fwrite(packet_get_raw_data_const(fpacket, 0), 1, MAX_PACKET_SIZE, fp) < MAX_PACKET_SIZE) { std::printf("error\n"); std::fprintf(stderr, "%s: could not write to file \"%s\" (std::fwrite: %s)\n", argv[0], reqfile, std::strerror(errno)); if (std::fclose(fp) < 0) std::fprintf(stderr, "%s: could not close file \"%s\" after writing (std::fclose: %s)\n", argv[0], reqfile, std::strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } } filelen -= currsize; if (filelen) { std::printf("."); std::fflush(stdout); if (client_blockrecv_raw_packet(sd, fpacket, filelen) < 0) { std::printf("error\n"); std::fprintf(stderr, "%s: server closed connection\n", argv[0]); if (std::fclose(fp) < 0) std::fprintf(stderr, "%s: could not close file \"%s\" after writing (std::fclose: %s)\n", argv[0], reqfile, std::strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } if (hexstrm) { std::fprintf(hexstrm, "%d: recv class=%s[0x%02hx] type=%s[0x%04hx] length=%u\n", sd, packet_get_class_str(fpacket), (unsigned int)packet_get_class(fpacket), packet_get_type_str(fpacket, packet_dir_from_server), packet_get_type(fpacket), packet_get_size(fpacket)); hexdump(hexstrm, packet_get_raw_data(fpacket, 0), packet_get_size(fpacket)); } if (std::fwrite(packet_get_raw_data_const(fpacket, 0), 1, filelen, fp) < filelen) { std::printf("error\n"); std::fprintf(stderr, "%s: could not write to file \"%s\"\n", argv[0], reqfile); if (std::fclose(fp) < 0) std::fprintf(stderr, "%s: could not close file \"%s\" after writing (std::fclose: %s)\n", argv[0], reqfile, std::strerror(errno)); packet_del_ref(fpacket); packet_del_ref(rpacket); return EXIT_FAILURE; } } packet_del_ref(fpacket); packet_del_ref(rpacket); if (hexstrm) { std::fprintf(hexstrm, "# end of dump\n"); if (std::fclose(hexstrm) < 0) std::fprintf(stderr, "%s: could not close hexdump file \"%s\" after writing (std::fclose: %s)", argv[0], hexfile, std::strerror(errno)); } if (std::fclose(fp) < 0) { std::fprintf(stderr, "%s: could not close file \"%s\" after writing (std::fclose: %s)\n", argv[0], reqfile, std::strerror(errno)); return EXIT_FAILURE; } std::printf("done\n"); return EXIT_FAILURE; } pvpgn-1.99.7.2.1+dfsg/src/client/bnstat.cpp000066400000000000000000001141001462640461400202410ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include #include #include #ifdef HAVE_TERMIOS_H # include #endif #include "compat/termios.h" #include "compat/psock.h" #include "common/bnet_protocol.h" #include "common/util.h" #include "common/tag.h" #include "common/packet.h" #include "common/bn_type.h" #include "common/bnettime.h" /* #include "common/init_protocol.h" #include "common/udp_protocol.h" #include "common/field_sizes.h" #include "common/network.h" #include "common/version.h" #ifdef CLIENTDEBUG # include "common/eventlog.h" #endif #include "udptest.h" */ #include "client_connect.h" #include "client.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" #ifdef CLIENTDEBUG # define dprintf printf #else # define dprintf if (0) printf #endif using namespace pvpgn; using namespace pvpgn::client; namespace { char const * bnclass_get_str(unsigned int cclass) { switch (cclass) { case PLAYERINFO_DRTL_CLASS_WARRIOR: return "warrior"; case PLAYERINFO_DRTL_CLASS_ROGUE: return "rogue"; case PLAYERINFO_DRTL_CLASS_SORCERER: return "sorcerer"; default: return "unknown"; } } void usage(char const * progname) { std::fprintf(stderr, "usage: %s [] [ []]\n", progname); std::fprintf(stderr, " -b, --client=SEXP report client as Brood Wars\n" " -d, --client=DRTL report client as Diablo Retail\n" " --client=DSHR report client as Diablo Shareware\n" " -s, --client=STAR report client as Starcraft (default)\n"); std::fprintf(stderr, " --client=SSHR report client as Starcraft Shareware\n" " -w, --client=W2BN report client as Warcraft II BNE\n" " --client=D2DV report client as Diablo II\n" " --client=D2XP report client as Diablo II: LoD\n" " --client=WAR3 report client as Warcraft III\n"); std::fprintf(stderr, " -o NAME, --owner=NAME report CD owner as NAME\n" " -k KEY, --cdkey=KEY report CD key as KEY\n" " -i, --ignore-version ignore version request (do not send game version, CD owner/key)\n" " -p PLR, --player=PLR print stats for player PLR\n" " --bnetd also print BNETD-specific stats\n" " --fsgs also print FSGS-specific stats\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n"); std::exit(EXIT_FAILURE); } } extern int main(int argc, char * argv[]) { int a; int sd; int gotplayer = 0; struct sockaddr_in saddr; t_packet * packet; t_packet * rpacket; char const * cdowner = NULL; char const * cdkey = NULL; char const * clienttag = NULL; char const * servname = NULL; unsigned short servport = 0; char text[MAX_MESSAGE_LEN] = ""; unsigned int commpos; struct termios in_attr_old; struct termios in_attr_new; int changed_in; unsigned int count; unsigned int sessionkey; unsigned int sessionnum; unsigned int val; int fd_stdin = 0; int use_bnetd = 0; int use_fsgs = 0; unsigned int screen_width, screen_height; int munged = 0; int ignoreversion = 0; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return EXIT_FAILURE; } for (a = 1; a < argc; a++) if (servname && std::isdigit((int)argv[a][0]) && a + 1 >= argc) { if (str_to_ushort(argv[a], &servport) < 0) { std::fprintf(stderr, "%s: \"%s\" should be a positive integer\n", argv[0], argv[a]); usage(argv[0]); } } else if (!servname && argv[a][0] != '-' && a + 2 >= argc) servname = argv[a]; else if (std::strcmp(argv[a], "-b") == 0 || std::strcmp(argv[a], "--client=SEXP") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_BROODWARS; } else if (std::strcmp(argv[a], "-d") == 0 || std::strcmp(argv[a], "--client=DRTL") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLORTL; } else if (std::strcmp(argv[a], "--client=DSHR") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_DIABLOSHR; } else if (std::strcmp(argv[a], "-s") == 0 || std::strcmp(argv[a], "--client=STAR") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_STARCRAFT; } else if (std::strcmp(argv[a], "--client=SSHR") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_SHAREWARE; } else if (std::strcmp(argv[a], "-w") == 0 || std::strcmp(argv[a], "--client=W2BN") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag); usage(argv[0]); } clienttag = CLIENTTAG_WARCIIBNE; } else if (std::strcmp(argv[a], "--client=D2DV") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag ); usage(argv[0]); } clienttag = CLIENTTAG_DIABLO2DV; } else if (std::strcmp(argv[a], "--client=D2XP") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag ); usage(argv[0]); } clienttag = CLIENTTAG_DIABLO2XP; } else if (std::strcmp(argv[a], "--client=WAR3") == 0) { if (clienttag) { std::fprintf(stderr, "%s: client type was already specified as \"%s\"\n", argv[0], clienttag ); usage(argv[0]); } clienttag = CLIENTTAG_WARCRAFT3; } else if (std::strncmp(argv[a], "--client=", 9) == 0) { std::fprintf(stderr, "%s: unknown client tag \"%s\"\n", argv[0], &argv[a][9]); usage(argv[0]); } else if (std::strcmp(argv[a], "-o") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (cdowner) { std::fprintf(stderr, "%s: CD owner was already specified as \"%s\"\n", argv[0], cdowner); usage(argv[0]); } cdowner = argv[++a]; } else if (std::strncmp(argv[a], "--owner=", 8) == 0) { if (cdowner) { std::fprintf(stderr, "%s: CD owner was already specified as \"%s\"\n", argv[0], cdowner); usage(argv[0]); } cdowner = &argv[a][8]; } else if (std::strcmp(argv[a], "-k") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (cdkey) { std::fprintf(stderr, "%s: CD key was already specified as \"%s\"\n", argv[0], cdkey); usage(argv[0]); } cdkey = argv[++a]; } else if (std::strncmp(argv[a], "--cdkey=", 8) == 0) { if (cdkey) { std::fprintf(stderr, "%s: CD key was already specified as \"%s\"\n", argv[0], cdkey); usage(argv[0]); } cdkey = &argv[a][8]; } else if (std::strcmp(argv[a], "-p") == 0) { if (a + 1 >= argc) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } if (gotplayer) { std::fprintf(stderr, "%s: player was already specified as \"%s\"\n", argv[0], text); usage(argv[0]); } a++; if (argv[a][0] == '\0') { std::fprintf(stderr, "%s: player name can not be empty\n", argv[0]); usage(argv[0]); } std::strncpy(text, argv[a], sizeof(text)); text[sizeof(text)-1] = '\0'; gotplayer = 1; } else if (std::strncmp(argv[a], "--player=", 9) == 0) { if (gotplayer) { std::fprintf(stderr, "%s: player was already specified as \"%s\"\n", argv[0], text); usage(argv[0]); } if (argv[a][9] == '\0') { std::fprintf(stderr, "%s: player name can not be empty\n", argv[0]); usage(argv[0]); } std::strncpy(text, &argv[a][9], sizeof(text)); text[sizeof(text)-1] = '\0'; gotplayer = 1; } else if (std::strcmp(argv[a], "--bnetd") == 0) use_bnetd = 1; else if (std::strcmp(argv[a], "--fsgs") == 0) use_fsgs = 1; else if (std::strcmp(argv[a], "-h") == 0 || std::strcmp(argv[a], "--help") == 0 || std::strcmp(argv[a], "--usage") == 0) usage(argv[0]); else if (std::strcmp(argv[a], "-i") == 0 || std::strcmp(argv[a], "--ignore-version") == 0) ignoreversion = 1; else if (std::strcmp(argv[a], "-v") == 0 || std::strcmp(argv[a], "--version") == 0) { std::printf("version " PVPGN_VERSION "\n"); return EXIT_SUCCESS; } else if (std::strcmp(argv[a], "--client") == 0 || std::strcmp(argv[a], "--owner") == 0 || std::strcmp(argv[a], "--cdkey") == 0 || std::strcmp(argv[a], "--player") == 0) { std::fprintf(stderr, "%s: option \"%s\" requires an argument\n", argv[0], argv[a]); usage(argv[0]); } else { std::fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[a]); usage(argv[0]); } if (servport == 0) servport = BNETD_SERV_PORT; if (!cdowner) cdowner = BNETD_DEFAULT_OWNER; if (!cdkey) cdkey = BNETD_DEFAULT_KEY; if (!clienttag) clienttag = CLIENTTAG_STARCRAFT; if (!servname) servname = BNETD_DEFAULT_HOST; if (gotplayer) changed_in = 0; /* no need to change terminal attributes */ else { fd_stdin = fileno(stdin); if (tcgetattr(fd_stdin, &in_attr_old) >= 0) { in_attr_new = in_attr_old; in_attr_new.c_lflag &= ~(ECHO | ICANON); /* turn off ECHO and ICANON */ in_attr_new.c_cc[VMIN] = 1; /* require reads to return at least one byte */ in_attr_new.c_cc[VTIME] = 0; /* no timeout */ tcsetattr(fd_stdin, TCSANOW, &in_attr_new); changed_in = 1; } else { std::fprintf(stderr, "%s: could not set terminal attributes for stdin\n", argv[0]); changed_in = 0; } if (client_get_termsize(fd_stdin, &screen_width, &screen_height) < 0) { std::fprintf(stderr, "%s: could not determine screen size, assuming 80x24\n", argv[0]); screen_width = 80; screen_height = 24; } } if ((sd = client_connect(argv[0], servname, servport, cdowner, cdkey, clienttag, ignoreversion, &saddr, &sessionkey, &sessionnum, ARCHTAG_WINX86, CLIENT_AUTH_INFO_GAMELANG)) < 0) { std::fprintf(stderr, "%s: fatal error during handshake\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } /* reuse this same packet over and over */ if (!(rpacket = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(sd); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(sd); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_fileinforeq)); packet_set_type(packet, CLIENT_FILEINFOREQ); bn_int_set(&packet->u.client_fileinforeq.type, CLIENT_FILEINFOREQ_TYPE_TOS); bn_int_set(&packet->u.client_fileinforeq.unknown2, CLIENT_FILEINFOREQ_UNKNOWN2); packet_append_string(packet, CLIENT_FILEINFOREQ_FILE_TOSUSA); client_blocksend_packet(sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); psock_close(sd); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } while (packet_get_type(rpacket) != SERVER_FILEINFOREPLY); dprintf("Got FILEINFOREPLY\n"); if (!gotplayer) { munged = 1; commpos = 0; text[0] = '\0'; } for (;;) { unsigned int i; if (!gotplayer) { switch (client_get_comm("player: ", text, sizeof(text), &commpos, 1, munged, screen_width)) { case -1: /* cancel or error */ break; case 0: /* timeout */ munged = 0; continue; case 1: munged = 0; if (text[0] == '\0') continue; } if (text[0] == '\0') break; std::printf("\r"); for (i = 0; i < std::strlen(text) && i < screen_width; i++) std::printf(" "); std::printf("\r"); } if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", argv[0]); psock_close(sd); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } packet_set_size(packet, sizeof(t_client_statsreq)); packet_set_type(packet, CLIENT_STATSREQ); bn_int_set(&packet->u.client_statsreq.name_count, 1); bn_int_set(&packet->u.client_statsreq.requestid, 0); packet_append_string(packet, text); count = 0; if (use_bnetd) { packet_append_string(packet, "BNET\\acct\\username"); packet_append_string(packet, "BNET\\acct\\userid"); packet_append_string(packet, "BNET\\acct\\lastlogin_clienttag"); packet_append_string(packet, "BNET\\acct\\lastlogin_connection"); packet_append_string(packet, "BNET\\acct\\lastlogin_time"); packet_append_string(packet, "BNET\\acct\\firstlogin_clienttag"); packet_append_string(packet, "BNET\\acct\\firstlogin_connection"); packet_append_string(packet, "BNET\\acct\\firstlogin_time"); count += 8; } if (use_fsgs) { packet_append_string(packet, "FSGS\\Created"); count += 1; } packet_append_string(packet, "profile\\sex"); packet_append_string(packet, "profile\\age"); packet_append_string(packet, "profile\\location"); packet_append_string(packet, "profile\\description"); count += 4; if (std::strcmp(clienttag, "STAR") == 0) { packet_append_string(packet, "Record\\STAR\\0\\last game"); packet_append_string(packet, "Record\\STAR\\0\\last game result"); packet_append_string(packet, "Record\\STAR\\0\\wins"); packet_append_string(packet, "Record\\STAR\\0\\losses"); packet_append_string(packet, "Record\\STAR\\0\\disconnects"); packet_append_string(packet, "Record\\STAR\\0\\draws"); count += 6; packet_append_string(packet, "Record\\STAR\\1\\last game"); packet_append_string(packet, "Record\\STAR\\1\\last game result"); packet_append_string(packet, "Record\\STAR\\1\\rating"); packet_append_string(packet, "Record\\STAR\\1\\active rating"); packet_append_string(packet, "Record\\STAR\\1\\high rating"); packet_append_string(packet, "Record\\STAR\\1\\rank"); packet_append_string(packet, "Record\\STAR\\1\\active rank"); packet_append_string(packet, "Record\\STAR\\1\\high rank"); packet_append_string(packet, "Record\\STAR\\1\\wins"); packet_append_string(packet, "Record\\STAR\\1\\losses"); packet_append_string(packet, "Record\\STAR\\1\\disconnects"); packet_append_string(packet, "Record\\STAR\\1\\draws"); count += 12; } else if (std::strcmp(clienttag, "SEXP") == 0) { packet_append_string(packet, "Record\\SEXP\\0\\last game"); packet_append_string(packet, "Record\\SEXP\\0\\last game result"); packet_append_string(packet, "Record\\SEXP\\0\\wins"); packet_append_string(packet, "Record\\SEXP\\0\\losses"); packet_append_string(packet, "Record\\SEXP\\0\\disconnects"); packet_append_string(packet, "Record\\SEXP\\0\\draws"); count += 6; packet_append_string(packet, "Record\\SEXP\\1\\last game"); packet_append_string(packet, "Record\\SEXP\\1\\last game result"); packet_append_string(packet, "Record\\SEXP\\1\\rating"); packet_append_string(packet, "Record\\SEXP\\1\\active rating"); packet_append_string(packet, "Record\\SEXP\\1\\high rating"); packet_append_string(packet, "Record\\SEXP\\1\\rank"); packet_append_string(packet, "Record\\SEXP\\1\\active rank"); packet_append_string(packet, "Record\\SEXP\\1\\high rank"); packet_append_string(packet, "Record\\SEXP\\1\\wins"); packet_append_string(packet, "Record\\SEXP\\1\\losses"); packet_append_string(packet, "Record\\SEXP\\1\\disconnects"); packet_append_string(packet, "Record\\SEXP\\1\\draws"); count += 12; } else if (std::strcmp(clienttag, "SSHR") == 0) { packet_append_string(packet, "Record\\SSHR\\0\\last game"); packet_append_string(packet, "Record\\SSHR\\0\\last game result"); packet_append_string(packet, "Record\\SSHR\\0\\wins"); packet_append_string(packet, "Record\\SSHR\\0\\losses"); packet_append_string(packet, "Record\\SSHR\\0\\disconnects"); packet_append_string(packet, "Record\\SSHR\\0\\draws"); count += 6; } else if (std::strcmp(clienttag, "DSHR") == 0 || std::strcmp(clienttag, "DRTL") == 0) { if (use_bnetd) { packet_append_string(packet, "BNET\\Record\\DRTL\\0\\level"); packet_append_string(packet, "BNET\\Record\\DRTL\\0\\class"); packet_append_string(packet, "BNET\\Record\\DRTL\\0\\strength"); packet_append_string(packet, "BNET\\Record\\DRTL\\0\\magic"); packet_append_string(packet, "BNET\\Record\\DRTL\\0\\dexterity"); packet_append_string(packet, "BNET\\Record\\DRTL\\0\\vitality"); packet_append_string(packet, "BNET\\Record\\DRTL\\0\\gold"); packet_append_string(packet, "BNET\\Record\\DRTL\\0\\diablo kills"); count += 8; } } else if (std::strcmp(clienttag, "W2BN") == 0) { packet_append_string(packet, "Record\\W2BN\\0\\last game"); packet_append_string(packet, "Record\\W2BN\\0\\last game result"); packet_append_string(packet, "Record\\W2BN\\0\\wins"); packet_append_string(packet, "Record\\W2BN\\0\\losses"); packet_append_string(packet, "Record\\W2BN\\0\\disconnects"); packet_append_string(packet, "Record\\W2BN\\0\\draws"); count += 6; packet_append_string(packet, "Record\\W2BN\\1\\last game"); packet_append_string(packet, "Record\\W2BN\\1\\last game result"); packet_append_string(packet, "Record\\W2BN\\1\\rating"); packet_append_string(packet, "Record\\W2BN\\1\\active rating"); packet_append_string(packet, "Record\\W2BN\\1\\high rating"); packet_append_string(packet, "Record\\W2BN\\1\\rank"); packet_append_string(packet, "Record\\W2BN\\1\\active rank"); packet_append_string(packet, "Record\\W2BN\\1\\high rank"); packet_append_string(packet, "Record\\W2BN\\1\\wins"); packet_append_string(packet, "Record\\W2BN\\1\\losses"); packet_append_string(packet, "Record\\W2BN\\1\\disconnects"); packet_append_string(packet, "Record\\W2BN\\1\\draws"); count += 12; packet_append_string(packet, "Record\\W2BN\\3\\last game"); packet_append_string(packet, "Record\\W2BN\\3\\last game result"); packet_append_string(packet, "Record\\W2BN\\3\\rating"); packet_append_string(packet, "Record\\W2BN\\3\\active rating"); packet_append_string(packet, "Record\\W2BN\\3\\high rating"); packet_append_string(packet, "Record\\W2BN\\3\\rank"); packet_append_string(packet, "Record\\W2BN\\3\\active rank"); packet_append_string(packet, "Record\\W2BN\\3\\high rank"); packet_append_string(packet, "Record\\W2BN\\3\\wins"); packet_append_string(packet, "Record\\W2BN\\3\\losses"); packet_append_string(packet, "Record\\W2BN\\3\\disconnects"); packet_append_string(packet, "Record\\W2BN\\3\\draws"); count += 12; } bn_int_set(&packet->u.client_statsreq.key_count, count); client_blocksend_packet(sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", argv[0]); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_FAILURE; } while (packet_get_type(rpacket) != SERVER_STATSREPLY); dprintf("Got STATSREPLY\n"); { unsigned int names, keys; unsigned int j; unsigned int strpos; char const * temp; unsigned int uid; struct in_addr laddr; std::time_t tm; char timestr[STAT_TIME_MAXLEN]; names = bn_int_get(rpacket->u.server_statsreply.name_count); keys = bn_int_get(rpacket->u.server_statsreply.key_count); std::printf("----\n"); strpos = sizeof(t_server_statsreply); for (i = 0; i < names; i++) { j = 0; if (use_bnetd) { if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (temp[0] == '\0') std::printf(" Username: UNKNOWN\n"); else std::printf(" Username: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &uid) < 0 || uid < 1) std::printf(" Account: UNKNOWN\n"); else std::printf(" Account: " UID_FORMATF "\n", uid); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" Last login client: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &val) < 0) std::printf(" Last login host: UNKNOWN\n"); else { laddr.s_addr = htonl(val); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(laddr), addrstr, sizeof(addrstr)); std::printf(" Last login host: %s\n", addrstr); } if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &val) < 0 || val == 0) std::printf(" Last login time: NEVER\n"); else { tm = (std::time_t)val; std::strftime(timestr, STAT_TIME_MAXLEN, STAT_TIME_FORMAT, std::localtime(&tm)); std::printf(" Last login time: %s\n", timestr); } if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" First login client: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &val) < 0) std::printf(" First login host: UNKNOWN\n"); else { laddr.s_addr = htonl(val); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(laddr), addrstr, sizeof(addrstr)); std::printf(" First login host: %s\n", addrstr); } if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &val) < 0 || val == 0) std::printf(" First login time: NEVER\n"); else { tm = (std::time_t)val; std::strftime(timestr, STAT_TIME_MAXLEN, STAT_TIME_FORMAT, std::localtime(&tm)); std::printf(" First login time: %s\n", timestr); } } else std::printf(" Username: %s\n", text); if (use_fsgs) { if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &val) < 0 || val == 0) std::printf(" Creation time: NEVER\n"); else { tm = (std::time_t)val; std::strftime(timestr, STAT_TIME_MAXLEN, STAT_TIME_FORMAT, std::localtime(&tm)); std::printf(" Creation time: %s\n", timestr); } } if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" Sex: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" Age: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" Location: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" Description: %s\n", temp); if (std::strcmp(clienttag, "STAR") == 0 || std::strcmp(clienttag, "SSHR") == 0 || std::strcmp(clienttag, "SEXP") == 0 || std::strcmp(clienttag, "W2BN") == 0) { t_bnettime bntime; unsigned int wins, losses, disconnects, draws; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (bnettime_set_str(&bntime, temp) < 0) std::strcpy(timestr, "NEVER"); else { tm = bnettime_to_time(bntime); std::strftime(timestr, STAT_TIME_MAXLEN, STAT_TIME_FORMAT, std::localtime(&tm)); } std::printf(" Last game time: %s\n", timestr); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" Last game result: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &wins) < 0) wins = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &losses) < 0) losses = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &disconnects) < 0) disconnects = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &draws) < 0) draws = 0; std::printf(" Record: (%u/%u/%u) %u draws\n", wins, losses, disconnects, draws); } if (std::strcmp(clienttag, "STAR") == 0 || std::strcmp(clienttag, "SEXP") == 0 || std::strcmp(clienttag, "W2BN") == 0) { t_bnettime bntime; unsigned int wins, losses, disconnects, draws; unsigned int rating, rank; unsigned int active_rating, active_rank; unsigned int high_rating, high_rank; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (bnettime_set_str(&bntime, temp) < 0) std::strcpy(timestr, "NEVER"); else { tm = bnettime_to_time(bntime); std::strftime(timestr, STAT_TIME_MAXLEN, STAT_TIME_FORMAT, std::localtime(&tm)); } std::printf(" Last standard ladder game time: %s\n", timestr); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf("Last standard ladder game result: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &rating) < 0) rating = 0; if (rating) std::printf(" Current standard ladder rating: %u\n", rating); else std::printf(" Current standard ladder rating: UNRATED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &active_rating) < 0) active_rating = 0; if (active_rating) std::printf(" Active standard ladder rating: %u\n", active_rating); else std::printf(" Active standard ladder rating: UNRATED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &high_rating) < 0) high_rating = 0; if (high_rating) std::printf(" High standard ladder rating: %u\n", high_rating); else std::printf(" High standard ladder rating: UNRATED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &rank) < 0) rank = 0; if (rank) std::printf(" Current standard ladder rank: #%u\n", rank); else std::printf(" Current standard ladder rank: UNRANKED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &active_rank) < 0) active_rank = 0; if (active_rank) std::printf(" Active standard ladder rank: #%u\n", active_rank); else std::printf(" Active standard ladder rank: UNRANKED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &high_rank) < 0) high_rank = 0; if (high_rank) std::printf(" High standard ladder rank: #%u\n", high_rank); else std::printf(" High standard ladder rank: UNRANKED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &wins) < 0) wins = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &losses) < 0) losses = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &disconnects) < 0) disconnects = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &draws) < 0) draws = 0; std::printf(" Standard ladder record: (%u/%u/%u) %u draws\n", wins, losses, disconnects, draws); } if (std::strcmp(clienttag, "W2BN") == 0) { t_bnettime bntime; unsigned int wins, losses, disconnects, draws; unsigned int rating, rank; unsigned int active_rating, active_rank; unsigned int high_rating, high_rank; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (bnettime_set_str(&bntime, temp) < 0) std::strcpy(timestr, "NEVER"); else { tm = bnettime_to_time(bntime); std::strftime(timestr, STAT_TIME_MAXLEN, STAT_TIME_FORMAT, std::localtime(&tm)); } std::printf(" Last Ironman ladder game time: %s\n", timestr); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; std::printf(" Last Ironman ladder game result: %s\n", temp); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &rating) < 0) rating = 0; if (rating) std::printf(" Current Ironman ladder rating: %u\n", rating); else std::printf(" Current Ironman ladder rating: UNRATED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &active_rating) < 0) active_rating = 0; if (active_rating) std::printf(" Active Ironman ladder rating: %u\n", active_rating); else std::printf(" Active Ironman ladder rating: UNRATED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &high_rating) < 0) high_rating = 0; if (high_rating) std::printf(" High Ironman ladder rating: %u\n", high_rating); else std::printf(" High Ironman ladder rating: UNRATED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &rank) < 0) rank = 0; if (rank) std::printf(" Current Ironman ladder rank: #%u\n", rank); else std::printf(" Current Ironman ladder rank: UNRANKED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &active_rank) < 0) active_rank = 0; if (active_rank) std::printf(" Active Ironman ladder rank: #%u\n", active_rank); else std::printf(" Active Ironman ladder rank: UNRANKED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &high_rank) < 0) high_rank = 0; if (high_rank) std::printf(" High Ironman ladder rank: #%u\n", high_rank); else std::printf(" High Ironman ladder rank: UNRANKED\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &wins) < 0) wins = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &losses) < 0) losses = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &disconnects) < 0) disconnects = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &draws) < 0) draws = 0; std::printf(" Ironman ladder record: (%u/%u/%u) %u draws\n", wins, losses, disconnects, draws); } if (std::strcmp(clienttag, "DSHR") == 0 || std::strcmp(clienttag, "DRTL") == 0) { unsigned int level; unsigned int chclass; unsigned int diablo_kills; unsigned int strength; unsigned int magic; unsigned int dexterity; unsigned int vitality; unsigned int gold; if (use_bnetd) { if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &level)<0) level = 0; if (level>0) std::printf(" Level: %u\n", level); else std::printf(" Level: NONE\n"); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &chclass) < 0) chclass = 99; std::printf(" Class: %s\n", bnclass_get_str(chclass)); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &strength) < 0) strength = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &magic) < 0) magic = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &dexterity) < 0) dexterity = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &vitality) < 0) vitality = 0; if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &gold) < 0) gold = 0; std::printf(" Stats: %u str %u mag %u dex %u vit %u gld\n", strength, magic, dexterity, vitality, gold); if (j < keys && (temp = packet_get_str_const(rpacket, strpos, 256))) strpos += std::strlen(temp) + 1; else temp = ""; j++; if (str_to_uint(temp, &diablo_kills) < 0) diablo_kills = 0; std::printf(" Diablo Kills: %u\n", diablo_kills); } } } std::printf("----\n"); } if (gotplayer) break; commpos = 0; text[0] = '\0'; } psock_close(sd); if (changed_in) tcsetattr(fd_stdin, TCSAFLUSH, &in_attr_old); return EXIT_SUCCESS; } pvpgn-1.99.7.2.1+dfsg/src/client/client.cpp000066400000000000000000000152641462640461400202370ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Oleg Drokin (green@ccssu.ccssu.crimea.ua) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "client.h" #include #include #include #ifdef WIN32 # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "common/network.h" #include "common/setup_after.h" namespace pvpgn { namespace client { extern int client_blocksend_packet(int sock, t_packet const * packet) { unsigned int size = 0; for (;;) switch (net_send_packet(sock, packet, &size)) { case -1: return -1; case 0: continue; default: return 0; } } extern int client_blockrecv_packet(int sock, t_packet * packet) { unsigned int size = 0; for (;;) switch (net_recv_packet(sock, packet, &size)) { case -1: return -1; case 0: continue; default: /* std::printf("Got packet len %u/%u type 0x%04x\n",size,packet_get_size(packet),packet_get_type(packet)); */ return 0; } } extern int client_blockrecv_raw_packet(int sock, t_packet * packet, unsigned int len) { unsigned int size = 0; packet_set_size(packet, len); for (;;) switch (net_recv_packet(sock, packet, &size)) { case -1: return -1; case 0: continue; case 1: default: return 0; } } extern int client_get_termsize(int fd, unsigned int * w, unsigned int * h) { if (fd < 0 || !w || !h) return -1; *w = 0; *h = 0; #ifdef HAVE_IOCTL #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl(fd, TIOCGWINSZ, &ws) >= 0) { *w = (unsigned int)ws.ws_col; *h = (unsigned int)ws.ws_row; } } #endif #endif { char const * str; int val; if (!*w && (str = std::getenv("COLUMNS")) && (val = std::atoi(str)) > 0) *w = val; if (!*h && (str = std::getenv("LINES")) && (val = std::atoi(str)) > 0) *h = val; } if (!*w) *w = DEF_SCREEN_WIDTH; if (!*h) *h = DEF_SCREEN_HEIGHT; return 0; } /* This routine std::gets keyboard input. It handles printing the prompt, cursor positioning, and text scrolling. It unfortunatly assumes that the chars read from stdin are in ASCII. */ /* visible: -1=nothing, 0=prompt only, 1=prompt and text */ extern int client_get_comm(char const * prompt, char * text, unsigned int maxlen, unsigned int * curpos, int visible, int redraw, unsigned int width) { char temp; unsigned int addlen; unsigned int i; unsigned int count; int beg_pos; for (count = 0; count < 16; count++) { beg_pos = 0; if (std::strlen(prompt) + 1 >= width) visible = 0; /* no room to show any of the text */ else if (*curpos + std::strlen(prompt) + 1 >= width) beg_pos = (int)(*curpos + std::strlen(prompt) + 1 - width); if (redraw) { if (visible != -1) std::printf("\r%s", prompt); if (visible == 1) std::printf("%s", text + beg_pos); } std::fflush(stdout); #ifndef WIN32 addlen = read(fileno(stdin), &temp, 1); #else if (kbhit()) { temp = getch(); addlen = 1; } else { temp = 0; addlen = 0; } #endif if (addlen < 1) return addlen; switch (temp) { case '\033': /* ESC */ return -1; case '\r': case '\n': return 1; case '\b': /* BS */ case '\177': /* DEL */ if (*curpos<1) /* already empty */ { if (visible == 1) std::printf("\a"); continue; } (*curpos)--; text[*curpos] = '\0'; if (visible == 1) { if (beg_pos>0) { beg_pos--; std::printf("\r%s%s", prompt, text + beg_pos); } else { std::printf("\b \b"); } } continue; case '\024': /* ^T */ if (*curpos < 2) { if (visible == 1) std::printf("\a"); continue; } { char swap; swap = text[*curpos - 1]; text[*curpos - 1] = text[*curpos - 2]; text[*curpos - 2] = swap; if (visible == 1) { std::printf("\b\b"); std::printf("%s", &text[*curpos - 2]); } } continue; case '\027': /* ^W */ if (*curpos < 1) { if (visible == 1) std::printf("\a"); continue; } { char * t = std::strrchr(text, ' '); unsigned int t1 = beg_pos, t2; addlen = t ? t - text : 0; text[addlen] = '\0'; beg_pos -= *curpos - addlen; if (beg_pos < 0) beg_pos = 0; if (visible == 1) { if (t1 == 0) for (i = 0; i < *curpos - addlen; i++) std::printf("\b \b"); else { /* the \r is counted in the return value, but that's ok because the last column is always blank */ t2 = std::printf("\r%s%s", prompt, text + beg_pos); if (t1 > 0 && beg_pos == 0) { for (i = 0; i < width - t2; i++) std::printf(" "); for (i = 0; i < width - t2; i++) std::printf("\b"); } } } } *curpos = addlen; continue; case '\025': /* ^U */ if (visible == 1) { unsigned int t2; t2 = std::printf("\r%s", prompt); for (i = 0; i0 && temp < 32) /* unhandled control char */ { if (visible == 1) std::printf("\a"); continue; } if ((*curpos + 1) >= maxlen) /* too full */ { if (visible == 1) std::printf("\a"); continue; } if (visible == 1) { if (beg_pos > 0 || (*curpos + std::strlen(prompt) + 2 > width)) { beg_pos++; std::printf("\r%s%s", prompt, text + beg_pos); } std::printf("%c", temp); } text[*curpos] = temp; (*curpos)++; text[*curpos] = '\0'; continue; } } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/client/client.h000066400000000000000000000026171462640461400177020ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_CLIENT_PROTOS #define INCLUDED_CLIENT_PROTOS #include "common/packet.h" namespace pvpgn { namespace client { extern int client_blocksend_packet(int sock, t_packet const * packet); extern int client_blockrecv_packet(int sock, t_packet * packet); extern int client_blockrecv_raw_packet(int sock, t_packet * packet, unsigned int len); extern int client_get_termsize(int fd, unsigned int * w, unsigned int * h); extern int client_get_comm(char const * prompt, char * text, unsigned int maxlen, unsigned int * curpos, int visible, int redraw, unsigned int width); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/client/client_connect.cpp000066400000000000000000000377611462640461400217560ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "client_connect.h" #include #include #include #include #include "compat/gethostname.h" #include "common/tag.h" #include "common/packet.h" #include "common/init_protocol.h" #include "common/bnet_protocol.h" #include "common/bn_type.h" #include "common/bnethash.h" #include "common/bnethashconv.h" #include "common/bnettime.h" #ifdef CLIENTDEBUG # include "common/eventlog.h" #endif #include "udptest.h" #include "client.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" #define COMPNAMELEN 128 #ifdef CLIENTDEBUG # define dprintf printf #else # define dprintf if (0) printf #endif namespace { int key_interpret(char const * cdkey, unsigned int * productid, unsigned int * keyvalue1, unsigned int * keyvalue2) { /* FIXME: implement... have example code from Eurijk! */ *productid = 0; *keyvalue1 = 0; *keyvalue2 = 0; return 0; } int get_defversioninfo(char const * progname, char const * clienttag, unsigned int * versionid, unsigned int * gameversion, char const * * exeinfo, unsigned int * checksum) { if (std::strcmp(clienttag, CLIENTTAG_DIABLORTL) == 0) { *versionid = CLIENT_VERSIONID_DRTL; *gameversion = CLIENT_GAMEVERSION_DRTL; *exeinfo = CLIENT_EXEINFO_DRTL; *checksum = CLIENT_CHECKSUM_DRTL; return 0; } if (std::strcmp(clienttag, CLIENTTAG_STARCRAFT) == 0) { *versionid = CLIENT_VERSIONID_STAR; *gameversion = CLIENT_GAMEVERSION_STAR; *exeinfo = CLIENT_EXEINFO_STAR; *checksum = CLIENT_CHECKSUM_STAR; return 0; } if (std::strcmp(clienttag, CLIENTTAG_SHAREWARE) == 0) { *versionid = CLIENT_VERSIONID_SSHR; *gameversion = CLIENT_GAMEVERSION_SSHR; *exeinfo = CLIENT_EXEINFO_SSHR; *checksum = CLIENT_CHECKSUM_SSHR; return 0; } if (std::strcmp(clienttag, CLIENTTAG_BROODWARS) == 0) { *versionid = CLIENT_VERSIONID_SEXP; *gameversion = CLIENT_GAMEVERSION_SEXP; *exeinfo = CLIENT_EXEINFO_SEXP; *checksum = CLIENT_CHECKSUM_SEXP; return 0; } if (std::strcmp(clienttag, CLIENTTAG_WARCIIBNE) == 0) { *versionid = CLIENT_VERSIONID_W2BN; *gameversion = CLIENT_GAMEVERSION_W2BN; *exeinfo = CLIENT_EXEINFO_W2BN; *checksum = CLIENT_CHECKSUM_W2BN; return 0; } if (std::strcmp(clienttag, CLIENTTAG_DIABLO2DV) == 0) { *versionid = CLIENT_VERSIONID_D2DV; *gameversion = CLIENT_GAMEVERSION_D2DV; *exeinfo = CLIENT_EXEINFO_D2DV; *checksum = CLIENT_CHECKSUM_D2DV; return 0; } if (std::strcmp(clienttag, CLIENTTAG_DIABLO2XP) == 0) { *versionid = CLIENT_VERSIONID_D2XP; *gameversion = CLIENT_GAMEVERSION_D2XP; *exeinfo = CLIENT_EXEINFO_D2XP; *checksum = CLIENT_CHECKSUM_D2XP; return 0; } if (std::strcmp(clienttag, CLIENTTAG_WARCRAFT3) == 0 || std::strcmp(clienttag, CLIENTTAG_WAR3XP) == 0) { *versionid = CLIENT_VERSIONID_WAR3; *gameversion = CLIENT_GAMEVERSION_WAR3; *exeinfo = CLIENT_EXEINFO_WAR3; *checksum = CLIENT_CHECKSUM_WAR3; return 0; } *versionid = 0; *gameversion = 0; *exeinfo = ""; *checksum = 0; if (std::strcmp(clienttag, CLIENTTAG_BNCHATBOT) == 0) return 0; std::fprintf(stderr, "%s: unsupported clienttag \"%s\"\n", progname, clienttag); // aaron: dunno what we should return in case of this.. but returning nothing was definetly wrong return -1; } } namespace pvpgn { namespace client { extern int client_connect(char const * progname, char const * servname, unsigned short servport, char const * cdowner, char const * cdkey, char const * clienttag, int ignoreversion, struct sockaddr_in * saddr, unsigned int * sessionkey, unsigned int * sessionnum, char const * archtag, char const * gamelang) { struct hostent * host; char const * username; int sd; t_packet * packet; t_packet * rpacket; char compname[COMPNAMELEN]; int lsock; unsigned short lsock_port; unsigned int versionid; unsigned int gameversion; char const * exeinfo; unsigned int checksum; if (!progname) { std::fprintf(stderr, "got NULL progname\n"); return -1; } if (!saddr) { std::fprintf(stderr, "%s: got NULL saddr\n", progname); return -1; } if (!sessionkey) { std::fprintf(stderr, "%s: got NULL sessionkey\n", progname); return -1; } if (!sessionnum) { std::fprintf(stderr, "%s: got NULL sessionnum\n", progname); return -1; } if (psock_init() < 0) { std::fprintf(stderr, "%s: could not inialialize socket functions\n", progname); return -1; } if (!(host = gethostbyname(servname))) { std::fprintf(stderr, "%s: unknown host \"%s\"\n", progname, servname); return -1; } if (host->h_addrtype != PSOCK_AF_INET) std::fprintf(stderr, "%s: host \"%s\" is not in IPv4 address family\n", progname, servname); if (gethostname(compname, COMPNAMELEN) < 0) { std::fprintf(stderr, "%s: could not get host name (gethostname: %s)\n", progname, std::strerror(errno)); return -1; } #ifdef HAVE_GETLOGIN if (!(username = getlogin())) #endif { username = "unknown"; dprintf("%s: could not get login name, using \"%s\" (getlogin: %s)\n", progname, username, std::strerror(errno)); } if ((sd = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP)) < 0) { std::fprintf(stderr, "%s: could not create socket (psock_socket: %s)\n", progname, std::strerror(psock_errno())); return -1; } std::memset(saddr, 0, sizeof(*saddr)); saddr->sin_family = PSOCK_AF_INET; saddr->sin_port = htons(servport); std::memcpy(&saddr->sin_addr.s_addr, host->h_addr_list[0], host->h_length); if (psock_connect(sd, (struct sockaddr *)saddr, sizeof(*saddr)) < 0) { std::fprintf(stderr, "%s: could not connect to server \"%s\" port %hu (psock_connect: %s)\n", progname, servname, servport, std::strerror(psock_errno())); psock_close(sd); return -1; } char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(saddr->sin_addr), addrstr, sizeof(addrstr)); std::printf("Connected to %s:%hu.\n", addrstr, servport); #ifdef CLIENTDEBUG eventlog_set(stderr); #endif if ((lsock = client_udptest_setup(progname, &lsock_port)) >= 0) dprintf("Got UDP data on port %hu\n", lsock_port); if (!(packet = packet_create(packet_class_init))) { std::fprintf(stderr, "%s: could not create packet\n", progname); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } bn_byte_set(&packet->u.client_initconn.cclass, CLIENT_INITCONN_CLASS_BNET); client_blocksend_packet(sd, packet); packet_del_ref(packet); /* reuse this same packet over and over */ if (!(rpacket = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", progname); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } get_defversioninfo(progname, clienttag, &versionid, &gameversion, &exeinfo, &checksum); if (std::strcmp(clienttag, CLIENTTAG_DIABLOSHR) == 0 || std::strcmp(clienttag, CLIENTTAG_DIABLORTL) == 0) { if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } packet_set_size(packet, sizeof(t_client_unknown_1b)); packet_set_type(packet, CLIENT_UNKNOWN_1B); bn_short_set(&packet->u.client_unknown_1b.unknown1, CLIENT_UNKNOWN_1B_UNKNOWN3); bn_short_nset(&packet->u.client_unknown_1b.port, lsock_port); bn_int_nset(&packet->u.client_unknown_1b.ip, 0); /* FIXME */ bn_int_set(&packet->u.client_unknown_1b.unknown2, CLIENT_UNKNOWN_1B_UNKNOWN3); bn_int_set(&packet->u.client_unknown_1b.unknown3, CLIENT_UNKNOWN_1B_UNKNOWN3); client_blocksend_packet(sd, packet); packet_del_ref(packet); } if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } packet_set_size(packet, sizeof(t_client_auth_info)); packet_set_type(packet, CLIENT_AUTH_INFO); bn_int_set(&packet->u.client_auth_info.protocol, CLIENT_AUTH_INFO_PROTOCOL); bn_int_tag_set(&packet->u.client_auth_info.archtag, archtag); bn_int_tag_set(&packet->u.client_auth_info.clienttag, clienttag); //AARON bn_int_set(&packet->u.client_auth_info.versionid, versionid); bn_int_tag_set(&packet->u.client_auth_info.gamelang, gamelang); bn_int_set(&packet->u.client_auth_info.localip, CLIENT_AUTH_INFO_LOCALIP); { int bias; bias = local_tzbias(); bn_int_set(&packet->u.client_auth_info.bias, (unsigned int)bias); /* rely on 2's complement */ dprintf("my tzbias = %d (0x%08hx)\n", bias, (unsigned int)bias); } /* FIXME: determine from locale */ bn_int_set(&packet->u.client_auth_info.lcid, CLIENT_AUTH_INFO_LANGID_USENGLISH); bn_int_set(&packet->u.client_auth_info.langid, CLIENT_AUTH_INFO_LANGID_USENGLISH); packet_append_string(packet, CLIENT_AUTH_INFO_LANGSTR_USENGLISH); /* FIXME: determine from locale+timezone... from domain name... nothing really would work. Maybe add some command-line options */ packet_append_string(packet, CLIENT_AUTH_INFO_COUNTRYNAME_USA); client_blocksend_packet(sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } while (packet_get_type(rpacket) != SERVER_AUTHREQ_109 && packet_get_type(rpacket) != SERVER_AUTHREPLY_109); if (packet_get_type(rpacket) == SERVER_AUTHREQ_109) /* hmm... server wants to check the version number */ { dprintf("Got AUTHREQ_109\n"); *sessionkey = bn_int_get(rpacket->u.server_authreq_109.sessionkey); *sessionnum = bn_int_get(rpacket->u.server_authreq_109.sessionnum); /* FIXME: also get filename and equation */ if (!ignoreversion) { if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } packet_set_size(packet, sizeof(t_client_authreq_109)); packet_set_type(packet, CLIENT_AUTHREQ_109); bn_int_set(&packet->u.client_authreq_109.ticks, std::time(NULL)); { t_cdkey_info cdkey_info = {}; bn_int_set(&packet->u.client_authreq_109.gameversion, gameversion); bn_int_set(&packet->u.client_authreq_109.cdkey_number, 1); /* only one */ /* FIXME: put the input cdkey here */ packet_append_data(packet, &cdkey_info, sizeof(cdkey_info)); packet_append_string(packet, exeinfo); packet_append_string(packet, cdowner); } bn_int_set(&packet->u.client_authreq_109.spawn, 0x0000); bn_int_set(&packet->u.client_authreq_109.checksum, checksum); client_blocksend_packet(sd, packet); packet_del_ref(packet); /* now wait for reply */ do if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } while (packet_get_type(rpacket) != SERVER_AUTHREPLY_109); //FIXME: check if AUTHREPLY_109 is success or fail if (bn_int_get(rpacket->u.server_authreply_109.message) != SERVER_AUTHREPLY_109_MESSAGE_OK) { std::fprintf(stderr, "AUTHREPLY_109 failed - closing connection\n"); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } } } else std::fprintf(stderr, "We didn't get a sessionkey, don't expect login to work!"); dprintf("Got AUTHREPLY_109\n"); if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } packet_set_size(packet, sizeof(t_client_iconreq)); packet_set_type(packet, CLIENT_ICONREQ); client_blocksend_packet(sd, packet); packet_del_ref(packet); do if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } while (packet_get_type(rpacket) != SERVER_ICONREPLY); dprintf("Got ICONREPLY\n"); if (std::strcmp(clienttag, CLIENTTAG_STARCRAFT) == 0 || std::strcmp(clienttag, CLIENTTAG_BROODWARS) == 0 || std::strcmp(clienttag, CLIENTTAG_WARCIIBNE) == 0) { if (!(packet = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } { struct { bn_int sessionkey; bn_int ticks; bn_int productid; bn_int keyvalue1; bn_int keyvalue2; } temp; t_hash key_hash; unsigned int ticks; unsigned int keyvalue1, keyvalue2, productid; if (key_interpret(cdkey, &productid, &keyvalue1, &keyvalue2) < 0) { std::fprintf(stderr, "%s: specified key is not valid, sending junk\n", progname); productid = 0; keyvalue1 = 0; keyvalue2 = 0; } ticks = 0; /* FIXME: what to use here? */ bn_int_set(&temp.ticks, ticks); bn_int_set(&temp.sessionkey, *sessionkey); bn_int_set(&temp.productid, productid); bn_int_set(&temp.keyvalue1, keyvalue1); bn_int_set(&temp.keyvalue2, keyvalue2); bnet_hash(&key_hash, sizeof(temp), &temp); packet_set_size(packet, sizeof(t_client_cdkey2)); packet_set_type(packet, CLIENT_CDKEY2); bn_int_set(&packet->u.client_cdkey2.spawn, CLIENT_CDKEY2_SPAWN_FALSE); /* FIXME: add option */ bn_int_set(&packet->u.client_cdkey2.keylen, std::strlen(cdkey)); bn_int_set(&packet->u.client_cdkey2.productid, productid); bn_int_set(&packet->u.client_cdkey2.keyvalue1, keyvalue1); bn_int_set(&packet->u.client_cdkey2.sessionkey, *sessionkey); bn_int_set(&packet->u.client_cdkey2.ticks, ticks); hash_to_bnhash((t_hash const *)&key_hash, packet->u.client_cdkey2.key_hash); /* avoid warning */ packet_append_string(packet, cdowner); client_blocksend_packet(sd, packet); packet_del_ref(packet); } do if (client_blockrecv_packet(sd, rpacket) < 0) { std::fprintf(stderr, "%s: server closed connection\n", progname); packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); psock_close(sd); return -1; } while (packet_get_type(rpacket) != SERVER_CDKEYREPLY2); dprintf("Got CDKEYREPLY2 (%u)\n", bn_int_get(rpacket->u.server_cdkeyreply2.message)); } packet_destroy(rpacket); if (lsock >= 0) psock_close(lsock); return sd; } } } pvpgn-1.99.7.2.1+dfsg/src/client/client_connect.h000066400000000000000000000067321462640461400214150ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_CLIENT_CONNECT_PROTOS #define INCLUDED_CLIENT_CONNECT_PROTOS // Diablo 1.09b #define CLIENT_VERSIONID_DRTL 0x0000002a #define CLIENT_GAMEVERSION_DRTL 0x01000902 #define CLIENT_EXEINFO_DRTL "Diablo.exe 05/18/01 23:10:57 767760" #define CLIENT_CHECKSUM_DRTL 0x23135c73 //plain Starcraft 1.15.2 #define CLIENT_VERSIONID_STAR 0x000000d1 #define CLIENT_GAMEVERSION_STAR 0x010f0200 #define CLIENT_EXEINFO_STAR "StarCraft.exe 01/08/08 23:45:36 1220608" #define CLIENT_CHECKSUM_STAR 0x91c0f907 #define CLIENT_VERSIONID_SSHR 0x000000a5 /* FIXME: wrong? */ #define CLIENT_GAMEVERSION_SSHR 0x0100080a /* FIXME: wrong? */ #define CLIENT_EXEINFO_SSHR "starcraft.exe 03/08/99 22:41:50 1042432" /* FIXME: wrong */ #define CLIENT_CHECKSUM_SSHR 0x12345678 //Broodwar 1.15.2 #define CLIENT_VERSIONID_SEXP 0x000000d1 #define CLIENT_GAMEVERSION_SEXP 0x010f0201 #define CLIENT_EXEINFO_SEXP "StarCraft.exe 01/10/08 20:23:42 1220608" #define CLIENT_CHECKSUM_SEXP 0x8fbdf18d //Warcraft 2 Battle.Net Edition 2.02b #define CLIENT_VERSIONID_W2BN 0x0000004f #define CLIENT_GAMEVERSION_W2BN 0x02000201 #define CLIENT_EXEINFO_W2BN "Warcraft II BNE.exe 05/21/01 21:52:22 712704" #define CLIENT_CHECKSUM_W2BN 0xff0d4c4a //Diablo 2 1.11b #define CLIENT_VERSIONID_D2DV 0x0000000b #define CLIENT_GAMEVERSION_D2DV 0x01000b00 #define CLIENT_EXEINFO_D2DV "Game.exe 08/17/05 01:11:43 2125824" #define CLIENT_CHECKSUM_D2DV 0x602f8323 //Diable 2 Expansion 1.11b #define CLIENT_VERSIONID_D2XP 0x0000000b #define CLIENT_GAMEVERSION_D2XP 0x01000b00 #define CLIENT_EXEINFO_D2XP "Game.exe 08/17/05 01:12:38 2129920" #define CLIENT_CHECKSUM_D2XP 0xbfc36199 //Warcraft 3 1.21b and Warcraft 3 Frozen Throne 1.21b #define CLIENT_VERSIONID_WAR3 0x00000015 #define CLIENT_GAMEVERSION_WAR3 0x0115019c #define CLIENT_EXEINFO_WAR3 "war3.exe 07/19/07 18:41:12 409660" #define CLIENT_CHECKSUM_WAR3 0x1b735294 #define CLIENT_AUTH_INFO_PROTOCOL 0x00000000 #define CLIENT_AUTH_INFO_VERSIONID_D2DV 0x00000009 #define CLIENT_AUTH_INFO_GAMELANG "enUS" #define CLIENT_AUTH_INFO_LOCALIP 0x00000000 #define CLIENT_AUTH_INFO_LANGID_USENGLISH 0x00000409 #define CLIENT_AUTH_INFO_LANGSTR_USENGLISH "ENU" #define CLIENT_AUTH_INFO_COUNTRYNAME_USA "United States" #include "compat/psock.h" namespace pvpgn { namespace client { extern int client_connect(char const * progname, char const * servname, unsigned short servport, char const * cdowner, char const * cdkey, char const * clienttag, int ignoreversion, struct sockaddr_in * saddr, unsigned int * sessionkey, unsigned int * sessionnum, char const * archtag, char const * gamelang); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/client/udptest.cpp000066400000000000000000000105641462640461400204470ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "udptest.h" #include #include #include #include #include "compat/psock.h" #include "common/packet.h" #include "common/bn_type.h" #include "common/tag.h" #include "common/setup_after.h" #ifdef CLIENTDEBUG #define dprintf printf #else #define dprintf if (0) printf #endif namespace pvpgn { namespace client { extern int client_udptest_setup(char const * progname, unsigned short * lsock_port_ret) { int lsock; struct sockaddr_in laddr; unsigned short lsock_port; if (!progname) { std::fprintf(stderr, "got NULL progname\n"); return -1; } if ((lsock = psock_socket(PF_INET, SOCK_DGRAM, PSOCK_IPPROTO_UDP)) < 0) { std::fprintf(stderr, "%s: could not create UDP socket (psock_socket: %s)\n", progname, std::strerror(psock_errno())); return -1; } if (psock_ctl(lsock, PSOCK_NONBLOCK) < 0) std::fprintf(stderr, "%s: could not set UDP socket to non-blocking mode (psock_ctl: %s)\n", progname, std::strerror(psock_errno())); for (lsock_port = BNETD_MIN_TEST_PORT; lsock_port <= BNETD_MAX_TEST_PORT; lsock_port++) { std::memset(&laddr, 0, sizeof(laddr)); laddr.sin_family = PSOCK_AF_INET; laddr.sin_port = htons(lsock_port); laddr.sin_addr.s_addr = htonl(INADDR_ANY); if (psock_bind(lsock, (struct sockaddr *)&laddr, (psock_t_socklen)sizeof(laddr)) == 0) break; if (lsock_port == BNETD_MIN_TEST_PORT) dprintf("Could not bind to standard UDP port %hu, trying others. (psock_bind: %s)\n", BNETD_MIN_TEST_PORT, std::strerror(psock_errno())); } if (lsock_port > BNETD_MAX_TEST_PORT) { std::fprintf(stderr, "%s: could not bind to any UDP port %hu through %hu (psock_bind: %s)\n", progname, BNETD_MIN_TEST_PORT, BNETD_MAX_TEST_PORT, std::strerror(psock_errno())); psock_close(lsock); return -1; } if (lsock_port_ret) *lsock_port_ret = lsock_port; return lsock; } extern int client_udptest_recv(char const * progname, int lsock, unsigned short lsock_port, unsigned int timeout) { int len; unsigned int count; t_packet * rpacket; std::time_t start; if (!progname) { std::fprintf(stderr, "%s: got NULL progname\n", progname); return -1; } if (!(rpacket = packet_create(packet_class_bnet))) { std::fprintf(stderr, "%s: could not create packet\n", progname); return -1; } start = std::time(NULL); count = 0; while (start + (std::time_t)timeout >= std::time(NULL)) /* timeout after a few seconds from last packet */ { if ((len = psock_recv(lsock, packet_get_raw_data_build(rpacket, 0), MAX_PACKET_SIZE, 0)) < 0) { if (psock_errno() != PSOCK_EAGAIN && psock_errno() != PSOCK_EWOULDBLOCK) std::fprintf(stderr, "%s: failed to receive UDPTEST on port %hu (psock_recv: %s)\n", progname, lsock_port, std::strerror(psock_errno())); continue; } packet_set_size(rpacket, len); if (packet_get_type(rpacket) != SERVER_UDPTEST) { dprintf("Got unexpected UDP packet type %u on port %hu\n", packet_get_type(rpacket), lsock_port); continue; } if (bn_int_tag_eq(rpacket->u.server_udptest.bnettag, BNETTAG) < 0) { std::fprintf(stderr, "%s: got bad UDPTEST packet on port %hu\n", progname, lsock_port); continue; } count++; if (count >= 2) break; start = std::time(NULL); } packet_destroy(rpacket); if (count < 2) { std::printf("Only received %d UDP packets on port %hu. Connection may be slow or firewalled.\n", count, lsock_port); return -1; } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/client/udptest.h000066400000000000000000000021651462640461400201120ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_UDPTEST_PROTOS #define INCLUDED_UDPTEST_PROTOS namespace pvpgn { namespace client { extern int client_udptest_setup(char const * progname, unsigned short * lsock_port_ret); extern int client_udptest_recv(char const * progname, int lsock, unsigned short lsock_port, unsigned int timeout); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/000077500000000000000000000000001462640461400162575ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/common/CMakeLists.txt000066400000000000000000000027361462640461400210270ustar00rootroot00000000000000set(COMMON_SOURCES addr.cpp addr.h anongame_protocol.h asnprintf.cpp asnprintf.h bnethashconv.cpp bnethashconv.h bnethash.cpp bnethash.h bnet_protocol.h bnettime.cpp bnettime.h bn_type.cpp bn_type.h bot_protocol.h conf.cpp conf.h d2char_checksum.cpp d2char_checksum.h d2char_file.h d2cs_bnetd_protocol.h d2cs_d2dbs_ladder.h d2cs_d2gs_character.h d2cs_d2gs_protocol.h d2cs_protocol.h d2game_protocol.h elist.h eventlog.cpp eventlog.h fdwatch.cpp fdwatch_epoll.cpp fdwatch_epoll.h fdwatch.h fdwatch_kqueue.cpp fdwatch_kqueue.h fdwatch_poll.cpp fdwatch_poll.h fdwatch_select.cpp fdwatch_select.h fdwbackend.cpp fdwbackend.h field_sizes.h file_protocol.h flags.h give_up_root_privileges.cpp give_up_root_privileges.h hashtable.cpp hashtable.h hash_tuple.hpp hexdump.cpp hexdump.h init_protocol.h introtate.h irc_protocol.h list.cpp list.h lstr.h make_unique.hpp network.cpp network.h packet.cpp packet.h proginfo.cpp proginfo.h queue.cpp queue.h rcm.cpp rcm.h rlimit.cpp rlimit.h scoped_array.h scoped_ptr.h setup_after.h setup_before.h systemerror.cpp systemerror.h tag.cpp tag.h token.cpp token.h tracker.h trans.cpp trans.h udp_protocol.h util.cpp util.h version.h wolhash.cpp wolhash.h xalloc.cpp xalloc.h xstr.cpp xstr.h xstring.cpp xstring.h gui_printf.h gui_printf.cpp bigint.cpp bigint.h bnetsrp3.cpp bnetsrp3.h peerchat.cpp peerchat.h wol_gameres_protocol.h format.cc format.h pugiconfig.h pugixml.cpp pugixml.h) add_library(common ${COMMON_SOURCES} ) pvpgn-1.99.7.2.1+dfsg/src/common/addr.cpp000066400000000000000000000314741462640461400177060ustar00rootroot00000000000000/* * Copyright (C) 1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define ADDR_INTERNAL_ACCESS #include "common/setup_before.h" #include "common/addr.h" #include #include #include #include #include "compat/psock.h" #include "common/eventlog.h" #include "common/list.h" #include "common/util.h" #include "common/xalloc.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" namespace pvpgn { static char const * netaddr_num_to_addr_str(unsigned int netipaddr, unsigned int netmask); #define HACK_SIZE 4 /* both arguments are in host byte order */ extern char const * addr_num_to_addr_str(unsigned int ipaddr, unsigned short port) { static unsigned int curr = 0; static char temp[HACK_SIZE][64]; struct sockaddr_in tsa = { 0 }; curr = (curr + 1) % HACK_SIZE; tsa.sin_family = PSOCK_AF_INET; tsa.sin_port = htons((unsigned short)0); tsa.sin_addr.s_addr = htonl(ipaddr); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(tsa.sin_addr), addrstr, sizeof(addrstr)); std::sprintf(temp[curr], "%.32s:%hu", addrstr, port); return temp[curr]; } /* ipaddr is in host byte order */ extern char const * addr_num_to_ip_str(unsigned int ipaddr) { static unsigned int curr = 0; static char temp[HACK_SIZE][64]; struct sockaddr_in tsa; curr = (curr + 1) % HACK_SIZE; std::memset(&tsa, 0, sizeof(tsa)); tsa.sin_family = PSOCK_AF_INET; tsa.sin_port = htons((unsigned short)0); tsa.sin_addr.s_addr = htonl(ipaddr); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(tsa.sin_addr), addrstr, sizeof(addrstr)); std::sprintf(temp[curr], "%.32s", addrstr); return temp[curr]; } static char const * netaddr_num_to_addr_str(unsigned int netipaddr, unsigned int netmask) { static unsigned int curr = 0; static char temp[HACK_SIZE][64]; struct sockaddr_in tsa; curr = (curr + 1) % HACK_SIZE; std::memset(&tsa, 0, sizeof(tsa)); tsa.sin_family = PSOCK_AF_INET; tsa.sin_port = htons((unsigned short)0); tsa.sin_addr.s_addr = htonl(netipaddr); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(tsa.sin_addr), addrstr, sizeof(addrstr)); std::sprintf(temp[curr], "%.32s/0x%08x", addrstr, netmask); return temp[curr]; } extern char const * host_lookup(char const * hoststr, unsigned int * ipaddr) { struct sockaddr_in tsa; #ifdef HAVE_GETHOSTBYNAME struct hostent * hp; #endif if (!hoststr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hoststr"); return NULL; } if (!ipaddr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL ipaddr"); return NULL; } std::memset(&tsa, 0, sizeof(tsa)); tsa.sin_family = PSOCK_AF_INET; tsa.sin_port = htons(0); #ifdef HAVE_GETHOSTBYNAME #ifdef WIN32 psock_init(); #endif hp = gethostbyname(hoststr); if (!hp || !hp->h_addr_list) #endif { if (inet_pton(AF_INET, hoststr, &tsa.sin_addr) == 1) { *ipaddr = ntohl(tsa.sin_addr.s_addr); return hoststr; /* We could call gethostbyaddr() on tsa to try and get the official hostname but most systems would have already found it when sending a dotted-quad to gethostbyname(). This is good enough when that fails. */ } eventlog(eventlog_level_error, __FUNCTION__, "could not lookup host \"{}\"", hoststr); return NULL; } #ifdef HAVE_GETHOSTBYNAME std::memcpy(&tsa.sin_addr, (void *)hp->h_addr_list[0], sizeof(struct in_addr)); /* avoid warning */ *ipaddr = ntohl(tsa.sin_addr.s_addr); if (hp->h_name) return hp->h_name; return hoststr; #endif } extern t_addr * addr_create_num(unsigned int ipaddr, unsigned short port) { t_addr * temp; temp = (t_addr*)xmalloc(sizeof(t_addr)); temp->str = xstrdup(addr_num_to_addr_str(ipaddr, port)); temp->ip = ipaddr; temp->port = port; temp->data.p = NULL; return temp; } extern t_addr * addr_create_str(char const * str, unsigned int defipaddr, unsigned short defport) { char * tstr; t_addr * temp; unsigned int ipaddr; unsigned short port; char const * hoststr; char * portstr; char const * hostname; if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return NULL; } tstr = xstrdup(str); if ((portstr = std::strrchr(tstr, ':'))) { char * protstr; *portstr = '\0'; portstr++; if ((protstr = std::strrchr(portstr, '/'))) { *protstr = '\0'; protstr++; } if (portstr[0] != '\0') { if (str_to_ushort(portstr, &port) < 0) { #ifdef HAVE_GETSERVBYNAME struct servent * sp; if (!(sp = getservbyname(portstr, protstr ? protstr : "tcp"))) #endif { eventlog(eventlog_level_error, __FUNCTION__, "could not convert \"{}\" to a port number", portstr); xfree(tstr); return NULL; } #ifdef HAVE_GETSERVBYNAME port = ntohs(sp->s_port); #endif } } else port = defport; } else port = defport; char addrstr[INET_ADDRSTRLEN] = {}; if (tstr[0] != '\0') { hoststr = tstr; } else { struct sockaddr_in tsa {}; tsa.sin_addr.s_addr = htonl(defipaddr); hoststr = inet_ntop(AF_INET, &(tsa.sin_addr), addrstr, sizeof(addrstr)); } if (!(hostname = host_lookup(hoststr, &ipaddr))) { eventlog(eventlog_level_error, __FUNCTION__, "could not lookup host \"{}\"", hoststr); xfree(tstr); return NULL; } temp = (t_addr*)xmalloc(sizeof(t_addr)); temp->str = xstrdup(hostname); xfree(tstr); temp->ip = ipaddr; temp->port = port; temp->data.p = NULL; return temp; } extern int addr_destroy(t_addr const * addr) { if (!addr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr"); return -1; } if (addr->str) xfree((void *)addr->str); /* avoid warning */ xfree((void *)addr); /* avoid warning */ return 0; } /* hostname or IP */ extern char * addr_get_host_str(t_addr const * addr, char * str, unsigned int len) { if (!addr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr"); return NULL; } if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return NULL; } if (len < 2) { eventlog(eventlog_level_error, __FUNCTION__, "str too short"); return NULL; } if (!addr->str) { eventlog(eventlog_level_error, __FUNCTION__, "addr has NULL str"); return NULL; } std::strncpy(str, addr->str, len - 1); str[len - 1] = '\0'; return str; } /* IP:port */ extern char * addr_get_addr_str(t_addr const * addr, char * str, unsigned int len) { if (!addr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr"); return NULL; } if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return NULL; } if (len < 2) { eventlog(eventlog_level_error, __FUNCTION__, "str too short"); return NULL; } std::strncpy(str, addr_num_to_addr_str(addr->ip, addr->port), len - 1); str[len - 1] = '\0'; return str; } extern unsigned int addr_get_ip(t_addr const * addr) { if (!addr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr"); return 0; } return addr->ip; } extern unsigned short addr_get_port(t_addr const * addr) { if (!addr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr"); return 0; } return addr->port; } extern int addr_set_data(t_addr * addr, t_addr_data data) { if (!addr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr"); return -1; } addr->data = data; return 0; } extern t_addr_data addr_get_data(t_addr const * addr) { t_addr_data tdata; if (!addr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addr"); tdata.p = NULL; return tdata; } return addr->data; } extern t_netaddr * netaddr_create_str(char const * netstr) { t_netaddr * netaddr; char * temp; char const * netipstr; char const * netmaskstr; unsigned int netip; unsigned int netmask; if (!netstr) { eventlog(eventlog_level_error, __FUNCTION__, "unable to allocate memory for netaddr"); return NULL; } temp = xstrdup(netstr); if (!(netipstr = std::strtok(temp, "/"))) { xfree(temp); return NULL; } if (!(netmaskstr = std::strtok(NULL, "/"))) { xfree(temp); return NULL; } netaddr = (t_netaddr*)xmalloc(sizeof(t_netaddr)); /* FIXME: call getnetbyname() first, then host_lookup() */ if (!host_lookup(netipstr, &netip)) { eventlog(eventlog_level_error, __FUNCTION__, "could not lookup net"); xfree(netaddr); xfree(temp); return NULL; } netaddr->ip = netip; if (str_to_uint(netmaskstr, &netmask) < 0) { struct sockaddr_in tsa; if (inet_pton(AF_INET, netmaskstr, &tsa.sin_addr) == 1) netmask = ntohl(tsa.sin_addr.s_addr); else { eventlog(eventlog_level_error, __FUNCTION__, "could not convert mask"); xfree(netaddr); xfree(temp); return NULL; } } else { if (netmask > 32) { eventlog(eventlog_level_error, __FUNCTION__, "network bits must be less than or equal to 32 ({})", netmask); xfree(netaddr); xfree(temp); return NULL; } /* for example, 8 -> 11111111000000000000000000000000 */ if (netmask != 0) netmask = ~((1 << (32 - netmask)) - 1); } netaddr->mask = netmask; xfree(temp); return netaddr; } extern int netaddr_destroy(t_netaddr const * netaddr) { if (!netaddr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL netaddr"); return -1; } xfree((void *)netaddr); /* avoid warning */ return 0; } extern char * netaddr_get_addr_str(t_netaddr const * netaddr, char * str, unsigned int len) { if (!netaddr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL netaddr"); return NULL; } if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return NULL; } if (len < 2) { eventlog(eventlog_level_error, __FUNCTION__, "str too short"); return NULL; } std::strncpy(str, netaddr_num_to_addr_str(netaddr->ip, netaddr->mask), len - 1); /* FIXME: format nicely with x.x.x.x/bitcount */ str[len - 1] = '\0'; return str; } extern int netaddr_contains_addr_num(t_netaddr const * netaddr, unsigned int ipaddr) { if (!netaddr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL netaddr"); return -1; } return (ipaddr&netaddr->mask) == netaddr->ip; } extern int addrlist_append(t_addrlist * addrlist, char const * str, unsigned int defipaddr, unsigned short defport) { t_addr * addr; char * tstr; char * tok; assert(addrlist != NULL); if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return -1; } tstr = xstrdup(str); for (tok = std::strtok(tstr, ","); tok; tok = std::strtok(NULL, ",")) /* std::strtok modifies the string it is passed */ { if (!(addr = addr_create_str(tok, defipaddr, defport))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create addr"); xfree(tstr); return -1; } list_append_data(addrlist, addr); } xfree(tstr); return 0; } extern t_addrlist * addrlist_create(char const * str, unsigned int defipaddr, unsigned short defport) { t_addrlist * addrlist; if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return NULL; } addrlist = list_create(); if (addrlist_append(addrlist, str, defipaddr, defport) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not append to newly created addrlist"); list_destroy(addrlist); return NULL; } return addrlist; } extern int addrlist_destroy(t_addrlist * addrlist) { t_elem * curr; t_addr * addr; if (!addrlist) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL addrlist"); return -1; } LIST_TRAVERSE(addrlist, curr) { if (!(addr = (t_addr*)elem_get_data(curr))) eventlog(eventlog_level_error, __FUNCTION__, "found NULL addr in list"); else addr_destroy(addr); list_remove_elem(addrlist, &curr); } return list_destroy(addrlist); } extern int addrlist_get_length(t_addrlist const * addrlist) { return list_get_length(addrlist); } } pvpgn-1.99.7.2.1+dfsg/src/common/addr.h000066400000000000000000000064641462640461400173540ustar00rootroot00000000000000/* * Copyright (C) 1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ADDR_TYPES #define INCLUDED_ADDR_TYPES #ifdef JUST_NEED_TYPES # include "common/list.h" #else # define JUST_NEED_TYPES # include "common/list.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { typedef union { unsigned long n; int i; void * p; } t_addr_data; typedef struct addr_struct #ifdef ADDR_INTERNAL_ACCESS { char const * str; /* hostname or ip */ unsigned int ip; unsigned int port; t_addr_data data; /* data argument */ } #endif t_addr; typedef struct netaddr_struct #ifdef ADDR_INTERNAL_ACCESS { unsigned int ip; unsigned int mask; } #endif t_netaddr; typedef t_list t_addrlist; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_ADDR_PROTOS #define INCLUDED_ADDR_PROTOS namespace pvpgn { /* ipaddr and port are in host byte order */ extern char const * addr_num_to_addr_str(unsigned int ipaddr, unsigned short port); /* ipaddr is in host byte order */ extern char const * addr_num_to_ip_str(unsigned int ipaddr); extern char const * host_lookup(char const * hoststr, unsigned int * ipaddr); /* ipaddr and port are in host byte order */ extern t_addr * addr_create_num(unsigned int ipaddr, unsigned short port); /* defipaddr and defport are in host byte order */ extern t_addr * addr_create_str(char const * str, unsigned int defipaddr, unsigned short defport); extern int addr_destroy(t_addr const * addr); extern char * addr_get_host_str(t_addr const * addr, char * str, unsigned int len); extern char * addr_get_addr_str(t_addr const * addr, char * str, unsigned int len); /* returned in host byte order */ extern unsigned int addr_get_ip(t_addr const * addr); /* returned in host byte order */ extern unsigned short addr_get_port(t_addr const * addr); extern int addr_set_data(t_addr * addr, t_addr_data data); extern t_addr_data addr_get_data(t_addr const * addr); extern t_netaddr * netaddr_create_str(char const * str); extern int netaddr_destroy(t_netaddr const * netaddr); extern char * netaddr_get_addr_str(t_netaddr const * netaddr, char * str, unsigned int len); extern int netaddr_contains_addr_num(t_netaddr const * netaddr, unsigned int ipaddr); /* defipaddr and defport are in host byte order */ extern int addrlist_append(t_addrlist * addrlist, char const * str, unsigned int defipaddr, unsigned short defport); extern t_addrlist * addrlist_create(char const * str, unsigned int defipaddr, unsigned short defport); extern int addrlist_destroy(t_addrlist * addrlist); extern int addrlist_get_length(t_addrlist const * addrlist); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/anongame_protocol.h000066400000000000000000000660441462640461400221500ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ANONGAME_PROTOCOL_TYPES #define INCLUDED_ANONGAME_PROTOCOL_TYPES #ifdef JUST_NEED_TYPES # include "common/bn_type.h" #else # define JUST_NEED_TYPES # include "common/bn_type.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { /***********************************************************************************/ /* first packet recieved from client - option decides which struct to use next */ #define CLIENT_FINDANONGAME 0x44ff #define SERVER_FINDANONGAME 0x44ff typedef struct { t_bnet_header h; bn_byte option; /* rest of packet data */ } PACKED_ATTR() t_client_anongame; /***********************************************************************************/ #define CLIENT_FINDANONGAME_SEARCH 0x00 #define CLIENT_FINDANONGAME_INFOS 0x02 #define CLIENT_FINDANONGAME_CANCEL 0x03 #define CLIENT_FINDANONGAME_PROFILE 0x04 #define CLIENT_FINDANONGAME_AT_SEARCH 0x05 #define CLIENT_FINDANONGAME_AT_INVITER_SEARCH 0x06 #define CLIENT_ANONGAME_TOURNAMENT 0X07 #define CLIENT_FINDANONGAME_PROFILE_CLAN 0x08 #define CLIENT_FINDANONGAME_GET_ICON 0x09 #define CLIENT_FINDANONGAME_SET_ICON 0x0A #define SERVER_FINDANONGAME_SEARCH 0x00 #define SERVER_FINDANONGAME_FOUND 0x01 #define SERVER_FINDANONGAME_CANCEL 0x03 /***********************************************************************************/ /* option 00 - anongame search */ /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Tournament PG 1v1 - battle.net # 440 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=28 class=bnet 0000: FF 44 1C 00 00 01 00 00 00 00 00 00 00[02 13]01 .D.............. 0010: 00 00 00 08 13 C7 30 04 04 00 00 00 ......0..... PG 1v1 # 438 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=28 class=bnet 0000: FF 44 1C 00 00 01 00 00 00 00 00 00 00[00 00]FF .D.............. 0010: 00 00 00 08 C2 13 A6 02 20 00 00 00 ........ ... PG 2v2 # 456 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=28 class=bnet 0000: FF 44 1C 00 00 02 00 00 00 00 00 00 00[00 01]FF .D.............. 0010: 07 00 00 08 A0 45 A6 02 20 00 00 00 .....E.. ... PG 3v3 # 482 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=28 class=bnet 0000: FF 44 1C 00 00 03 00 00 00 00 00 00 00[00 02]FF .D.............. 0010: 03 00 00 08 E8 7D A6 02 20 00 00 00 .....}.. ... PG 4v4 # 516 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=28 class=bnet 0000: FF 44 1C 00 00 04 00 00 00 00 00 00 00[00 03]FF .D.............. 0010: 01 00 00 08 48 B0 A6 02 20 00 00 00 ....H... ... PG sffa # 550 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=28 class=bnet 0000: FF 44 1C 00 00 05 00 00 00 00 00 00 00[00 04]FF .D.............. 0010: 01 00 00 08 E6 F4 A6 02 20 00 00 00 ........ ... =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= inviter 2v2 - battle.net # 74059 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=57 class=bnet 0000: FF 44 39 00 06 09 00 00 00 A0 03 00 0C 72 40 27 .D9..........r@' 0010: 3F 02 B9 DA 6B 9A A4 22 5D 60 49 EF 15 6F 44 25 ?...k.."]`I..oD% 0020: 02 4E F9 13 BC 4D 00 00 00 00[01 00]FF 07 00 00 .N...M.......... 0030: 08 7E 98 E6 04 04 00 00 00 .~....... inviter 2v2 - battle.net # 83545 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=57 class=bnet 0000: FF 44 39 00 06 0A 00 00 00 E4 03 00 0C 9E 45 27 .D9...........E' 0010: 3F 02 1E 4E E7 53 80 F9 01 00 4F 68 A1 58 C5 1B ?..N.S....Oh.X.. 0020: 44 31 BD EE 47 7F 00 00 00 00[01 00]FF 07 00 00 D1..G........... 0030: 08 48 07 FB 04 04 00 00 00 .H....... =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= AT 3v3 - pvpgn - with uid of team members # 378 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=57 class=bnet 0000: FF 44 39 00 06 01 00 00 00[B7 36 C4 19]1B 02 2F .D9.......6..../ 0010: 3F 03[19 00 00 00|02 00 00 00|01 00 00 00|FF FF ?............... 0020: FF FF|FF FF FF FF]00 00 00 00 01 02 FF 03 00 00 ................ 0030: 08 AA A4 E2 0C 20 00 00 00 ..... ... # 379 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=51 class=bnet 0000: FF 44 33 00 05 01 00 00 00[B7 36 C4 19]1B 02 2F .D3.......6..../ 0010: 3F 03[19 00 00 00|02 00 00 00|01 00 00 00|FF FF ?............... 0020: FF FF|FF FF FF FF]00 00 00 00 08 59 C6 0E 00 20 ...........Y... 0030: 00 00 00 ... # 382 packet from client: type=0x44ff(CLIENT_FINDANONGAME) length=51 class=bnet 0000: FF 44 33 00 05 01 00 00 00[B7 36 C4 19]1B 02 2F .D3.......6..../ 0010: 3F 03[19 00 00 00|02 00 00 00|01 00 00 00|FF FF ?............... 0020: FF FF|FF FF FF FF]00 00 00 00 08 45 2C 13 00 20 ...........E,.. 0030: 00 00 00 ... =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ typedef struct { t_bnet_header h; bn_byte option; bn_int count; /* Goes up each time client clicks search */ bn_int unknown2; /* 00 00 00 00 */ bn_byte type; /* 0 = PG , 1 = AT , 2 = TY - from TYPE */ bn_byte gametype; /* 0 = 1v1 , 1 = 2v2 , 2 = 3v3 , 3 = 4v4 , 4 = sffa - from TYPE */ bn_int map_prefs; /* map preferences bitmask */ bn_byte unknown3; /* 8 */ bn_int id; /* client id */ bn_int race; /* 1 = H , 2 = O , 4 = N , 8 = U , 0x20 = R */ } PACKED_ATTR() t_client_findanongame; typedef struct { t_bnet_header h; bn_byte option; bn_int count; bn_int tid; /* team id */ bn_int timestamp; bn_byte teamsize; bn_int info[5]; /* client get this info from SERVER_ARRANGEDTEAM_INVITE_FRIEND_ACK (0x61ff) */ bn_int unknown2; /* 00 00 00 00 */ bn_byte type; /* 0 = PG , 1 = AT , 2 = TY - from TYPE */ bn_byte gametype; /* 0 = 1v1 , 1 = 2v2 , 2 = 3v3 , 3 = 4v4 , 4 = sffa - from TYPE */ bn_int map_prefs; /* map preferences bitmask */ bn_byte unknown3; /* 08 */ bn_int id; /* client id */ bn_int race; /* 1 = H , 2 = O , 4 = N , 8 = U , 0x20 = R */ } PACKED_ATTR() t_client_findanongame_at_inv; typedef struct { t_bnet_header h; bn_byte option; bn_int count; /* Goes up each time client clicks search */ bn_int tid; /* team id */ bn_int timestamp; bn_byte teamsize; bn_int info[5]; /* client get this info from inviter form SERVER_ARRANGEDTEAM_INVITE_FRIEND_ACK (0x61ff) */ bn_int unknown2; /* 00 00 00 00 */ bn_byte unknown3; /* 08 */ bn_int id; /* client id */ bn_int race; /* 1 = H , 2 = O , 4 = N , 8 = U , 0x20 = R */ } PACKED_ATTR() t_client_findanongame_at; #define SERVER_ANONGAME_SEARCH_REPLY 0x44ff typedef struct { t_bnet_header h; bn_byte option; bn_int count; bn_int reply; /* bn_short avgtime; - only in W3XP so far average time in seconds of search */ } PACKED_ATTR() t_server_anongame_search_reply; /***********************************************************************************/ /* option 01 - anongame found */ /* tournament 1v1 # 490 packet from server: type=0x44ff(unknown) length=84 class=bnet 0000: FF 44 54 00 01 01 00 00 00 00 00 00 00 3F F1 53 .DT..........?.S 0010: CD E0 17 F8 CC 00 00 3E 4F 8E 12 06[02 13]4D 61 .......>O.....Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 34 29 46 6C 6F 6F 64 70 6C 61 69 6E 73 31 76 (4)Floodplains1v 0040: 31 2E 77 33 78 00 FF FF FF FF 31 44 4C 46 02 00 1.w3x.....1DLF.. 0050: 00 00 02 02 .... PG 2v2 # 28332 packet from server: type=0x44ff(unknown) length=83 class=bnet 0000: FF 44 53 00 01 02 00 00 00 00 00 00 00 D3 E9 00 .DS............. 0010: DC E0 17 2E 67 00 00 9A 7D 56 0F 06 00 01 4D 61 ....g...}V....Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 36 29 53 63 6F 72 63 68 65 64 42 61 73 69 6E (6)ScorchedBasin 0040: 2E 77 33 78 00 FF FF FF FF 4D 41 45 54 04 02 00 .w3x.....MAET... 0050: 00 02 02 ... PG 4v4 # 28827 packet from server: type=0x44ff(unknown) length=82 class=bnet 0000: FF 44 52 00 01 02 00 00 00 00 00 00 00 3F F1 53 .DR..........?.S 0010: DA E0 17 77 D6 00 00 92 D4 B8 56 06 00 03 4D 61 ...w......V...Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 38 29 42 61 74 74 6C 65 67 72 6F 75 6E 64 2E (8)Battleground. 0040: 77 33 78 00 FF FF FF FF 4D 41 45 54 08 02 00 00 w3x.....MAET.... 0050: 02 02 .. PG 3v3 # 357 packet from server: type=0x44ff(unknown) length=84 class=bnet 0000: FF 44 54 00 01 01 00 00 00 00 00 00 00 3F F1 53 .DT..........?.S 0010: CE E0 17 10 22 01 00 29 0F 62 A0 06 00 02 4D 61 ...."..).b....Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 36 29 42 6C 6F 6F 64 73 74 6F 6E 65 4D 65 73 (6)BloodstoneMes 0040: 61 2E 77 33 78 00 FF FF FF FF 4D 41 45 54 06 02 a.w3x.....MAET.. 0050: 00 00 02 02 .... PG 4v4 # 620 packet from server: type=0x44ff(unknown) length=79 class=bnet 0000: FF 44 4F 00 01 02 00 00 00 00 00 00 00 3F F1 53 .DO..........?.S 0010: DA E0 17 94 22 01 00 4C 29 5F 62 06 00 03 4D 61 ...."..L)_b...Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 38 29 53 61 6E 63 74 75 61 72 79 2E 77 33 78 (8)Sanctuary.w3x 0040: 00 FF FF FF FF 4D 41 45 54 08 02 00 00 02 02 .....MAET...... PG sffa # 810 packet from server: type=0x44ff(unknown) length=82 class=bnet 0000: FF 44 52 00 01 03 00 00 00 00 00 00 00 3F F1 53 .DR..........?.S 0010: C9 E0 17 3E 22 01 00 93 44 ED 22 06 00 04 4D 61 ...>"...D."...Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 38 29 42 61 74 74 6C 65 67 72 6F 75 6E 64 2E (8)Battleground. 0040: 77 33 78 00 FF FF FF FF 20 41 46 46 05 00 00 00 w3x..... AFF.... 0050: 02 02 .. =-=-=-=-==-=-=-=-= AT 2v2 # 74077 packet from server: type=0x44ff(unknown) length=87 class=bnet 0000: FF 44 57 00 01 09 00 00 00 00 00 00 00 3F F1 53 .DW..........?.S 0010: D3 E0 17 D8 D1 00 00 B4 2B 57 C6 06[01 00]4D 61 ........+W....Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 36 29 53 74 72 61 6E 67 6C 65 74 68 6F 72 6E (6)Stranglethorn 0040: 56 61 6C 65 2E 77 33 78 00 FF FF FF FF 32 53 56 Vale.w3x.....2SV 0050: 32 04 02 00 00 02 02 2...... AT 2v2 # 83549 packet from server: type=0x44ff(unknown) length=79 class=bnet 0000: FF 44 4F 00 01 0A 00 00 00 00 00 00 00 3F F1 53 .DO..........?.S 0010: CF E0 17 BB D1 00 00 01 CB E4 BA 06[01 00]4D 61 ..............Ma 0020: 70 73 5C 46 72 6F 7A 65 6E 54 68 72 6F 6E 65 5C ps\FrozenThrone\ 0030: 28 36 29 47 6E 6F 6C 6C 57 6F 6F 64 2E 77 33 78 (6)GnollWood.w3x 0040: 00 FF FF FF FF 32 53 56 32 04 02 00 00 02 02 .....2SV2...... */ #define SERVER_ANONGAME_FOUND 0x44ff typedef struct { t_bnet_header h; bn_byte option; /* 1: anongame found */ bn_int count; bn_int unknown1; /* 00 00 00 00 */ bn_int ip; bn_short port; bn_byte unknown2; bn_byte unknown3; bn_short unknown4; /* usually 00 00 , seen 01 00 */ bn_int id; /* random val for identifying client */ bn_byte unknown5; /* 0x06 */ bn_byte type; /* 0 = PG , 1 = AT , 2 = TY - from TYPE */ bn_byte gametype; /* for PG - 0 = 1v1 , 1 = 2v2 , 2 = 3v3 , 3 = 4v4 , 4 = sffa * for AT - 0 = 2v2 , 2 = 3v3 , 3 = 4v3 * for TY - set to 0 * from TYPE */ /* char * mapname */ /* t_saf_pt2 * pt2 */ } PACKED_ATTR() t_server_anongame_found; /* MISC PACKET APPEND DATA's */ typedef struct { bn_int unknown1; /* 0xFFFFFFFF */ bn_int anongame_string; /* ie. SOLO, TEAM, 2VS2, etc. */ bn_byte totalplayers; bn_byte totalteams; /* 1v1 & sffa = 0, rest 2 */ bn_short unknown2; /* 0x0000 */ bn_byte visibility; /* 0x01 = dark - 0x02 = default */ bn_byte unknown3; /* 0x02 */ } PACKED_ATTR() t_saf_pt2; /***********************************************************************************/ /* option 02 - info request */ #define CLIENT_FINDANONGAME_INFOREQ 0x44ff typedef struct { t_bnet_header h; bn_byte option; /* type of request: * 0x02 for matchmaking infos */ bn_int count; /* 0x00000001 increments each request of same type */ bn_byte noitems; } PACKED_ATTR() t_client_findanongame_inforeq; #define SERVER_FINDANONGAME_INFOREPLY 0x44ff typedef struct { t_bnet_header h; bn_byte option; /* as received from client */ bn_int count; /* as received from client */ bn_byte noitems; /* not very sure about it */ /* data */ /* for type 0x02 : if is : URL\0 : contains 3 NULL terminated urls/strings MAP\0 : contains 7 (seen so far) maps names TYPE : unknown 38 bytes probably meaning anongame types DESC : */ } PACKED_ATTR() t_server_findanongame_inforeply; /***********************************************************************************/ /* option 03 - playgame cancel */ #define SERVER_FINDANONGAME_PLAYGAME_CANCEL 0x44ff typedef struct { t_bnet_header h; /* header */ bn_byte cancel; /* Cancel byte always 03 */ bn_int count; } PACKED_ATTR() t_server_findanongame_playgame_cancel; /* option 04 - profile request */ typedef struct { t_bnet_header h; bn_byte option; bn_int count; /* USERNAME TO LOOKUP * CLIENT TAG */ } PACKED_ATTR() t_client_findanongame_profile; #define SERVER_FINDANONGAME_PROFILE 0x44ff /* typedef struct { t_bnet_header h; //header bn_byte option; // in this case it will be 0x04 (for profile) bn_int count; // count that goes up each time user clicks on someones profile // REST OF PROFILE STATS - THIS WILL BE SET IN HANDLE_BNET.C after // SERVER LOOKS UP THE USER ACCOUNT } PACKED_ATTR() t_server_findanongame_profile; */ typedef struct { t_bnet_header h; bn_byte option; bn_int count; bn_int icon; bn_byte rescount; /* REST OF PROFILE STATS - THIS WILL BE SET IN HANDLE_BNET.C after * SERVER LOOKS UP THE USER ACCOUNT */ } PACKED_ATTR() t_server_findanongame_profile2; /***********************************************************************************/ /* option 07 - tournament request */ #define CLIENT_FINDANONGAME_TOURNAMENT_REQUEST 0x44ff typedef struct { t_bnet_header h; bn_byte option; /* 07 */ bn_int count; /* 01 00 00 00 */ } PACKED_ATTR() t_client_anongame_tournament_request; #define SERVER_FINDANONGAME_TOURNAMENT_REPLY 0x44ff typedef struct { t_bnet_header h; bn_byte option; /* 07 */ bn_int count; /* 00 00 00 01 reply with same number */ bn_byte type; /* type - 01 = notice - time = prelim round begins * 02 = signups - time = signups end * 03 = signups over - time = prelim round ends * 04 = prelim over - time = finals round 1 begins */ bn_byte unknown; /* 00 */ bn_short unknown4; /* random ? might be part of time/date ? */ bn_int timestamp; bn_byte unknown5; /* 01 effects time/date */ bn_short countdown; /* countdown until next timestamp (seconds) */ bn_short unknown2; /* 00 00 */ bn_byte wins; /* during prelim */ bn_byte losses; /* during prelim */ bn_byte ties; /* during prelim */ bn_byte unknown3; /* 00 = notice. 08 = signups thru prelim over (02-04) */ bn_byte selection; /* matches anongame_TY_section of DESC */ bn_byte descnum; /* matches desc_count of DESC */ bn_byte nulltag; /* 00 */ } PACKED_ATTR() t_server_anongame_tournament_reply; /***********************************************************************************/ /* option 08 - clan profile request */ typedef struct { t_bnet_header h; bn_byte option; bn_int count; bn_int clantag; bn_int clienttag; } PACKED_ATTR() t_client_findanongame_profile_clan; #define SERVER_FINDANONGAME_PROFILE_CLAN 0x44ff typedef struct { t_bnet_header h; bn_byte option; bn_int count; bn_byte rescount; /* REST OF PROFILE STATS - THIS WILL BE SET IN HANDLE_BNET.C after * SERVER LOOKS UP THE USER ACCOUNT */ } PACKED_ATTR() t_server_findanongame_profile_clan; /***********************************************************************************/ /* option 9 - icon request */ #define SERVER_FINDANONGAME_ICONREPLY 0x44ff typedef struct{ t_bnet_header h; bn_byte option; /* as received from client */ bn_int count; /* as received from client */ bn_int curricon; /* current icon code */ bn_byte table_width; /* the icon table width */ bn_byte table_size; /* the icon table total size */ /* table data */ } PACKED_ATTR() t_server_findanongame_iconreply; /***********************************************************************************/ #define SERVER_ANONGAME_SOLO_STR 0x534F4C4F /* "SOLO" */ #define SERVER_ANONGAME_TEAM_STR 0x5445414D /* "TEAM" */ #define SERVER_ANONGAME_SFFA_STR 0x46464120 /* "FFA " */ #define SERVER_ANONGAME_AT2v2_STR 0x32565332 /* "2VS2" */ #define SERVER_ANONGAME_AT3v3_STR 0x33565333 /* "3VS3" */ #define SERVER_ANONGAME_AT4v4_STR 0x34565334 /* "4VS4" */ #define SERVER_ANONGAME_TY_STR 0X54592020 /* "TY " FIXME-TY: WHAT TO PUT HERE */ #define CLIENT_FINDANONGAME_INFOTAG_URL 0x55524c /* URL\0 */ #define CLIENT_FINDANONGAME_INFOTAG_MAP 0x4d4150 /* MAP\0 */ #define CLIENT_FINDANONGAME_INFOTAG_TYPE 0x54595045 /* TYPE */ #define CLIENT_FINDANONGAME_INFOTAG_DESC 0x44455343 /* DESC */ #define CLIENT_FINDANONGAME_INFOTAG_LADR 0x4c414452 /* LADR */ #define CLIENT_FINDANONGAME_INFOTAG_SOLO 0x534f4c4f /* SOLO */ #define CLIENT_FINDANONGAME_INFOTAG_TEAM 0x5445414d /* TEAM */ #define CLIENT_FINDANONGAME_INFOTAG_FFA 0x46464120 /* FFA\20 */ #define ANONGAME_TYPE_1V1 0 #define ANONGAME_TYPE_2V2 1 #define ANONGAME_TYPE_3V3 2 #define ANONGAME_TYPE_4V4 3 #define ANONGAME_TYPE_SMALL_FFA 4 #define ANONGAME_TYPE_AT_2V2 5 #define ANONGAME_TYPE_TEAM_FFA 6 #define ANONGAME_TYPE_AT_3V3 7 #define ANONGAME_TYPE_AT_4V4 8 /* Added by Omega */ #define ANONGAME_TYPE_TY 9 #define ANONGAME_TYPE_5V5 10 #define ANONGAME_TYPE_6V6 11 #define ANONGAME_TYPE_2V2V2 12 #define ANONGAME_TYPE_3V3V3 13 #define ANONGAME_TYPE_4V4V4 14 #define ANONGAME_TYPE_2V2V2V2 15 #define ANONGAME_TYPE_3V3V3V3 16 #define ANONGAME_TYPE_AT_2V2V2 17 #define ANONGAME_TYPES 18 #define SERVER_FINDANONGAME_PROFILE_UNKNOWN2 0x6E736865 /* Sheep */ /***********************************************************************************/ /***********************************************************************************/ /* This is a blank packet - includes just type and size */ #define CLIENT_ARRANGEDTEAM_FRIENDSCREEN 0x60ff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_arrangedteam_friendscreen; /***********************************************************************************/ #define SERVER_ARRANGEDTEAM_FRIENDSCREEN 0x60ff typedef struct { t_bnet_header h; bn_byte f_count; /* usernames get appended here */ } PACKED_ATTR() t_server_arrangedteam_friendscreen; #define SERVER_ARRANGED_TEAM_ADDNAME 0x01 /***********************************************************************************/ /* 0000: FF 61 1C 00 01 00 00 00 C9 7B A0 02 01 00 00 00 .........{...... 0010: 01 74 72 65 6E 64 65 63 69 64 65 00 .trendecide. */ #define CLIENT_ARRANGEDTEAM_INVITE_FRIEND 0x61ff typedef struct { t_bnet_header h; bn_int count; bn_int id; bn_int unknown1; /* 01 00 00 00 */ bn_byte numfriends; /* next is a byte, that is the number of friends to invite */ /* usernames get appended here */ } PACKED_ATTR() t_client_arrangedteam_invite_friend; /***********************************************************************************/ #define SERVER_ARRANGEDTEAM_INVITE_FRIEND_ACK 0x61ff typedef struct { t_bnet_header h; bn_int count; bn_int id; /* client id */ bn_int timestamp; bn_byte teamsize; /* numfriends + 1 */ bn_int info[5]; } PACKED_ATTR() t_server_arrangedteam_invite_friend_ack; /***********************************************************************************/ #define SERVER_ARRANGEDTEAM_SEND_INVITE 0x63ff typedef struct { t_bnet_header h; bn_int count; bn_int id; /* client id of inviter */ bn_int inviterip; /* IP address of the person who invited them into the game */ bn_short port; /* Port of the person who invited them into the game */ bn_byte numfriends; /* Number of friends that got invited to the game */ /* username of the inviter */ /* usernames of the others who got invited */ } PACKED_ATTR() t_server_arrangedteam_send_invite; /***********************************************************************************/ #define CLIENT_ARRANGEDTEAM_ACCEPT_DECLINE_INVITE 0x63ff typedef struct { t_bnet_header h; bn_int count; bn_int id; bn_int option; /* accept or decline */ /* username of the inviter */ } PACKED_ATTR() t_client_arrangedteam_accept_decline_invite; #define CLIENT_ARRANGEDTEAM_ACCEPT 0x00000003 #define CLIENT_ARRANGEDTEAM_DECLINE 0x00000002 /***********************************************************************************/ #define SERVER_ARRANGEDTEAM_MEMBER_DECLINE 0x62ff typedef struct { t_bnet_header h; bn_int count; bn_int action; /* number assigned to player? playernum? */ /* username of the person who declined invitation */ } PACKED_ATTR() t_server_arrangedteam_member_decline; #define SERVER_ARRANGEDTEAM_ACCEPT 0x00000003 #define SERVER_ARRANGEDTEAM_DECLINE 0x00000002 /***********************************************************************************/ /* not used ?? [Omega] */ /* 0000: FF 64 1C 00 02 00 00 00 00 00 00 00 01 00 00 00 .d.............. 0010: 00 00 00 00 03 00 00 00 00 00 00 00 ............ */ /* THIS NEEDS FINISHED */ /* #define SERVER_ARRANGEDTEAM_TEAM_STATS 0x64ff typedef struct { t_bnet_header h; } */ /***********************************************************************************/ /***********************************************************************************/ /* # 144 packet from client: type=0x65ff(unknown) length=4 class=bnet 0000: FF 65 04 00 .e.. */ #define CLIENT_FRIENDSLISTREQ 0x65ff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_friendslistreq; /* # 158 packet from server: type=0x65ff(unknown) length=16 class=bnet 0000: FF 65 10 00 01 66 6F 6F 00 00 00 00 00 00 00 00 .e.. .foo. ....... */ #define SERVER_FRIENDSLISTREPLY 0x65ff typedef struct { t_bnet_header h; bn_byte friendcount; /* 1 byte status, 0-terminated name, 6 bytes unknown, ... */ } PACKED_ATTR() t_server_friendslistreply; typedef struct { bn_byte status; bn_byte location; bn_int clienttag; } PACKED_ATTR() t_server_friendslistreply_status; /* # 124 packet from client: type=0x66ff(unknown) length=5 class=bnet 0000: FF 66 05 00 00 .f... */ /* FF 66-05 00 00 40 - AT */ #define CLIENT_FRIENDINFOREQ 0x66ff typedef struct { t_bnet_header h; bn_byte friendnum; } PACKED_ATTR() t_client_friendinforeq; /* # 126 packet from server: type=0x66ff(unknown) length=12 class=bnet 0000: FF 66 0C 00 00 00 00 00 00 00 00 00 .f.......... Arranged Team sends this to each inviter FF 66-1A 00 00 01 03 33 52 41 ..g....f.....3RA 0x0040 57 41 72 72 61 6E 67 65-64 20 54 65 61 6D 73 00 WArranged Teams. and this to the inviter FF 66-18 00 00 01 02 33 52 41 ..i....f.....3RA 0x0040 57 57 61 72 63 72 61 66-74 20 49 49 49 00 WWarcraft III. */ #define SERVER_FRIENDINFOREPLY 0x66ff typedef struct { t_bnet_header h; bn_byte friendnum; bn_byte type; bn_byte status; bn_int clienttag; /* game name */ } PACKED_ATTR() t_server_friendinforeply; #define FRIEND_TYPE_NON_MUTUAL 0x00 #define FRIEND_TYPE_MUTUAL 0x01 #define FRIEND_TYPE_DND 0x02 #define FRIEND_TYPE_AWAY 0x04 /******************************************************/ /* # 126 packet from server: type=0x67ff(unknown) length=15 class=bnet 0000: FF 67 0F 00 66 6F 6F 00 00 00 00 00 00 00 00 .g..foo........ */ #define SERVER_FRIENDADD_ACK 0x67ff typedef struct { t_bnet_header h; /* friend name, status */ } PACKED_ATTR() t_server_friendadd_ack; /******************************************************/ /* # 114 packet from server: type=0x68ff(unknown) length=5 class=bnet 0000: FF 68 05 00 01 .h... */ #define SERVER_FRIENDDEL_ACK 0x68ff typedef struct { t_bnet_header h; bn_byte friendnum; } PACKED_ATTR() t_server_frienddel_ack; /******************************************************/ #define SERVER_FRIENDMOVE_ACK 0x69ff typedef struct { t_bnet_header h; bn_byte pos1; bn_byte pos2; } PACKED_ATTR() t_server_friendmove_ack; #define FRIENDSTATUS_OFFLINE 0x00 #define FRIENDSTATUS_ONLINE 0x01 #define FRIENDSTATUS_CHAT 0x02 #define FRIENDSTATUS_PUBLIC_GAME 0x03 #define FRIENDSTATUS_PRIVATE_GAME 0x05 } #endif pvpgn-1.99.7.2.1+dfsg/src/common/asnprintf.cpp000066400000000000000000000220631462640461400207720ustar00rootroot00000000000000/* * Copyright (C) 2005 Olaf Freyer (aaron@cs.tu-berlin.de) * * Based upon lib/vsnprintf.c from the linux kernel sources * * Copyright (C) 1991, 1992 Linus Torvalds * vsprintf.c -- Lars Wirzenius & Linus Torvalds. * * * Wirzenius wrote this portably, Torvalds fucked it up :-) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "asnprintf.h" #include #include #include #include "common/setup_after.h" namespace pvpgn { static int skip_atoi(const char **s) { int i = 0; while (std::isdigit(**s)) i = i * 10 + *((*s)++) - '0'; return i; } std::uint32_t do_div(std::uint64_t *n, std::uint32_t base) { std::uint32_t remainder = *n % base; *n = *n / base; return remainder; } static char * number(char * buf, char * end, std::uint64_t num, int base, int size, int precision, int type) { char c, sign, tmp[66]; const char *digits; static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int i; digits = (type & LARGE) ? large_digits : small_digits; if (type & LEFT) type &= ~ZEROPAD; if (base < 2 || base > 36) return NULL; c = (type & ZEROPAD) ? '0' : ' '; sign = 0; if (type & SIGN) { if ((std::int64_t)num < 0) { sign = '-'; num = -(std::int64_t)num; size--; } else if (type & PLUS) { sign = '+'; size--; } else if (type & SPACE) { sign = ' '; size--; } } if (type & SPECIAL) { if (base == 16) size -= 2; else if (base == 8) size--; } i = 0; if (num == 0) tmp[i++] = '0'; else while (num != 0) tmp[i++] = digits[do_div(&num, base)]; if (i > precision) precision = i; size -= precision; if (!(type&(ZEROPAD + LEFT))) { while (size-- > 0) { if (buf <= end) *buf = ' '; ++buf; } } if (sign) { if (buf <= end) *buf = sign; ++buf; } if (type & SPECIAL) { if (base == 8) { if (buf <= end) *buf = '0'; ++buf; } else if (base == 16) { if (buf <= end) *buf = '0'; ++buf; if (buf <= end) *buf = digits[33]; ++buf; } } if (!(type & LEFT)) { while (size-- > 0) { if (buf <= end) *buf = c; ++buf; } } while (i < precision--) { if (buf <= end) *buf = '0'; ++buf; } while (i-- > 0) { if (buf <= end) *buf = tmp[i]; ++buf; } while (size-- > 0) { if (buf <= end) *buf = ' '; ++buf; } return buf; } int vasnprintf(char *buf, std::size_t size, t_fmtentry *entries, unsigned entlen, const char *fmt, std::va_list args) { int len; std::uint64_t num; int i, base; char *str, *end, c; const char *s; int flags; /* flags to number() */ int field_width; /* width of output field */ int precision; /* min. # of digits for integers; max number of chars for from string */ int qualifier; /* 'h', 'l', or 'L' for integer fields */ /* 'z' support added 23/7/1999 S.H. */ /* 'z' changed to 'Z' --davidm 1/25/99 */ unsigned fmtno = 0; /* Reject out-of-range values early */ if ((int)size < 0) return 0; str = buf; end = buf + size - 1; /* if (end < buf - 1) { end = ((void *) -1); size = end - buf + 1; } */ for (; *fmt; ++fmt) { t_fmtentry* const fmtent = entries + fmtno; if (*fmt != '%') continue; if (fmtno >= entlen) return -1; fmtent->trans = str; fmtent->fmt = fmt; fmtno++; /* process flags */ flags = 0; repeat: ++fmt; /* this also skips first '%' */ switch (*fmt) { case '-': flags |= LEFT; goto repeat; case '+': flags |= PLUS; goto repeat; case ' ': flags |= SPACE; goto repeat; case '#': flags |= SPECIAL; goto repeat; case '0': flags |= ZEROPAD; goto repeat; } /* get field width */ field_width = -1; if (std::isdigit(*fmt)) field_width = skip_atoi(&fmt); else if (*fmt == '*') { ++fmt; /* it's the next argument */ field_width = va_arg(args, int); if (field_width < 0) { field_width = -field_width; flags |= LEFT; } } /* get the precision */ precision = -1; if (*fmt == '.') { ++fmt; if (std::isdigit(*fmt)) precision = skip_atoi(&fmt); else if (*fmt == '*') { ++fmt; /* it's the next argument */ precision = va_arg(args, int); } if (precision < 0) precision = 0; } /* get the conversion qualifier */ qualifier = -1; if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z' || *fmt == 'z') { qualifier = *fmt; ++fmt; if (qualifier == 'l' && *fmt == 'l') { qualifier = 'L'; ++fmt; } } /* default base */ base = 10; switch (*fmt) { case 'c': if (!(flags & LEFT)) { while (--field_width > 0) { if (str > end) return -1; *str = ' '; ++str; } } c = (unsigned char)va_arg(args, int); if (str > end) return -1; *str = c; ++str; while (--field_width > 0) { if (str > end) return -1; *str = ' '; ++str; } fmtent->fmtlen = fmt - fmtent->fmt + 1; fmtent->translen = str - fmtent->trans; continue; case 's': s = va_arg(args, char *); if (!s) s = ""; len = std::strlen(s); if (len > precision) len = precision; if (!(flags & LEFT)) { while (len < field_width--) { if (str > end) return -1; *str = ' '; ++str; } } for (i = 0; i < len; ++i) { if (str > end) return -1; *str = *s; ++str; ++s; } while (len < field_width--) { if (str > end) return -1; *str = ' '; ++str; } fmtent->fmtlen = fmt - fmtent->fmt + 1; fmtent->translen = str - fmtent->trans; continue; case 'p': if (field_width == -1) { field_width = 2 * sizeof(void *); flags |= ZEROPAD; } str = number(str, end, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); if (str > end) return -1; fmtent->fmtlen = fmt - fmtent->fmt + 1; fmtent->translen = str - fmtent->trans; continue; case 'n': /* FIXME: * What does C99 say about the overflow case here? */ if (qualifier == 'l') { long * ip = va_arg(args, long *); *ip = (str - buf); } else if (qualifier == 'Z' || qualifier == 'z') { std::size_t * ip = va_arg(args, std::size_t *); *ip = (str - buf); } else { int * ip = va_arg(args, int *); *ip = (str - buf); } fmtent->fmtlen = fmt - fmtent->fmt + 1; fmtent->translen = str - fmtent->trans; continue; case '%': if (str > end) return -1; *str = '%'; ++str; fmtent->fmtlen = fmt - fmtent->fmt + 1; fmtent->translen = str - fmtent->trans; continue; /* integer number formats - set up the flags and "break" */ case 'o': base = 8; break; case 'X': flags |= LARGE; case 'x': base = 16; break; case 'd': case 'i': flags |= SIGN; case 'u': break; default: if (str > end) return -1; *str = '%'; ++str; if (*fmt) { if (str > end) return -1; *str = *fmt; ++str; } else { --fmt; } fmtent->fmtlen = fmt - fmtent->fmt + 1; fmtent->translen = str - fmtent->trans; continue; } /* if (qualifier == 'L') num = va_arg(args, long long); else*/ if (qualifier == 'l') { num = va_arg(args, unsigned long); if (flags & SIGN) num = (signed long)num; } else if (qualifier == 'Z' || qualifier == 'z') { num = va_arg(args, std::size_t); } else if (qualifier == 'h') { num = (unsigned short)va_arg(args, int); if (flags & SIGN) num = (signed short)num; } else { num = va_arg(args, unsigned int); if (flags & SIGN) num = (signed int)num; } str = number(str, end, num, base, field_width, precision, flags); if (str > end) return -1; fmtent->fmtlen = fmt - fmtent->fmt + 1; fmtent->translen = str - fmtent->trans; } if (str <= end) *str = '\0'; /* the trailing null byte doesn't count towards the total * ++str; */ return str - buf; } int asnprintf(char * buf, std::size_t size, t_fmtentry *entries, unsigned entlen, const char *fmt, ...) { std::va_list args; int i; va_start(args, fmt); i = vasnprintf(buf, size, entries, entlen, fmt, args); va_end(args); return i; } } pvpgn-1.99.7.2.1+dfsg/src/common/asnprintf.h000066400000000000000000000037201462640461400204360ustar00rootroot00000000000000/* * Copyright (C) 2005 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ASNPRINTF_H #define ASNPRINTF_H #include #include #define ZEROPAD 1 /* pad with zero */ #define SIGN 2 /* unsigned/signed long */ #define PLUS 4 /* show plus */ #define SPACE 8 /* space if plus */ #define LEFT 16 /* left justified */ #define SPECIAL 32 /* 0x */ #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ namespace pvpgn { typedef struct { const char* trans; /* points to the translation of the format */ unsigned translen; /* how many chars in this translation */ const char* fmt; /* points to the fmt location of this format spec */ unsigned fmtlen; /* how many chars in this fmt spec */ } t_fmtentry; /* "array" snprintf - this function prints all vargs into the prepared buffer, * each of them as a \0 terminated string. The prepared char* [] locations will * contain the starting position of each seperate string afterwards. * The function returns the number of vargs that have been printed. */ int asnprintf(char * buffer, std::size_t size, t_fmtentry *entries, unsigned entlen, const char *fmt, ...); int vasnprintf(char * buffer, std::size_t size, t_fmtentry *entries, unsigned entlen, const char *fmt, std::va_list args); } #endif pvpgn-1.99.7.2.1+dfsg/src/common/bigint.cpp000066400000000000000000000411511462640461400202410ustar00rootroot00000000000000/* * Class for simple, arbitrary size unsigned integer math. * Note that some method implementations might lack features that weren't required so far! * * Copyright (C) 2008 - Olaf Freyer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. */ #include "common/setup_before.h" #include "bigint.h" #include #include #include #include #include #include #include #include #include #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { static const char small_digits[] = "0123456789abcdef"; BigInt::BigInt() { segment_count = 1; segment = (bigint_base*)xmalloc(segment_count * sizeof(bigint_base)); segment[0] = 0; } BigInt::BigInt(std::uint8_t input) { segment_count = 1; segment = (bigint_base*)xmalloc(segment_count * sizeof(bigint_base)); segment[0] = input; } BigInt::BigInt(std::uint16_t input) { segment_count = 1; segment = (bigint_base*)xmalloc(segment_count * sizeof(bigint_base)); segment[0] = input; } BigInt::BigInt(std::uint32_t input) { #ifndef HAVE_UINT64_T int i; #endif segment_count = sizeof(std::uint32_t) / sizeof(bigint_base); segment = (bigint_base*)xmalloc(segment_count * sizeof(bigint_base)); #ifdef HAVE_UINT64_T segment[0] = input; #else for (i = 0; i < segment_count; i++){ segment[i] = input & bigint_base_mask; input >>= bigint_base_bitcount; } #endif } BigInt::BigInt(std::uint64_t input) { int i; segment_count = sizeof(std::uint64_t) / sizeof(bigint_base); segment = (bigint_base*)xmalloc(segment_count * sizeof(bigint_base)); for (i = 0; i < segment_count; i++){ segment[i] = input & bigint_base_mask; input >>= bigint_base_bitcount; } } BigInt::BigInt(const BigInt& input) :segment_count(input.segment_count) { segment = (bigint_base*)xmalloc(segment_count * sizeof(bigint_base)); std::memcpy(segment, input.segment, segment_count * sizeof(bigint_base)); } BigInt& BigInt::operator=(const BigInt& input) { if (&input != this) { segment_count = input.segment_count; segment = (bigint_base*)xrealloc(segment, segment_count * sizeof(bigint_base)); std::memcpy(segment, input.segment, segment_count * sizeof(bigint_base)); } return *this; } BigInt::BigInt(unsigned char const * input, int input_size, int blockSize, bool bigEndian) { int i, j; unsigned char *in; unsigned char *inPointer; if (bigEndian){ in = (unsigned char*)input; inPointer = (unsigned char*)in; } else { in = (unsigned char*)xmalloc(input_size); inPointer = (unsigned char*)in + input_size - 1; if (blockSize == 1) std::memcpy(in, input, input_size); else { assert(blockSize % 2 == 0); for (i = 0; i < input_size; i += blockSize) { for (j = 0; j < blockSize / 2; j++) { in[i + j] = input[i + blockSize - (j + 1)]; in[i + blockSize - (j + 1)] = input[i + j]; } } } } segment_count = input_size / sizeof(bigint_base); if (input_size % sizeof(bigint_base)) segment_count++; segment = (bigint_base*)xmalloc(segment_count * sizeof(bigint_base)); std::memset(segment, 0, segment_count * sizeof(bigint_base)); for (i = input_size - 1; i >= 0; i--) { j = i / sizeof(bigint_base); segment[j] <<= 8; if (bigEndian) segment[j] += *(inPointer++); else segment[j] += *(inPointer--); } if (!bigEndian) xfree(in); } BigInt::~BigInt() throw() { if (segment) xfree(segment); } bool BigInt::operator== (const BigInt& right) const { int i; bool result; result = (segment_count == right.segment_count); for (i = 0; (i < segment_count) && result; i++) result = result && (segment[i] == right.segment[i]); return result; } bool BigInt::operator< (const BigInt& right) const { if (segment_count < right.segment_count) { return true; } else if (segment_count > right.segment_count){ return false; } else { int i; for (i = segment_count - 1; i >= 0; i--) { if (segment[i]right.segment[i]) return false; } } return false; } bool BigInt::operator> (const BigInt& right) const { if (segment_count > right.segment_count) { return true; } else if (segment_count < right.segment_count){ return false; } else { int i; for (i = segment_count - 1; i >= 0; i--) { if (segment[i]>right.segment[i]) return true; else if (segment[i] < right.segment[i]) return false; } } return false; } BigInt BigInt::operator+ (const BigInt& right) const { int i, max_segment_count; bigint_extended sum; bigint_extended lhs, rhs; bigint_base carry = 0; BigInt result; max_segment_count = std::max(segment_count, right.segment_count); result.segment_count = max_segment_count + 1; result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); for (i = 0; i < max_segment_count; i++) { lhs = (i < segment_count) ? segment[i] : 0; rhs = (i> bigint_base_bitcount; } result.segment[i] = carry; for (max_segment_count = result.segment_count - 1; max_segment_count>0; max_segment_count--) { if (result.segment[max_segment_count] != 0) break; } if (result.segment_count != max_segment_count + 1) { result.segment_count = max_segment_count + 1; result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); } return result; } BigInt BigInt::operator- (const BigInt& right) const { int i, max_segment_count; bigint_base diff; bigint_base lhs, rhs; bigint_base carry = 0; BigInt result; // Currently we only implement unsigned math // so we can either throw exception or simply return 0. if (!(this->operator>(right))) { return result; } result.segment_count = segment_count; result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); for (i = 0; i < result.segment_count; i++) { lhs = (i < segment_count) ? segment[i] : 0; rhs = (i < right.segment_count) ? right.segment[i] : 0; rhs += carry; if (lhs < rhs) { diff = (bigint_extended_carry + lhs) - rhs; carry = 1; } else { diff = (lhs)-rhs; carry = 0; } result.segment[i] = diff; } for (max_segment_count = result.segment_count - 1; max_segment_count > 0; max_segment_count--) { if (result.segment[max_segment_count] != 0) break; } if (result.segment_count != max_segment_count + 1) { result.segment_count = max_segment_count + 1; result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); } return result; } BigInt BigInt::operator* (const BigInt& right) const { int i, j, index, max_segment_count; bigint_extended prod, sum; bigint_extended lhs, rhs; bigint_base carry = 0; BigInt result; if ((segment_count == 1 && segment[0] == 0) || (right.segment_count == 1 && right.segment[0] == 0)) return result; result.segment_count = segment_count + right.segment_count; result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); std::memset(result.segment, 0, result.segment_count * sizeof(bigint_base)); for (i = 0; i < segment_count; i++) { for (j = 0; j < right.segment_count; j++) { lhs = (i < segment_count) ? segment[i] : 0; rhs = (j < right.segment_count) ? right.segment[j] : 0; prod = lhs * rhs; index = i + j; sum = result.segment[index] + prod + carry; result.segment[index] = sum & bigint_base_mask; carry = sum >> bigint_base_bitcount; } if (carry) { index = i + j; sum = result.segment[index] + carry; result.segment[index] = sum & bigint_base_mask; carry = sum >> bigint_base_bitcount; } } result.segment[i + j - 1] += carry; for (max_segment_count = result.segment_count - 1; max_segment_count > 0; max_segment_count--) { if (result.segment[max_segment_count] != 0) break; } if (result.segment_count != max_segment_count + 1) { result.segment_count = max_segment_count + 1; result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); } return result; } BigInt BigInt::operator/ (const BigInt& right) const { int i, j, max_segment_count; BigInt quotient; BigInt remainder; BigInt m; BigInt q; BigInt p; bigint_extended n, d, qest; if (this->operator<(right)){ return quotient; } quotient.segment_count = (segment_count - right.segment_count) + 1; quotient.segment = (bigint_base*)xrealloc(quotient.segment, quotient.segment_count*sizeof(bigint_base)); std::memset(quotient.segment, 0, quotient.segment_count * sizeof(bigint_base)); remainder.segment_count = right.segment_count + 1; remainder.segment = (bigint_base*)xrealloc(remainder.segment, remainder.segment_count*sizeof(bigint_base)); std::memset(remainder.segment, 0, remainder.segment_count * sizeof(bigint_base)); for (j = 0; j < right.segment_count; j++){ remainder.segment[j] = segment[(segment_count - right.segment_count) + j]; } n = 0; for (i = segment_count; i >= right.segment_count; i--){ //now do some "educated guessing" //qest=n/q //calculate qest*right // adjust by +/-1 until in target range n = remainder.segment[right.segment_count]; n <<= bigint_base_bitcount; n += remainder.segment[right.segment_count - 1]; d = right.segment[right.segment_count - 1]; qest = n / d; p = BigInt(qest) * right; while (1) { if (p == remainder) { break; } else if (p < remainder) { if ((remainder - p) > right) { qest++; p = p + right; } else { break; } } else { qest--; p = p - right; } }; quotient.segment[i - right.segment_count] = qest & bigint_base_mask; remainder = remainder - p; if (i > right.segment_count) { remainder = remainder << sizeof(bigint_base); remainder.segment[0] = segment[i - (right.segment_count + 1)]; } } for (max_segment_count = quotient.segment_count - 1; max_segment_count > 0; max_segment_count--) { if (quotient.segment[max_segment_count] != 0) break; } if (quotient.segment_count != max_segment_count + 1) { quotient.segment_count = max_segment_count + 1; quotient.segment = (bigint_base*)xrealloc(quotient.segment, quotient.segment_count * sizeof(bigint_base)); } return quotient; } BigInt BigInt::operator% (const BigInt& right) const { int i, j; BigInt remainder; BigInt m; BigInt q; BigInt p; bigint_extended n, d, qest; if (this->operator<(right)){ return *this; } remainder.segment_count = right.segment_count + 1; remainder.segment = (bigint_base*)xrealloc(remainder.segment, remainder.segment_count*sizeof(bigint_base)); std::memset(remainder.segment, 0, remainder.segment_count * sizeof(bigint_base)); for (j = 0; j < right.segment_count; j++){ remainder.segment[j] = segment[(segment_count - right.segment_count) + j]; } n = 0; for (i = segment_count; i >= right.segment_count; i--){ //now do some "educated guessing" //qest=n/q //calculate qest*right // adjust by +/-1 until in target range n = remainder.segment[right.segment_count]; n <<= bigint_base_bitcount; n += remainder.segment[right.segment_count - 1]; d = right.segment[right.segment_count - 1]; qest = n / d; p = BigInt(qest) * right; while (1) { if (p == remainder) { break; } else if (p < remainder) { if ((remainder - p) > right) { qest++; p = p + right; } else { break; } } else { qest--; p = p - right; } }; remainder = remainder - p; if (i > right.segment_count) { remainder = remainder << sizeof(bigint_base); remainder.segment[0] = segment[i - (right.segment_count + 1)]; } } return remainder; } BigInt BigInt::operator<< (int bytesToShift) const { int i; BigInt result; assert(bytesToShift >= 0); //currently we only implement (and need) segmentwise shifting assert(bytesToShift%sizeof(bigint_base) == 0); if (bytesToShift == 0) return result; int segmentsToShift = bytesToShift / sizeof(bigint_base); result.segment_count = segment_count + segmentsToShift; result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); for (i = segment_count - 1; i >= 0; i--) { result.segment[i + segmentsToShift] = segment[i]; } for (i = 0; i < segmentsToShift; i++) { result.segment[i] = 0; } return result; } BigInt BigInt::random(int size) { BigInt result; int i, j; unsigned int r; assert(size > 0); assert(size%sizeof(bigint_base) == 0); result.segment_count = size / sizeof(bigint_base); result.segment = (bigint_base*)xrealloc(result.segment, result.segment_count * sizeof(bigint_base)); for (i = 0; i < result.segment_count; i++){ result.segment[i] = 0; for (j = 0; j < sizeof(bigint_base); j += sizeof(unsigned int)){ r = rand(); result.segment[i] <<= (sizeof(unsigned int)); result.segment[i] = result.segment[i] + r; } } return result; } BigInt BigInt::powm(const BigInt& exp, const BigInt& mod) const { if (exp.segment_count == 1) { if (exp.segment[0] == 0x02) { return (*this * *this) % mod; } else if (exp.segment[0] == 0x01) { return *this; } else if (exp.segment[0] == 0x00) { return BigInt((std::uint8_t)0x01); } } //trying a divide&conquer approach if (exp.segment[0] % 2 == 0) { // exp is even BigInt half = exp / (BigInt((std::uint8_t)0x02)); BigInt halfpow = this->powm(half, mod); return (halfpow * halfpow) % mod; } else { // exp is odd BigInt half = exp / (BigInt((std::uint8_t)0x02)); BigInt halfpow = this->powm(half, mod); return (((halfpow * halfpow) % mod) * *this) % mod; } } unsigned char* BigInt::getData(int byteCount, int blockSize, bool bigEndian) const { unsigned char* result; result = (unsigned char*)xmalloc(byteCount); getData(result, byteCount, blockSize, bigEndian); return result; } void BigInt::getData(unsigned char* out, int byteCount, int blockSize, bool bigEndian) const { int i, j; unsigned char* pos; std::memset(out, 0, byteCount); pos = out + (byteCount - 1); for (i = 0; i < segment_count; i++) { bigint_base data; data = segment[segment_count - (i + 1)]; for (j = 0; j < sizeof(bigint_base); j++) { if (pos < out) break; *pos = data & 0xff; pos--; data >>= 8; } } if (!bigEndian && blockSize>1) { unsigned char val; assert(blockSize % 2 == 0); for (i = 0; i < byteCount; i += blockSize) { for (j = 0; j < blockSize / 2; j++) { val = out[i + j]; out[i + j] = out[i + blockSize - (j + 1)]; out[i + blockSize - (j + 1)] = val; } } } } std::string BigInt::toHexString() const { int i; char data; std::ostringstream result; bigint_base* src; // handle most significant segment src = &segment[segment_count - 1]; if (segment_count == 1 && *src == 0) { result << "00"; } else { int sum = 0; for (i = sizeof(bigint_base)-1; i >= 0; i--) { data = ((*src) & (0xff << (i * 8))) >> (i * 8); sum += data; if (sum) { result << small_digits[(data & 0xf0) >> 4]; result << small_digits[(data & 0x0f)]; } } // handle rest for (src = &segment[segment_count - 2]; src >= segment; src--) { for (i = sizeof(bigint_base)-1; i >= 0; i--) { data = ((*src) & (0xff << (i * 8))) >> (i * 8); result << small_digits[(data & 0xf0) >> 4]; result << small_digits[(data & 0x0f)]; } } } return result.str(); } } pvpgn-1.99.7.2.1+dfsg/src/common/bigint.h000066400000000000000000000043761462640461400177160ustar00rootroot00000000000000/* * Class for simple, arbitrary size unsigned integer math. * Note that some method implementations might lack features that weren't required so far! * * Copyright (C) 2008 - Olaf Freyer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. */ #ifndef INCLUDED_BIGINT_H #define INCLUDED_BIGINT_H #include #include namespace pvpgn { class BigInt { public: BigInt(); explicit BigInt(std::uint8_t input); explicit BigInt(std::uint16_t input); explicit BigInt(std::uint32_t input); BigInt(const BigInt& input); BigInt& operator=(const BigInt& input); BigInt(unsigned char const* input, int input_size, int blockSize = 1, bool bigEndian = true); ~BigInt() throw (); bool operator== (const BigInt& right) const; bool operator< (const BigInt& right) const; bool operator> (const BigInt& right) const; BigInt operator+ (const BigInt& right) const; BigInt operator- (const BigInt& right) const; BigInt operator* (const BigInt& right) const; BigInt operator/ (const BigInt& right) const; BigInt operator% (const BigInt& right) const; BigInt operator<< (int bytesToShift) const; static BigInt random(int size); static void status(); BigInt powm(const BigInt& exp, const BigInt& mod) const; unsigned char* getData(int byteCount, int blockSize = 1, bool bigEndian = true) const; void getData(unsigned char* out, int byteCount, int blockSize = 1, bool bigEndian = true) const; std::string toHexString() const; private: explicit BigInt(std::uint64_t input); using bigint_base = std::uint32_t; using bigint_extended = std::uint64_t; #define bigint_base_mask 0xffffffff #define bigint_extended_carry bigint_base_mask+0x01 #define bigint_base_bitcount (sizeof(bigint_base)*8) bigint_base *segment; int segment_count; }; } #endif /* __BIGINT_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/common/bn_type.cpp000066400000000000000000000327331462640461400204330ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/bn_type.h" #include #include #include "common/eventlog.h" #include "common/setup_after.h" /************************************************************/ namespace pvpgn { extern int bn_byte_tag_get(bn_byte const * src, char * dst, unsigned int len) { unsigned int i; if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (len < 1) { eventlog(eventlog_level_error, __FUNCTION__, "got zero len"); return -1; } for (i = 0; i < len - 1 && i < 1; i++) dst[i] = (char)(*src)[-i]; dst[i] = '\0'; return 0; } extern int bn_short_tag_get(bn_short const * src, char * dst, unsigned int len) { unsigned int i; if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (len < 1) { eventlog(eventlog_level_error, __FUNCTION__, "got zero len"); return -1; } for (i = 0; i < len - 1 && i < 2; i++) dst[i] = (char)(*src)[1 - i]; dst[i] = '\0'; return 0; } extern int bn_int_tag_get(bn_int const * src, char * dst, unsigned int len) { unsigned int i; if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (len < 1) { eventlog(eventlog_level_error, __FUNCTION__, "got zero len"); return -1; } for (i = 0; i < len - 1 && i < 4; i++) dst[i] = (char)(*src)[3 - i]; dst[i] = '\0'; return 0; } extern int bn_long_tag_get(bn_long const * src, char * dst, unsigned int len) { unsigned int i; if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (len < 1) { eventlog(eventlog_level_error, __FUNCTION__, "got zero len"); return -1; } for (i = 0; i < len - 1 && i < 8; i++) dst[i] = (char)(*src)[7 - i]; dst[i] = '\0'; return 0; } /************************************************************/ extern int bn_byte_tag_set(bn_byte * dst, char const * tag) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } (*dst)[0] = (unsigned char)tag[0]; return 0; } extern int bn_short_tag_set(bn_short * dst, char const * tag) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } (*dst)[0] = (unsigned char)tag[3]; (*dst)[1] = (unsigned char)tag[2]; return 0; } extern int bn_int_tag_set(bn_int * dst, char const * tag) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } (*dst)[0] = (unsigned char)tag[3]; (*dst)[1] = (unsigned char)tag[2]; (*dst)[2] = (unsigned char)tag[1]; (*dst)[3] = (unsigned char)tag[0]; return 0; } extern int bn_long_tag_set(bn_long * dst, char const * tag) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } (*dst)[0] = (unsigned char)tag[7]; (*dst)[1] = (unsigned char)tag[6]; (*dst)[2] = (unsigned char)tag[5]; (*dst)[3] = (unsigned char)tag[4]; (*dst)[4] = (unsigned char)tag[3]; (*dst)[5] = (unsigned char)tag[2]; (*dst)[6] = (unsigned char)tag[1]; (*dst)[7] = (unsigned char)tag[0]; return 0; } /************************************************************/ extern std::uint8_t bn_byte_get(bn_byte const src) { std::uint8_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint8_t)src[0]); return temp; } extern std::uint16_t bn_short_get(bn_short const src) { std::uint16_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint16_t)src[0]); temp |= ((std::uint16_t)src[1]) << 8; return temp; } extern std::uint16_t bn_short_nget(bn_short const src) { std::uint16_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint16_t)src[1]); temp |= ((std::uint16_t)src[0]) << 8; return temp; } extern std::uint32_t bn_int_get(bn_int const src) { std::uint32_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint32_t)src[0]); temp |= ((std::uint32_t)src[1]) << 8; temp |= ((std::uint32_t)src[2]) << 16; temp |= ((std::uint32_t)src[3]) << 24; return temp; } extern std::uint32_t bn_int_nget(bn_int const src) { std::uint32_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint32_t)src[3]); temp |= ((std::uint32_t)src[2]) << 8; temp |= ((std::uint32_t)src[1]) << 16; temp |= ((std::uint32_t)src[0]) << 24; return temp; } extern std::uint64_t bn_long_get(bn_long const src) { std::uint64_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint64_t)src[0]); temp |= ((std::uint64_t)src[1]) << 8; temp |= ((std::uint64_t)src[2]) << 16; temp |= ((std::uint64_t)src[3]) << 24; temp |= ((std::uint64_t)src[4]) << 32; temp |= ((std::uint64_t)src[5]) << 40; temp |= ((std::uint64_t)src[6]) << 48; temp |= ((std::uint64_t)src[7]) << 56; return temp; } extern std::uint32_t bn_long_get_a(bn_long const src) { std::uint32_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint32_t)src[4]); temp |= ((std::uint32_t)src[5]) << 8; temp |= ((std::uint32_t)src[6]) << 16; temp |= ((std::uint32_t)src[7]) << 24; return temp; } extern std::uint32_t bn_long_get_b(bn_long const src) { std::uint32_t temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return 0; } temp = ((std::uint32_t)src[0]); temp |= ((std::uint32_t)src[1]) << 8; temp |= ((std::uint32_t)src[2]) << 16; temp |= ((std::uint32_t)src[3]) << 24; return temp; } /************************************************************/ extern int bn_byte_set(bn_byte * dst, std::uint8_t src) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((src)); return 0; } extern int bn_short_set(bn_short * dst, std::uint16_t src) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((src)& 0xff); (*dst)[1] = (std::uint8_t)((src >> 8)); return 0; } extern int bn_short_nset(bn_short * dst, std::uint16_t src) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((src >> 8)); (*dst)[1] = (std::uint8_t)((src)& 0xff); return 0; } extern int bn_int_set(bn_int * dst, std::uint32_t src) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((src)& 0xff); (*dst)[1] = (std::uint8_t)((src >> 8) & 0xff); (*dst)[2] = (std::uint8_t)((src >> 16) & 0xff); (*dst)[3] = (std::uint8_t)((src >> 24)); return 0; } extern int bn_int_nset(bn_int * dst, std::uint32_t src) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((src >> 24)); (*dst)[1] = (std::uint8_t)((src >> 16) & 0xff); (*dst)[2] = (std::uint8_t)((src >> 8) & 0xff); (*dst)[3] = (std::uint8_t)((src)& 0xff); return 0; } extern int bn_long_set(bn_long * dst, std::uint64_t src) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((src)& 0xff); (*dst)[1] = (std::uint8_t)((src >> 8) & 0xff); (*dst)[2] = (std::uint8_t)((src >> 16) & 0xff); (*dst)[3] = (std::uint8_t)((src >> 24) & 0xff); (*dst)[4] = (std::uint8_t)((src >> 32) & 0xff); (*dst)[5] = (std::uint8_t)((src >> 40) & 0xff); (*dst)[6] = (std::uint8_t)((src >> 48) & 0xff); (*dst)[7] = (std::uint8_t)((src >> 56)); return 0; } extern int bn_long_nset(bn_long * dst, std::uint64_t src) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((src >> 56)); (*dst)[1] = (std::uint8_t)((src >> 48) & 0xff); (*dst)[2] = (std::uint8_t)((src >> 40) & 0xff); (*dst)[3] = (std::uint8_t)((src >> 32) & 0xff); (*dst)[4] = (std::uint8_t)((src >> 24) & 0xff); (*dst)[5] = (std::uint8_t)((src >> 16) & 0xff); (*dst)[6] = (std::uint8_t)((src >> 8) & 0xff); (*dst)[7] = (std::uint8_t)((src)& 0xff); return 0; } extern int bn_long_set_a_b(bn_long * dst, std::uint32_t srca, std::uint32_t srcb) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((srcb)& 0xff); (*dst)[1] = (std::uint8_t)((srcb >> 8) & 0xff); (*dst)[2] = (std::uint8_t)((srcb >> 16) & 0xff); (*dst)[3] = (std::uint8_t)((srcb >> 24) & 0xff); (*dst)[4] = (std::uint8_t)((srca)& 0xff); (*dst)[5] = (std::uint8_t)((srca >> 8) & 0xff); (*dst)[6] = (std::uint8_t)((srca >> 16) & 0xff); (*dst)[7] = (std::uint8_t)((srca >> 24)); return 0; } extern int bn_long_nset_a_b(bn_long * dst, std::uint32_t srca, std::uint32_t srcb) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } (*dst)[0] = (std::uint8_t)((srca >> 24)); (*dst)[1] = (std::uint8_t)((srca >> 16) & 0xff); (*dst)[2] = (std::uint8_t)((srca >> 8) & 0xff); (*dst)[3] = (std::uint8_t)((srca)& 0xff); (*dst)[4] = (std::uint8_t)((srcb >> 24) & 0xff); (*dst)[5] = (std::uint8_t)((srcb >> 16) & 0xff); (*dst)[6] = (std::uint8_t)((srcb >> 8) & 0xff); (*dst)[7] = (std::uint8_t)((srcb)& 0xff); return 0; } /************************************************************/ extern int bn_raw_set(void * dst, void const * src, unsigned int len) { if (!dst) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL dst"); return -1; } if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } std::memcpy(dst, src, len); return 0; } /************************************************************/ extern int bn_byte_tag_eq(bn_byte const src, char const * tag) { bn_byte temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } if (bn_byte_tag_set(&temp, tag) < 0) return -1; if (bn_byte_get(src) == bn_byte_get(temp)) return 0; return -1; } extern int bn_short_tag_eq(bn_short const src, char const * tag) { bn_short temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } if (bn_short_tag_set(&temp, tag) < 0) return -1; if (bn_short_get(src) == bn_short_get(temp)) return 0; return -1; } extern int bn_int_tag_eq(bn_int const src, char const * tag) { bn_int temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } if (bn_int_tag_set(&temp, tag) < 0) return -1; if (bn_int_get(src) == bn_int_get(temp)) return 0; return -1; } extern int bn_long_tag_eq(bn_long const src, char const * tag) { bn_long temp; if (!src) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL src"); return -1; } if (!tag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return -1; } if (bn_long_tag_set(&temp, tag) < 0) return -1; if (bn_long_get_a(src) == bn_long_get_a(temp) && bn_long_get_b(src) == bn_long_get_b(temp)) return 0; return -1; } /************************************************************/ extern int uint32_to_int(std::uint32_t num) { if (num < (1UL << 30)) return (int)num; return (-(int)((~(num)) + 1)); } } pvpgn-1.99.7.2.1+dfsg/src/common/bn_type.h000066400000000000000000000061261462640461400200750ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_BN_TYPE_TYPES #define INCLUDED_BN_TYPE_TYPES #include namespace pvpgn { using bn_basic = std::uint8_t; using bn_byte = bn_basic[1]; using bn_short = bn_basic[2]; using bn_int = bn_basic[4]; using bn_long = bn_basic[8]; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_BN_TYPE_PROTOS #define INCLUDED_BN_TYPE_PROTOS namespace pvpgn { extern int bn_byte_tag_get(bn_byte const * src, char * dst, unsigned int len); extern int bn_short_tag_get(bn_short const * src, char * dst, unsigned int len); extern int bn_int_tag_get(bn_int const * src, char * dst, unsigned int len); extern int bn_long_tag_get(bn_long const * src, char * dst, unsigned int len); extern int bn_byte_tag_set(bn_byte * dst, char const * tag); extern int bn_short_tag_set(bn_short * dst, char const * tag); extern int bn_int_tag_set(bn_int * dst, char const * tag); extern int bn_long_tag_set(bn_long * dst, char const * tag); extern std::uint8_t bn_byte_get(bn_byte const src); extern std::uint16_t bn_short_get(bn_short const src); extern std::uint16_t bn_short_nget(bn_short const src); extern std::uint32_t bn_int_get(bn_int const src); extern std::uint32_t bn_int_nget(bn_int const src); extern std::uint64_t bn_long_get(bn_long const src); extern std::uint32_t bn_long_get_a(bn_long const src); extern std::uint32_t bn_long_get_b(bn_long const src); extern int bn_byte_set(bn_byte * dst, std::uint8_t src); extern int bn_short_set(bn_short * dst, std::uint16_t src); extern int bn_short_nset(bn_short * dst, std::uint16_t src); extern int bn_int_set(bn_int * dst, std::uint32_t src); extern int bn_int_nset(bn_int * dst, std::uint32_t src); extern int bn_long_set(bn_long * dst, std::uint64_t src); extern int bn_long_nset(bn_long * dst, std::uint64_t src); extern int bn_long_set_a_b(bn_long * dst, std::uint32_t srca, std::uint32_t srcb); extern int bn_long_nset_a_b(bn_long * dst, std::uint32_t srca, std::uint32_t srcb); extern int bn_raw_set(void * dst, void const * src, unsigned int len); extern int bn_byte_tag_eq(bn_byte const src, char const * tag); extern int bn_short_tag_eq(bn_short const src, char const * tag); extern int bn_int_tag_eq(bn_int const src, char const * tag); extern int bn_long_tag_eq(bn_long const src, char const * tag); extern int uint32_to_int(std::uint32_t num); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/bnet_protocol.h000066400000000000000000004674001462640461400213140ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_BNET_PROTOCOL_TYPES #define INCLUDED_BNET_PROTOCOL_TYPES #ifdef JUST_NEED_TYPES # include "common/bn_type.h" #else # define JUST_NEED_TYPES # include "common/bn_type.h" # undef JUST_NEED_TYPES #endif /* * The "bnet" protocol (previously known as "normal") that is used for * chatting, ladder info, game listings, etc. * FIXME: put the #define's into the PROTO section */ namespace pvpgn { /******************************************************/ typedef struct { bn_short type; bn_short size; } PACKED_ATTR() t_bnet_header; /******************************************************/ /* [zap-zero] 20020529 - added support for war3 anongame routing packets */ /******************************************************/ typedef struct { bn_short type; bn_short size; } PACKED_ATTR() t_w3route_header; /******************************************************/ /******************************************************/ typedef struct { t_bnet_header h; } PACKED_ATTR() t_bnet_generic; /******************************************************/ /******************************************************/ typedef struct { t_w3route_header h; } PACKED_ATTR() t_w3route_generic; /******************************************************/ /* for unhandled pmap packets */ #define CLIENT_NULL 0xfeff /******************************************************/ /* 14: recv class=w3route[0x0c] type=unknown[0x00f7] length=53 0000: F7 1E 35 00 02 02 00 00 68 BF 0B 08 00 E0 17 01 ..5.....h....... 0010: 00 00 00 42 6C 61 63 6B 72 61 74 00 08 81 3E 0C ...Blackrat...>. 0020: 00 20 00 00 00 02 00 17 E1 C0 A8 00 01 00 00 00 . .............. 0030: 00 00 00 00 00 ..... */ #define CLIENT_W3ROUTE_REQ 0x1ef7 typedef struct { t_w3route_header h; bn_int unknown1; bn_int id; /* unique id sent by server */ bn_byte unknown2; bn_short port; bn_int handle; /* handle/descriptor for client <-> client communication */ /* player name, ... */ } PACKED_ATTR() t_client_w3route_req; /******************************************************/ /******************************************************/ /* f7 23 04 00 */ #define CLIENT_W3ROUTE_LOADINGDONE 0x23f7 typedef struct { t_w3route_header h; } PACKED_ATTR() t_client_w3route_loadingdone; /******************************************************/ /******************************************************/ /* f7 14 05 00 00 */ #define SERVER_W3ROUTE_READY 0x14f7 typedef struct { t_w3route_header h; bn_byte unknown1; } PACKED_ATTR() t_server_w3route_ready; /******************************************************/ /******************************************************/ /* f7 21 08 00 01 00 00 00 */ #define CLIENT_W3ROUTE_ABORT 0x21f7 typedef struct { t_w3route_header h; bn_int unknown1; /* count? */ } PACKED_ATTR() t_client_w3route_abort; /******************************************************/ /* f7 08 05 00 02 */ #define SERVER_W3ROUTE_LOADINGACK 0x08f7 typedef struct { t_w3route_header h; bn_byte playernum; } PACKED_ATTR() t_server_w3route_loadingack; /******************************************************/ /******************************************************/ /* f7 3b 06 00 09 00 */ #define CLIENT_W3ROUTE_CONNECTED 0x3bf7 typedef struct { t_w3route_header h; bn_short unknown1; } PACKED_ATTR() t_client_w3route_connected; /******************************************************/ /******************************************************/ /* f7 01 08 00 2e 85 2d 7b */ #define SERVER_W3ROUTE_ECHOREQ 0x01f7 typedef struct { t_w3route_header h; bn_int ticks; } PACKED_ATTR() t_server_w3route_echoreq; /******************************************************/ #define CLIENT_W3ROUTE_ECHOREPLY 0x46f7 /******************************************************/ /* 13: recv class=w3route[0x0c] type=unknown[0x2ef7] length=107 0000: F7 2E 6B 00 01 01 03 00 00 00 20 00 00 00 5C 01 ..k....... ...\. 0010: 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 1A ................ 0020: 04 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 ................ 0030: 00 00 00 01 00 00 00 00 00 00 00 05 00 00 00 00 ................ 0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0060: 00 00 00 00 00 00 00 00 00 00 00 ........... */ #define CLIENT_W3ROUTE_GAMERESULT 0x2ef7 /* F7 3A-84 00 02 01 04 00 00 00 0x0040 20 00 00 00 83 09 00 00-00 00 00 00 02 03 00 00 0x0050 00 20 00 00 00 2C 01 00-00 00 00 00 00 5C 04 00 0x0060 00 00 03 00 00 00 00 00-00 00 01 00 00 00 CC 0B 0x0070 00 00 00 00 00 00 72 0B-00 00 0E 00 00 00 00 00 0x0080 00 00 04 00 00 00 01 00-00 00 15 00 00 00 00 00 0x0090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 0x00A0 00 00 46 0A 00 00 2C 01-00 00 00 00 00 00 00 00 0x00B0 00 00 00 00 00 00 00 00-00 00 F7 3A-84 00 02 02 03 00 00 00 0x0040 20 00 00 00 2C 01 00 00-00 00 00 00 01 04 00 00 0x0050 00 20 00 00 00 83 09 00-00 00 00 00 00 5C 04 00 0x0060 00 00 01 00 00 00 01 00-00 00 00 00 00 00 39 03 0x0070 00 00 00 00 00 00 00 00-00 00 05 00 00 00 00 00 0x0080 00 00 01 00 00 00 00 00-00 00 05 00 00 00 00 00 0x0090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 0x00A0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 0x00B0 00 00 00 00 00 00 00 00-00 00 */ #define CLIENT_W3ROUTE_GAMERESULT_W3XP 0x3af7 typedef struct { t_w3route_header h; bn_byte number_of_results; /* t_client_w3route_gameresult_player players[]; 1-n times t_client_w3route_gameresult_part2 part2; t_client_w3route_gameresult_hero heroes[]; 0-n times t_client_w3route_gameresult_part3 part3; */ } PACKED_ATTR() t_client_w3route_gameresult; /******************************************************/ /* [zap-zero] what value is DRAW? */ #define W3_GAMERESULT_LOSS 0x00000003 #define W3_GAMERESULT_WIN 0x00000004 typedef struct { bn_byte number; bn_int result; bn_int race; bn_int unknown1; bn_int unknown2; } PACKED_ATTR() t_client_w3route_gameresult_player; typedef struct { bn_byte unknown0; /* but could also contain info about result */ bn_int unknown1; bn_int unknown2; bn_int unknown3; bn_int unknown4; bn_int unit_score; bn_int heroes_score; bn_int resource_score; bn_int units_produced; bn_int units_killed; bn_int buildings_produced; bn_int buildings_razed; bn_int largest_army; bn_int heroes_used_count; } PACKED_ATTR() t_client_w3route_gameresult_part2; typedef struct { bn_short level; bn_int race_and_name; bn_int hero_xp; } PACKED_ATTR() t_client_w3route_gameresult_hero; typedef struct { bn_int heroes_killed; bn_int items_obtained; bn_int mercenaries_hired; bn_int total_hero_xp; bn_int gold_mined; bn_int lumber_harvested; bn_int resources_traded_given; bn_int resources_traded_taken; bn_int tech_percentage; bn_int gold_lost_to_upkeep; } PACKED_ATTR() t_client_w3route_gameresult_part3; /******************************************************/ /* f7 04 1e 00 07 00 00 b2 72 76 ..............rv 0040 ec cc cc 02 02 00 04 0b d9 e9 5f ac 00 00 00 00 .........._..... 0050 00 00 00 00 .... */ #define SERVER_W3ROUTE_ACK 0x04f7 typedef struct { t_w3route_header h; bn_byte unknown1; /* 07 */ bn_short unknown2; /* 00 00 */ bn_int unknown3; /* random stuff */ bn_short unknown4; /* cc cc */ bn_byte playernum; /* 1-4 */ bn_short unknown5; /* 0x0002 */ bn_short port; /* client port */ bn_int ip; /* client ip */ bn_int unknown7; /* 00 00 00 00 */ bn_int unknown8; /* 00 00 00 00 */ } PACKED_ATTR() t_server_w3route_ack; /******************************************************/ #define SERVER_W3ROUTE_ACK_UNKNOWN3 0x484e2637 /******************************************************/ /* f7 06 3b 00 01 00 00 00 01 ** ........;......P 0040 ** ** ** ** ** ** ** 00 08 2d 09 9d 04 08 00 00 layer00..-...... 0050 00 02 00 17 e0 [censored] 00 00 00 00 00 00 00 .......g........ 0060 00 02 00 17 e0 [censored] 00 00 00 00 00 00 00 .......g........ 0070 00 */ #define SERVER_W3ROUTE_PLAYERINFO 0x06f7 typedef struct { t_w3route_header h; bn_int handle; bn_byte playernum; /* then: opponent name playerinfo2 playerinfo_addr (external addr) playerinfo_addr (local lan addr) */ } PACKED_ATTR() t_server_w3route_playerinfo; typedef struct { bn_byte unknown1; /* 8 (length?) */ bn_int id; /* id from FINDANONGAME_SEARCH packet */ bn_int race; /* see defines */ } PACKED_ATTR() t_server_w3route_playerinfo2; #define SERVER_W3ROUTE_LEVELINFO 0x47f7 typedef struct { t_w3route_header h; bn_byte numplayers; /* then: levelinfo2 for each player */ } PACKED_ATTR() t_server_w3route_levelinfo; typedef struct { bn_byte plnum; bn_byte unknown1; /* 3 (length?) */ bn_byte level; bn_short unknown2; } PACKED_ATTR() t_server_w3route_levelinfo2; typedef struct { bn_short unknown1; /* 2 */ bn_short port; bn_int ip; bn_int unknown2; /* 0 */ bn_int unknown3; /* 0 */ } PACKED_ATTR() t_server_w3route_playerinfo_addr; typedef struct { t_w3route_header h; /* f7 0a 04 00 */ } PACKED_ATTR() t_server_w3route_startgame1; #define SERVER_W3ROUTE_STARTGAME1 0x0af7 typedef struct { t_w3route_header h; /* f7 0b 04 00 */ } PACKED_ATTR() t_server_w3route_startgame2; #define SERVER_W3ROUTE_STARTGAME2 0x0bf7 /*******************************************************/ /* ALL ANONGAME PACKET INFO (0x44ff) MOVED TO anongame_protocol.h [Omega] */ /******************************************************/ /* These two dumps are from the original unpatched Starcraft client: FF 05 28 00 01 00 00 00 ..(..... D1 43 88 AA DA 9D 1B 00 9A F7 69 AB 4A 41 32 30 .C........i.JA20 35 43 2D 30 34 00 6C 61 62 61 73 73 69 73 74 00 5C-04.labassist. FF 05 24 00 01 00 00 00 D1 43 88 AA DA 9D 1B 00 ..$......C...... 9A F7 69 AB 42 4F 42 20 20 20 20 20 20 20 20 00 ..i.BOB . 42 6F 62 00 Bob. ??? note it sends NO host and user strings FF 05 14 00 01 00 00 00 D8 94 F6 07 B3 2C 6E 02 .............,n. B4 E0 3B 6C ..;l ??? sent right after it... request for session key? FF 28 08 00 F6 0F 08 00 .(...... Diablo II 1.03 ... note it sends NO host and user strings FF 05 14 00 01 00 00 00 D1 43 88 AA DA 9D ...... ...C.... 1B 00 9A F7 69 AB ....i. */ #define CLIENT_COMPINFO1 0x05ff typedef struct { t_bnet_header h; bn_int reg_version; /* 01 00 00 00 */ bn_int reg_auth; /* D1 43 88 AA */ /* looks like server ip */ bn_int client_id; /* DA 9D 1B 00 */ bn_int client_token; /* 9A F7 69 AB */ /* host */ /* optional */ /* user */ /* optional */ } PACKED_ATTR() t_client_compinfo1; #define CLIENT_COMPINFO1_REG_VERSION 0x00000001 #define CLIENT_COMPINFO1_REG_AUTH 0xaa8843d1 #define CLIENT_COMPINFO1_CLIENT_ID 0x001b9dda #define CLIENT_COMPINFO1_CLIENT_TOKEN 0xab69f79a /******************************************************/ /******************************************************/ /* CLIENT_COMPINFO2 was first seen in Starcraft 1.05 FF 1E 24 00 01 00 00 00 ..$..... 01 00 00 00 D1 43 88 AA 1C B9 48 00 31 8A F2 89 .....C....H.1... 43 4C 4F 55 44 00 63 6C 6F 75 64 00 CLOUD.cloud. FF 1E 28 00 01 00 00 00 01 00 00 00 D1 43 88 AA ..(..........C.. DA 9D 1B 00 9A F7 69 AB 42 4F 42 20 20 20 20 20 ......i.BOB 20 20 20 00 42 6F 62 00 .Bob. Diablo II 1.03 ... note it sends empty host and user strings FF 1E 1A 00 01 00 00 00 01 00 00 00 D1 43 ............C 88 AA DA 9D 1B 00 9A F7 69 AB 00 00 .......i... */ #define CLIENT_COMPINFO2 0x1eff typedef struct { t_bnet_header h; bn_int unknown1; /* 01 00 00 00 */ /* ??? Version */ bn_int reg_version; /* 01 00 00 00 */ bn_int reg_auth; /* D1 43 88 AA */ /* looks like server ip */ bn_int client_id; /* 1C B9 48 00 */ /* DA 9D 1B 00 */ bn_int client_token; /* 31 8A f2 89 */ /* 9A F7 69 AB */ /* host */ /* user */ } PACKED_ATTR() t_client_compinfo2; #define CLIENT_COMPINFO2_UNKNOWN1 0x00000001 #define CLIENT_COMPINFO2_REG_VERSION 0x00000001 #define CLIENT_COMPINFO2_REG_AUTH 0xaa8843d1 #define CLIENT_COMPINFO2_CLIENT_ID 0x001b9dda #define CLIENT_COMPINFO2_CLIENT_TOKEN 0xab69f79a /******************************************************/ /******************************************************/ /* Sent in response to CLIENT_COMPINFO[12] along with sessionkey FF 05 14 00 01 00 00 00 ........ D1 43 88 AA 1C B9 48 00 31 8A F2 89 .C....H.1... FF 05 14 00 01 00 00 00 D8 94 F6 07 3F 62 6E 02 ............?bn. CA A4 0D 99 .... FF 05 14 00 01 00 00 00 D8 94 F6 07 B3 2C 6E 02 .............,n. B4 E0 3B 6C ..;l To D2 Beta 1.02: FF 05 14 00 01 00 00 00 D1 43 88 AA 42 8D 2E 02 .........C..B... 2B 81 8C 2B +..+ */ #define SERVER_COMPREPLY 0x05ff typedef struct { t_bnet_header h; bn_int reg_version; /* 01 00 00 00 */ bn_int reg_auth; /* D1 43 88 AA */ /* looks like server ip */ bn_int client_id; /* DA 9D 1B 00 */ /* 1C B9 48 00 */ bn_int client_token; /* 9A F7 69 AB */ /* 31 8A F2 89 */ } PACKED_ATTR() t_server_compreply; #define SERVER_COMPREPLY_REG_VERSION 0x00000001 #define SERVER_COMPREPLY_REG_AUTH 0xaa8843d1 #define SERVER_COMPREPLY_CLIENT_ID 0x001b9dda #define SERVER_COMPREPLY_CLIENT_TOKEN 0xab69f79a /******************************************************/ /******************************************************/ /* Sent in repsonse to COMPINFO1 along with COMPINFOREPLY. Used for password hashing by the client. */ #define SERVER_SESSIONKEY1 0x28ff typedef struct { t_bnet_header h; bn_int sessionkey; } PACKED_ATTR() t_server_sessionkey1; /******************************************************/ /******************************************************/ /* Sent in response to COMPINFO2 along with COMPINFOREPLY. Used for password hashing by the client. FF 1D 0C 00 40 24 02 00 ....@$.. 99 F3 FD 78 ...x FF 1D 0C 00 0C 67 08 00 7A 3C D8 75 .....g..z<.u FF 1D 0C 00 58 77 00 00 27 45 44 7A ....Xw..'EDz FF 1D 0C 00 9D DF 01 00 7A 11 07 ED ........z... */ #define SERVER_SESSIONKEY2 0x1dff typedef struct { t_bnet_header h; bn_int sessionnum; bn_int sessionkey; } PACKED_ATTR() t_server_sessionkey2; #define SERVER_SESSIONKEY2_UNKNOWN1 0x00004df3 /******************************************************/ /******************************************************/ /* FF 12 3C 00 E0 28 02 E4 0A 37 BE 01 E0 50 A3 37 ..<..(...7...P.7 D0 36 BE 01 A4 01 00 00 09 04 00 00 09 04 00 00 .6.............. 09 04 00 00 65 6E 75 00 31 00 55 53 41 00 55 6E ....enu.1.USA.Un 69 74 65 64 20 53 74 61 74 65 73 00 ited States. still original client, but at a later date FF 12 3C 00 60 C5 4B 8B 19 DE BE 01 60 55 B1 40 ..<.`.K.....`U.@ E7 DD BE 01 A4 01 00 00 09 04 00 00 09 04 00 00 ................ 09 04 00 00 65 6E 75 00 31 00 55 53 41 00 55 6E ....enu.1.USA.Un 69 74 65 64 20 53 74 61 74 65 73 00 ited States. FF 12 3C 00 60 EA 02 23 F5 DE BE 01 60 7A 68 D8 ..<.`..#....`zh. C2 DE BE 01 A4 01 00 00 09 04 00 00 09 04 00 00 ................ 09 04 00 00 65 6E 75 00 31 00 55 53 41 00 55 6E ....enu.1.USA.Un 69 74 65 64 20 53 74 61 74 65 73 00 ited States. FF 12 35 00 ..5. 20 BA B0 55 F2 7B BE 01 20 62 98 C5 3D 7C BE 01 ..U.{.. b..=|.. E4 FD FF FF 12 04 00 00 12 04 00 00 12 04 00 00 ................ 6B 6F 72 00 38 32 00 4B 4F 52 00 4B 6F 72 65 61 kor.82.KOR.Korea 00 . FF 12 37 00 E0 D4 72 97 2F 8C BF 01 E0 3C 37 F9 ..7...r./....<7. 37 8C BF 01 C4 FF FF FF 07 04 00 00 07 04 00 00 7............... 07 04 00 00 64 65 75 00 34 39 00 44 45 55 00 47 ....deu.49.DEU.G 65 72 6D 61 6E 79 00 ermany. FF 12 36 00 20 F3 31 08 40 A7 BF 01 20 C3 BA CB ..6. .1.@... ... 50 A7 BF 01 C4 FF FF FF 1D 04 00 00 1D 04 00 00 P............... 1D 04 00 00 73 76 65 00 34 36 00 53 57 45 00 53 ....sve.46.SWE.S 77 65 64 65 6E 00 weden. Diablo II 1.03 FF 12 39 00 A0 DB AA 45 51 3F C0 01 A0 EB ..9....EQ?.... 56 17 A5 3F C0 01 A8 FD FF FF 09 0C 00 00 09 0C V..?............ 00 00 09 0C 00 00 65 6E 61 00 36 31 00 41 55 53 ......ena.61.AUS 00 41 75 73 74 72 61 6C 69 61 00 .Australia. */ #define CLIENT_COUNTRYINFO1 0x12ff typedef struct { t_bnet_header h; bn_long systemtime; /* GMT */ bn_long localtime; /* time in local timezone */ bn_int bias; /* (gmt-local)/60 (using signed math) */ bn_int langid1; /* 09 04 00 00 */ /* 12 04 00 00 */ bn_int langid2; /* 09 04 00 00 */ /* 12 04 00 00 */ bn_int langid3; /* 09 04 00 00 */ /* 12 04 00 00 */ /* langstr */ /* countrycode (long distance phone) */ /* countryabbrev */ /* countryname */ } PACKED_ATTR() t_client_countryinfo1; /******************************************************/ /******************************************************/ /* First seen in Diablo II (and LoD) 1.09 FF 50 34 00 00 00 00 00 36 38 58 49 50 58 32 44 .P4.....68XIPX2D 09 00 00 00 00 00 00 00 00 00 00 00 C4 FF FF FF ................ 07 04 00 00 07 04 00 00 44 45 55 00 47 65 72 6D ........DEU.Germ 61 6E 79 00 any. FF 50 47 00 00 00 00 00 36 38 58 49 56 44 32 44 .PG.....68XIVD2D 09 00 00 00 00 00 00 00 00 00 00 00 20 FE FF FF ............ ... 04 08 00 00 04 08 00 00 43 48 4E 00 50 65 6F 70 ........CHN.Peop 6C 65 27 73 20 52 65 70 75 62 6C 69 63 20 6F 66 le's Republic of 20 43 68 69 6E 61 00 China. */ #define CLIENT_AUTH_INFO 0x50ff typedef struct { t_bnet_header h; bn_int protocol; /* 00 00 00 00 always zero */ bn_int archtag; bn_int clienttag; bn_int versionid; /* 09 00 00 00 */ /* FIXME: what is this? */ bn_int gamelang; /* 00 00 00 00 always zero */ bn_int localip; /* 00 00 00 00 always zero */ bn_int bias; /* (gmt-local)/60 (using signed math) */ bn_int lcid; /* Win32 LCID */ bn_int langid; /* Win32 LangID */ /* langstr */ /* countryname */ } PACKED_ATTR() t_client_auth_info; /******************************************************/ /******************************************************/ /* FF 2A 20 00 91 4F 93 DF 57 74 B5 C8 48 0F 4D 9B .* ..O..Wt..H.M. A2 28 A6 03 C1 D9 DA 11 42 69 6D 42 6F 3A 29 00 .(......BimBo:). */ #define CLIENT_CREATEACCTREQ1 0x2aff typedef struct { t_bnet_header h; bn_int password_hash1[5]; /* hash of lowercase password w/o null */ /* player name */ } PACKED_ATTR() t_client_createacctreq1; /******************************************************/ /******************************************************/ /* FF 2A 18 00 01 00 00 00 .*...... 13 00 00 00 78 52 82 02 00 00 00 00 00 00 00 00 ................ ---120 82 130 2--- FF 2A 08 00 01 00 00 00 .*...... */ #define SERVER_CREATEACCTREPLY1 0x2aff typedef struct { t_bnet_header h; bn_int result; } PACKED_ATTR() t_server_createacctreply1; #define SERVER_CREATEACCTREPLY1_RESULT_OK 0x00000001 #define SERVER_CREATEACCTREPLY1_RESULT_NO 0x00000000 /******************************************************/ /******************************************************/ /* FF 2B 20 00 01 00 00 00 .+ ..... 00 00 00 00 4D 00 00 00 0E 01 00 00 20 00 00 00 ....M....... ... CE 01 00 00 DD 07 00 00 ........ FF 2B 20 00 01 00 00 00 00 00 00 00 06 00 00 .+ ............ 00 72 01 00 00 40 00 00 00 A9 07 00 00 FF 07 00 .r...@.......... 00 . from Starcraft 1.05 FF 2B 20 00 01 00 00 00 00 00 00 00 06 00 00 00 .+ ............. 7C 01 00 00 20 00 00 00 00 02 00 00 FF 07 00 00 |... ........... */ #define CLIENT_UNKNOWN_2B 0x2bff /* FIXME: what is this? */ typedef struct { t_bnet_header h; bn_int unknown1; /* 01 00 00 00 */ /* 01 00 00 00 */ bn_int unknown2; /* 00 00 00 00 */ /* 00 00 00 00 */ bn_int unknown3; /* 4D 00 00 00 */ /* 06 00 00 00 */ bn_int unknown4; /* 0E 01 00 00 */ /* 72 01 00 00 */ bn_int unknown5; /* 20 00 00 00 */ /* 40 00 00 00 */ bn_int unknown6; /* CE 01 00 00 */ /* A9 07 00 00 */ bn_int unknown7; /* DD 07 00 00 */ /* FF 07 00 00 */ } PACKED_ATTR() t_client_unknown_2b; #define CLIENT_UNKNOWN_2B_UNKNOWN1 0x00000001 #define CLIENT_UNKNOWN_2B_UNKNOWN2 0x00000000 #define CLIENT_UNKNOWN_2B_UNKNOWN3 0x0000004d #define CLIENT_UNKNOWN_2B_UNKNOWN4 0x0000010e #define CLIENT_UNKNOWN_2B_UNKNOWN5 0x00000020 #define CLIENT_UNKNOWN_2B_UNKNOWN6 0x000001ce #define CLIENT_UNKNOWN_2B_UNKNOWN7 0x000007dd /******************************************************/ /******************************************************/ /* later replaced by progident2 and the authreq packets FF 06 14 00 36 38 58 49 ....68XI 50 58 45 53 BB 00 00 00 00 00 00 00 PXES........ sent by 1.05 Starcraft FF 06 14 00 36 38 58 49 52 41 54 53 BD 00 00 00 ....68XIRATS.... 00 00 00 00 .... Diablo II 1.03 FF 06 14 00 36 38 58 49 56 44 32 44 03 00 ....68XIVD2D.. 00 00 00 00 00 00 ...... */ #define CLIENT_PROGIDENT 0x06ff typedef struct { t_bnet_header h; bn_int archtag; bn_int clienttag; /* see tag.h */ bn_int versionid; /* FIXME: how does the versionid work? */ bn_int unknown1; /* FIXME: always zero? spawn flag? */ } PACKED_ATTR() t_client_progident; /******************************************************/ /******************************************************/ /* These formulas are for authenticating the client version. FF 06 5A 00 00 86 BA E3 ..Z..... 09 28 BC 01 49 58 38 36 76 65 72 32 2E 6D 70 71 .(..IX86ver2.mpq 00 41 3D 32 30 31 39 34 39 38 38 39 39 20 42 3D .A=2019498899 B= 33 34 32 33 32 39 32 33 39 34 20 43 3D 31 37 31 3423292394 C=171 39 30 31 31 32 32 32 20 34 20 41 3D 41 5E 53 20 9011222 4 A=A^S 42 3D 42 2D 43 20 43 3D 43 5E 41 20 41 3D 41 5E B=B-C C=C^A A=A^ 42 00 B. FF 06 59 00 00 C1 12 EC 09 28 BC 01 49 58 38 36 ..Y......(..IX86 76 65 72 35 2E 6D 70 71 00 41 3D 31 38 37 35 35 ver5.mpq.A=18755 39 31 33 34 31 20 42 3D 32 34 39 31 30 39 39 38 91341 B=24910998 30 39 20 43 3D 36 33 34 38 35 36 36 30 34 20 34 09 C=634856604 4 20 41 3D 41 2D 53 20 42 3D 42 5E 43 20 43 3D 43 A=A-S B=B^C C=C 2B 41 20 41 3D 41 5E 42 00 +A A=A^B. FF 06 5A 00 00 C1 12 EC 09 28 BC 01 49 58 38 36 ..Z......(..IX86 76 65 72 35 2E 6D 70 71 00 41 3D 31 37 31 32 39 ver5.mpq.A=17129 34 38 34 32 36 20 42 3D 33 36 30 30 30 33 30 36 48426 B=36000306 30 37 20 43 3D 33 33 39 30 34 31 37 39 35 39 20 07 C=3390417959 34 20 41 3D 41 2D 53 20 42 3D 42 5E 43 20 43 3D 4 A=A-S B=B^C C= 43 2D 41 20 41 3D 41 2D 42 00 C-A A=A-B. FF 06 5A 00 00 3A 7F E8 09 28 BC 01 49 58 38 36 ..Z..:...(..IX86 76 65 72 34 2E 6D 70 71 00 41 3D 31 31 38 36 39 ver4.mpq.A=11869 35 38 31 34 31 20 42 3D 31 33 37 37 34 34 31 34 58141 B=13774414 35 37 20 43 3D 31 37 37 32 37 38 37 37 30 35 20 57 C=1772787705 34 20 41 3D 41 5E 53 20 42 3D 42 5E 43 20 43 3D 4 A=A^S B=B^C C= 43 2B 41 20 41 3D 41 5E 42 00 C+A A=A^B. FF 06 5A 00 00 56 CD F6 09 28 BC 01 49 58 38 36 ..Z..V...(..IX86 76 65 72 37 2E 6D 70 71 00 41 3D 31 30 32 36 30 ver7.mpq.A=10260 34 34 33 35 34 20 42 3D 34 31 33 32 36 33 30 37 44354 B=41326307 31 31 20 43 3D 32 33 30 32 34 31 31 33 32 38 20 11 C=2302411328 34 20 41 3D 41 5E 53 20 42 3D 42 5E 43 20 43 3D 4 A=A^S B=B^C C= 43 5E 41 20 41 3D 41 2B 42 00 C^A A=A+B. */ #define SERVER_AUTHREQ1 0x06ff typedef struct { t_bnet_header h; bn_long timestamp; /* FIXME: file modification time? */ /* versioncheck filename */ /* equation */ } PACKED_ATTR() t_server_authreq1; /******************************************************/ /******************************************************/ /* First seen in Diablo II (and LoD) 1.09 FF 50 65 00 00 00 00 00 36 1A 6C 45 76 BC 00 00 .Pe.....6.lEv... 00 48 A6 EF 09 28 BC 01 49 58 38 36 76 65 72 36 .H...(..IX86ver6 2E 6D 70 71 00 41 3D 33 38 34 35 35 38 31 36 33 .mpq.A=384558163 34 20 42 3D 38 38 30 38 32 33 35 38 30 20 43 3D 4 B=880823580 C= 31 33 36 33 39 33 37 31 30 33 20 34 20 41 3D 41 1363937103 4 A=A 2D 53 20 42 3D 42 2D 43 20 43 3D 43 2D 41 20 41 -S B=B-C C=C-A A 3D 41 2D 42 00 =A-B. FF 50 65 00 00 00 00 00 .Pe..... 30 4B C1 33 10 EB 09 00 00 A5 C4 DD 09 28 BC 01 0K.3.........(.. 49 58 38 36 76 65 72 30 2E 6D 70 71 00 41 3D 31 IX86ver0.mpq.A=1 34 33 32 36 36 32 34 37 38 20 42 3D 36 35 32 32 432662478 B=6522 37 38 36 32 35 20 43 3D 31 37 36 31 35 31 35 38 78625 C=17615158 36 39 20 34 20 41 3D 41 5E 53 20 42 3D 42 2B 43 69 4 A=A^S B=B+C 20 43 3D 43 2B 41 20 41 3D 41 5E 42 00 C=C+A A=A^B. */ #define SERVER_AUTHREQ_109 0x50ff typedef struct { t_bnet_header h; bn_int logontype; /* 00 00 00 00 always zero */ bn_int sessionkey; bn_int sessionnum; bn_long timestamp; /* versioncheck filename */ /* equation */ } PACKED_ATTR() t_server_authreq_109; #define SERVER_AUTHREQ_109_LOGONTYPE 0x0000000 #define SERVER_AUTHREQ_109_LOGONTYPE_W3 0x00000002 #define SERVER_AUTHREQ_109_LOGONTYPE_W3XP 0x00000002 /******************************************************/ /* ADDED BY UNDYING SOULZZ 4/3/02 */ #define VERSIONTAG_WARCRAFT3_113 "WAR3_113" /******************************************************/ /* FF 07 40 00 36 38 58 49 52 41 54 53 BD 00 00 00 ..@.68XIRATS.... 00 05 00 01 1E 88 D7 08 73 74 61 72 63 72 61 66 ........starcraf 74 2E 65 78 65 20 30 33 2F 30 38 2F 39 39 20 32 t.exe 03/08/99 2 32 3A 34 31 3A 35 30 20 31 30 34 32 34 33 32 00 2:41:50 1042432. sent by the 1.05 Starcraft FF 07 40 00 36 38 58 49 52 41 54 53 BD 00 00 00 ..@.68XIRATS.... 00 05 00 01 AE AC DE 87 73 74 61 72 63 72 61 66 ........starcraf 74 2E 65 78 65 20 30 33 2F 30 38 2F 39 39 20 32 t.exe 03/08/99 2 32 3A 34 31 3A 35 30 20 31 30 34 32 34 33 32 00 2:41:50 1042432. sent by the 1.08alpha Brood War (Starcraft game) in response to A=2521522835 B=3428392135 C=218673704 4 A=A^S B=B-C C=C+A A=A-B with IX86ver1.mpq FF 07 40 00 36 38 58 49 52 41 54 53 C3 00 00 00 ..@.68XIRATS.... 01 08 00 01 A1 52 CE FE 73 74 61 72 63 72 61 66 .....R..starcraf 74 2E 65 78 65 20 31 32 2F 32 38 2F 30 30 20 31 t.exe 12/28/00 1 31 3A 32 38 3A 35 32 20 31 30 38 32 33 36 38 00 1:28:52 1082368. sent by the 1.07 Diablo FF 07 3C 00 36 38 58 49 4C 54 52 44 26 00 00 00 ..<.68XILTRD&... 01 06 05 62 8C 56 E6 21 64 69 61 62 6C 6F 2E 65 ...b.V.!diablo.e 78 65 20 30 39 2F 31 37 2F 39 38 20 31 38 3A 30 xe 09/17/98 18:0 30 3A 34 30 20 37 36 30 33 32 30 00 0:40 760320. FF 07 45 00 36 38 58 49 4E 42 32 57 4B 00 00 00 ..E.68XINB2WK... 99 00 00 02 3D 51 C4 AA 57 61 72 63 72 61 66 74 ....=Q..Warcraft 20 49 49 20 42 4E 45 2E 65 78 65 20 31 30 2F 31 II BNE.exe 10/1 35 2F 39 39 20 30 30 3A 33 37 3A 35 34 20 37 30 5/99 00:37:54 70 34 35 31 32 00 4512. sent by the 1.03 Diablo II FF 07 3A 00 36 38 58 49 ..:.68XI 56 44 32 44 03 00 00 00 00 03 00 01 47 3E 26 73 VD2D........G>&s 47 61 6D 65 2E 65 78 65 20 30 38 2F 30 35 2F 30 Game.exe 08/05/0 30 20 30 31 3A 34 32 3A 32 38 20 32 39 34 39 31 0 01:42:28 29491 32 00 2. */ #define CLIENT_AUTHREQ1 0x07ff typedef struct { t_bnet_header h; bn_int archtag; bn_int clienttag; bn_int versionid; bn_int gameversion; bn_int checksum; /* executable info */ } PACKED_ATTR() t_client_authreq1; /******************************************************/ /******************************************************/ /* FF 07 0A 00 02 00 00 00 ........ 00 00 .. FF 07 0A 00 02 00 00 00 00 00 .......... */ #define SERVER_AUTHREPLY1 0x07ff typedef struct { t_bnet_header h; bn_int message; /* filename */ /* unknown */ } PACKED_ATTR() t_server_authreply1; #define SERVER_AUTHREPLY1_MESSAGE_BADVERSION 0x00000000 #define SERVER_AUTHREPLY1_MESSAGE_UPDATE 0x00000001 /* initiate auto-update */ #define SERVER_AUTHREPLY1_MESSAGE_OK 0x00000002 /******************************************************/ /******************************************************/ /* First seen in Diablo II (and LoD) 1.09 FF 51 09 00 00 00 00 00 00 .Q....... */ #define SERVER_AUTHREPLY_109 0x51ff typedef struct { t_bnet_header h; bn_int message; /* message string? */ } PACKED_ATTR() t_server_authreply_109; #define SERVER_AUTHREPLY_109_MESSAGE_OK 0x00000000 #define SERVER_AUTHREPLY_109_MESSAGE_UPDATE 0x00000100 #define SERVER_AUTHREPLY_109_MESSAGE_BADVERSION 0x00000101 /* we should check the first 10 values or so to see what they mean */ /******************************************************/ /******************************************************/ /* First seen in Diablo II (and LoD) 1.09 FF 51 67 00 C9 88 DA 42 00 09 00 01 46 97 62 9A .Qg....B....F.b. 01 00 00 00 00 00 00 00 10 00 00 00 06 00 00 00 ................ A5 E7 39 00 00 00 00 00 ED CD 4F F7 6A 7A 4F 96 ..9.......O.jzO. 85 7A 2D A2 7F 1F B1 D6 81 B3 8D 50 47 61 6D 65 .z-........PGame 2E 65 78 65 20 30 38 2F 31 36 2F 30 31 20 32 33 .exe 08/16/01 23 3A 30 34 3A 34 30 20 34 32 34 30 36 37 00 74 73 :04:40 424067.ts 69 6E 67 68 75 61 00 inghua. */ #define CLIENT_AUTHREQ_109 0x51ff typedef struct { t_bnet_header h; bn_int ticks; bn_int gameversion; bn_int checksum; bn_int cdkey_number; /* count of cdkeys, d2 = 1, lod = 2 */ bn_int spawn; /* set if using spawn copy */ /* cdkey info(s) */ /* executable info */ /* cdkey owner */ } PACKED_ATTR() t_client_authreq_109; /* values are the same as in CLIENT_AUTHREQ1 */ typedef struct { bn_int len; bn_int type; bn_int checksum; bn_int u1; bn_int hash[5]; } PACKED_ATTR() t_cdkey_info; /******************************************************/ /******************************************************/ /* Batle.net used to send requests for registry and email info. Thanks to bnetanon, I have a dump of these old packets. */ /* FF 18 41 00 00 00 00 00 01 00 00 80 53 6F 66 74 ..A.........Soft 77 61 72 65 5C 4D 69 63 72 6F 73 6F 66 74 5C 4D ware\Microsoft\M 53 20 53 65 74 75 70 20 28 41 43 4D 45 29 5C 55 S Setup (ACME)\U 73 65 72 20 49 6E 66 6F 00 44 65 66 4E 61 6D 65 ser Info.DefName 00 . FF 18 48 00 00 00 00 00 01 00 00 80 53 6F 66 74 ..H.........Soft 77 61 72 65 5C 4D 69 63 72 6F 73 6F 66 74 5C 4D ware\Microsoft\M 65 64 69 61 50 6C 61 79 65 72 5C 43 6F 6E 74 72 ediaPlayer\Contr 6F 6C 5C 50 6C 61 79 42 61 72 00 43 6C 72 42 61 ol\PlayBar.ClrBa 63 6B 43 6F 6C 6F 72 00 ckColor. */ #define SERVER_REGSNOOPREQ 0x18ff typedef struct { t_bnet_header h; bn_int unknown1; /* 00 00 00 00 */ /* sequence match like in other packets? */ bn_int hkey; /* registry key */ /* value name */ } PACKED_ATTR() t_server_regsnoopreq; #define SERVER_REGSNOOPREQ_UNKNOWN1 0x00000000 #define SERVER_REGSNOOPREQ_HKEY_CLASSES_ROOT 0x80000000 #define SERVER_REGSNOOPREQ_HKEY_CURRENT_USER 0x80000001 #define SERVER_REGSNOOPREQ_HKEY_LOCAL_MACHINE 0x80000002 #define SERVER_REGSNOOPREQ_HKEY_USERS 0x80000003 #define SERVER_REGSNOOPREQ_HKEY_PERFORMANCE_DATA 0x80000004 #define SERVER_REGSNOOPREQ_HKEY_CURRENT_CONFIG 0x80000005 #define SERVER_REGSNOOPREQ_HKEY_DYN_DATA 0x80000006 #define SERVER_REGSNOOPREQ_HKEY_PERFORMANCE_TEXT 0x80000050 #define SERVER_REGSNOOPREQ_HKEY_PERFORMANCE_NLSTEXT 0x80000060 #define SERVER_REGSNOOPREQ_REGKEY "Software\\Microsoft\\MS Setup (ACME)\\User Info" #define SERVER_REGSNOOPREQ_REGVALNAME "DefName" /******************************************************/ /******************************************************/ /* If the key exists, the client send this back */ /* FF 18 0C 00 00 00 00 00 42 6F 62 00 ........Bob. FF 18 0C 00 00 00 00 00 A0 9C A0 00 ............ */ #define CLIENT_REGSNOOPREPLY 0x18ff typedef struct { t_bnet_header h; bn_int unknown1; /* 00 00 00 00 */ /* same as request? */ /* registry value (string, dword, or binary */ } PACKED_ATTR() t_client_regsnoopreply; /******************************************************/ /******************************************************/ /* FF 07 0A 00 02 00 00 00 00 00 .......... */ #define CLIENT_ICONREQ 0x2dff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_iconreq; /******************************************************/ /******************************************************/ /* FF 2D 16 00 76 34 1F 8F .-..v4.. C0 D6 BD 01 69 63 6F 6E 73 2E 62 6E 69 00 ....icons.bni. FF 2D 16 00 00 77 D0 01 C7 B1 BE 01 69 63 6F 6E .-...w......icon 73 2E 62 6E 69 00 s.bni. */ #define SERVER_ICONREPLY 0x2dff typedef struct { t_bnet_header h; bn_long timestamp; /* file modification time? */ /* filename */ } PACKED_ATTR() t_server_iconreply; /******************************************************/ /******************************************************/ #define CLIENT_LADDERSEARCHREQ 0x2fff typedef struct { t_bnet_header h; bn_int clienttag; bn_int id; /* (AKA ladder type) 1==standard, 3==ironman */ bn_int type; /* (AKA ladder sort) */ /* player name */ } PACKED_ATTR() t_client_laddersearchreq; #define CLIENT_LADDERSEARCHREQ_ID_STANDARD 0x00000001 #define CLIENT_LADDERSEARCHREQ_ID_IRONMAN 0x00000003 #define CLIENT_LADDERSEARCHREQ_TYPE_HIGHESTRATED 0x00000000 #define CLIENT_LADDERSEARCHREQ_TYPE_MOSTWINS 0x00000002 #define CLIENT_LADDERSEARCHREQ_TYPE_MOSTGAMES 0x00000003 /******************************************************/ /******************************************************/ #define SERVER_LADDERSEARCHREPLY 0x2fff typedef struct /* FIXME: how does client know how many names? do we send separate replies for each name in the request? */ { t_bnet_header h; bn_int rank; /* 0 means 1st, etc */ } PACKED_ATTR() t_server_laddersearchreply; #define SERVER_LADDERSEARCHREPLY_RANK_NONE 0xffffffff /******************************************************/ /******************************************************/ /* FF 30 1C 00 00 00 00 00 .0...... 32 37 34 34 37 37 32 39 31 34 38 32 38 00 63 6C 2744772914828.cl 6F 75 64 00 oud. */ #define CLIENT_CDKEY 0x30ff typedef struct { t_bnet_header h; bn_int spawn; /* FIXME: not sure if this is correct, but cdkey2 does it this way */ /* cd key */ /* owner name */ /* Was this always here? */ } PACKED_ATTR() t_client_cdkey; #define CLIENT_CDKEY_UNKNOWN1 0x00000000 /******************************************************/ /******************************************************/ /* FF 30 0E 00 01 00 00 00 .0...... 63 6C 6F 75 64 00 cloud. */ #define SERVER_CDKEYREPLY 0x30ff typedef struct { t_bnet_header h; bn_int message; /* owner name */ } PACKED_ATTR() t_server_cdkeyreply; #define SERVER_CDKEYREPLY_MESSAGE_OK 0x00000001 #define SERVER_CDKEYREPLY_MESSAGE_BAD 0x00000002 #define SERVER_CDKEYREPLY_MESSAGE_WRONGAPP 0x00000003 #define SERVER_CDKEYREPLY_MESSAGE_ERROR 0x00000004 /* disabled */ #define SERVER_CDKEYREPLY_MESSAGE_INUSE 0x00000005 /* (any other value seems to correspond to ok) */ /******************************************************/ /******************************************************/ /* FF 36 34 00 00 00 00 00 0D 00 00 00 01 00 00 00 .64............. B5 AE 23 00 50 E5 D5 C0 DB 55 1E 38 0A F5 58 B9 ..#.P....U.8..X. 47 64 C6 C2 9F BB FF B8 81 E7 EB EC 1B 13 C6 38 Gd.............8 52 6F 62 00 Rob. FF 36 34 00 00 00 00 00 0D 00 00 00 01 00 00 00 .64............. 7F D7 00 00 90 64 77 2F D7 5B 42 38 1F A1 A2 6F .....dw/.[B8...o E8 FA BE F8 B6 0B BA 0F CA 64 3A 17 14 56 83 AB .........d:..V.. 42 6F 62 00 Bob. FF 36 35 00 00 00 00 00 10 00 00 00 04 00 00 00 .65............. 0D 43 03 00 7A 11 07 ED 7C 9E 1E 38 E5 87 8B 3B .C..z...|..8...; 9C 19 91 D9 0D 10 FC C1 C0 86 8C 8D DA A4 45 0B ..............E. XX XX XX XX 00 XXXX. FF 36 34 00 00 00 00 00 10 00 00 00 04 00 00 00 .64............. 70 F9 02 00 58 F9 B6 E6 38 49 5C 38 38 9C 31 E4 p...X...8I\88.1. 1D 3D 40 05 66 AD 4C C8 1D 12 8E 49 9E 60 1A CB .=@.f.L....I.`.. 42 6F 62 00 Bob. */ #define CLIENT_CDKEY2 0x36ff typedef struct { t_bnet_header h; bn_int spawn; bn_int keylen; /* without terminating NUL */ bn_int productid; bn_int keyvalue1; bn_int sessionkey; bn_int ticks; bn_int key_hash[5]; /* owner name */ } PACKED_ATTR() t_client_cdkey2; #define CLIENT_CDKEY2_SPAWN_TRUE 0x00000001 #define CLIENT_CDKEY2_SPAWN_FALSE 0x00000000 /******************************************************/ /******************************************************/ /* From Diablo II 1.08? FF 42 43 00 AB 4C A4 3B .BC..L.; 01 00 00 00 00 00 00 00 10 00 00 00 06 00 00 00 ................ XX 60 12 00 00 00 00 00 5D 82 82 C4 F4 8F D0 91 X`......]....... E1 5B AB 95 D9 EE EF 18 44 3E F1 C9 XX XX XX XX .[......D>..XXXX XX XX XX XX XX XX XX XX XX XX 00 XXXXXXXXXX. FF 42 44 00 17 78 42 77 01 00 00 00 00 00 00 00 .BD..xBw........ 10 00 00 00 06 00 00 00 XX F3 10 00 00 00 00 00 ........X....... A8 29 8B C4 41 BD 33 AB 74 4C 1F 1E 5C XX CA 83 .)..A.3.tL..\X.. 7F E5 36 14 XX XX XX XX XX XX XX XX XX XX XX XX ..6.XXXXXXXXXXXX XX XX XX 00 XXX. FF 42 44 00 C6 25 A1 3B 01 00 00 00 00 00 00 00 .BD..%.;........ 10 00 00 00 06 00 00 00 XX F3 10 00 00 00 00 00 ........X....... C4 3F FB 05 94 0C AC D4 3B 63 B1 90 E4 XX 53 B9 .?......;c...XS. 70 C3 6F 2E XX XX XX XX XX XX XX XX XX XX XX XX p.o.XXXXXXXXXXXX XX XX XX 00 XXX. */ #define CLIENT_CDKEY3 0x42ff typedef struct { t_bnet_header h; bn_int unknown1; /* FIXME: some kind of salt? */ bn_int unknown2; /* 01 00 00 00 */ bn_int unknown3; /* 00 00 00 00 */ bn_int unknown4; /* 10 00 00 00 */ bn_int unknown5; /* 06 00 00 00 */ bn_int unknown6; /* FIXME: value1? */ bn_int unknown7; /* 00 00 00 00 */ bn_int key_hash[5]; /* owner name */ } PACKED_ATTR() t_client_cdkey3; #define CLIENT_CDKEY3_UNKNOWN1 0xffffffff #define CLIENT_CDKEY3_UNKNOWN2 0x00000001 #define CLIENT_CDKEY3_UNKNOWN3 0x00000000 #define CLIENT_CDKEY3_UNKNOWN4 0x00000010 #define CLIENT_CDKEY3_UNKNOWN5 0x00000006 #define CLIENT_CDKEY3_UNKNOWN6 0x00123456 #define CLIENT_CDKEY3_UNKNOWN7 0x00000000 /******************************************************/ /******************************************************/ /* FF 42 09 00 00 00 00 00 .B...... 00 FF 42 09 00 00 00 00 00 00 .B....... */ #define SERVER_CDKEYREPLY3 0x42ff typedef struct { t_bnet_header h; bn_int message; /* owner name */ /* FIXME: or error message, or ... */ } PACKED_ATTR() t_server_cdkeyreply3; #define SERVER_CDKEYREPLY3_MESSAGE_OK 0x00000000 /******************************************************/ /******************************************************/ /* FF 34 0D 00 00 00 00 00 00 00 00 00 00 .4........... */ #define CLIENT_REALMLISTREQ 0x34ff typedef struct { t_bnet_header h; bn_int unknown1; bn_int unknown2; } PACKED_ATTR() t_client_realmlistreq; /******************************************************/ /******************************************************/ /* 0000: FF 40 04 00 .@.. */ #define CLIENT_REALMLISTREQ_110 0x40ff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_realmlistreq_110; /******************************************************/ /******************************************************/ /* FF 34 5E 00 00 00 00 00 01 00 00 00 00 00 00 C0 .4^............. 00 00 00 00 00 00 00 00 00 00 00 00 10 82 01 00 ................ FF FF FF FF 00 00 00 00 42 65 74 61 57 65 73 74 ........BetaWest 00 50 6C 65 61 73 65 20 73 65 6C 65 63 74 20 74 .Please select t 68 69 73 20 61 73 20 79 6F 75 72 20 72 65 61 6C his as your real 6D 20 64 75 72 69 6E 67 20 62 65 74 61 00 m during beta. ff 34 5e 00 00 00 00 00 01 00 00 00 00 00 00 c0 .4^............. 00 00 00 00 00 00 00 00 00 00 00 00 bc 95 01 00 ................ ff ff ff ff 00 00 00 00 42 65 74 61 57 65 73 74 ........BetaWest 00 50 6c 65 61 73 65 20 73 65 6c 65 63 74 20 74 .Please select t 68 69 73 20 61 73 20 79 6f 75 72 20 72 65 61 6c his as your real 6d 20 64 75 72 69 6e 67 20 62 65 74 61 00 m during beta. ff 34 5e 00 00 00 00 00 01 00 00 00 00 00 00 c0 .4^............. 00 00 00 00 00 00 00 00 00 00 00 00 c8 99 01 00 ................ ff ff ff ff 00 00 00 00 42 65 74 61 57 65 73 74 ........BetaWest 00 50 6c 65 61 73 65 20 73 65 6c 65 63 74 20 74 .Please select t 68 69 73 20 61 73 20 79 6f 75 72 20 72 65 61 6c his as your real 6d 20 64 75 72 69 6e 67 20 62 65 74 61 00 m during beta. from bnetd-0.3.23pre18 to Diablo II 1.03 FF 34 4B 00 00 00 00 00 01 00 00 00 00 00 .4K........... 00 C0 00 00 00 00 00 00 00 00 00 00 00 00 10 82 ................ 01 00 FF FF FF FF 00 00 00 00 51 61 72 61 74 68 ..........Qarath 52 65 61 6C 6D 00 54 48 45 20 43 68 6F 69 63 65 Realm.THE Choice 20 46 6F 72 20 4E 6F 77 28 74 6D 29 00 For Now(tm). */ #define SERVER_REALMLISTREPLY 0x34ff /* realm list reply? */ typedef struct { t_bnet_header h; bn_int unknown1; bn_int count; /* realm entries */ } PACKED_ATTR() t_server_realmlistreply; #define SERVER_REALMLISTREPLY_UNKNOWN1 0x00000000 typedef struct { bn_int unknown3; bn_int unknown4; bn_int unknown5; bn_int unknown6; bn_int unknown7; /* this one is always different... 00 01 XX XX.. what is it? */ bn_int unknown8; bn_int unknown9; /* realm name */ /* realm description */ } PACKED_ATTR() t_server_realmlistreply_data; #define SERVER_REALMLISTREPLY_DATA_UNKNOWN3 0xc0000000 #define SERVER_REALMLISTREPLY_DATA_UNKNOWN4 0x00000000 #define SERVER_REALMLISTREPLY_DATA_UNKNOWN5 0x00000000 #define SERVER_REALMLISTREPLY_DATA_UNKNOWN6 0x00000000 #define SERVER_REALMLISTREPLY_DATA_UNKNOWN7 0x00018210 /* 98832 or 1;33296 */ #define SERVER_REALMLISTREPLY_DATA_UNKNOWN8 0xffffffff #define SERVER_REALMLISTREPLY_DATA_UNKNOWN9 0x00000000 /******************************************************/ /******************************************************/ /* # 44 packet from server: type=0x40ff(unknown) length=40 class=bnet 0000: FF 40 28 00 00 00 00 00 01 00 00 00 01 00 00 00 .@(............. 0010: 45 75 72 6F 70 65 00 52 65 61 6C 6D 20 66 6F 72 Europe.Realm for 0020: 20 45 75 72 6F 70 65 00 Europe. */ #define SERVER_REALMLISTREPLY_110 0x40ff typedef struct { t_bnet_header h; bn_int unknown1; bn_int count; /* realm entries */ } PACKED_ATTR() t_server_realmlistreply_110; #define SERVER_REALMLISTREPLY_110_UNKNOWN1 0x00000000 typedef struct { bn_int unknown1; /* realm name */ /* realm description */ } PACKED_ATTR() t_server_realmlistreply_110_data; #define SERVER_REALMLISTREPLY_110_DATA_UNKNOWN1 0x00000001 #define CLIENT_PROFILEREQ 0x35ff typedef struct /* join realm request */ { t_bnet_header h; bn_int count; /* player name */ } PACKED_ATTR() t_client_profilereq; #define SERVER_PROFILEREPLY 0x35ff typedef struct /* realm join reply? */ { t_bnet_header h; bn_int count; /* same as in req */ bn_byte fail; /* != 0 if a problem occured */ /* profile-description */ /* profile-location */ /* bn_int clanTAG */ } PACKED_ATTR() t_server_profilereply; #define CLIENT_UNKNOWN_37 0x37ff typedef struct /* character list request, character list upload? */ { t_bnet_header h; bn_int opencount; /* Number of OPEN characters on user's machine! */ /* Always zero for "closed" connections. */ /* unknown2 */ /* subsequent blocks of t_d2char_info or something */ /* similar, so server could read this list and */ /* include in the 0x37ff reply as a choice (this */ /* makes sense cuz the server does NOT store open */ /* character details - this also explains why */ /* unknown1 is always 0 in the beta, and the 0x00 */ /* of unknown2 acts as a EOF when client read the */ /* t_d2char_info structures */ } PACKED_ATTR() t_client_unknown_37; /******************************************************/ /******************************************************/ /* FF 37 01 01 00 00 00 00 08 00 00 00 04 00 00 00 .7.............. 42 65 74 61 57 65 73 74 2C 4D 6F 4E 6B 00 87 80 BetaWest,MoNk... 06 01 01 01 01 50 FF FF 02 02 FF FF FF FF FF FF .....P.......... 02 49 50 50 50 50 FF FF FF 50 50 FF FF FF FF FF .IPPPP...PP..... FF 14 88 82 80 80 FF FF FF 00 42 65 74 61 57 65 ..........BetaWe 73 74 2C 4D 6F 4E 6B 2D 65 00 83 80 05 02 02 01 st,MoNk-e....... 01 2B FF 1B 02 02 FF FF FF FF FF FF 03 FF FF FF .+.............. FF FF FF FF A8 FF FF FF FF FF FF FF FF 10 80 82 ................ 80 80 FF FF FF 00 42 65 74 61 57 65 73 74 2C 4D ......BetaWest,M 6F 4E 6B 2D 65 65 00 83 80 06 01 01 01 01 FF 4C oNk-ee.........L FF 02 02 FF FF FF FF FF FF 01 FF 48 48 48 48 FF ...........HHHH. A6 FF 48 48 FF FF FF FF FF FF 0F 80 80 80 80 FF ..HH............ FF FF 00 42 65 74 61 57 65 73 74 2C 4D 6F 4E 6B ...BetaWest,MoNk 2D 74 77 6F 00 87 80 01 01 01 01 01 FF FF FF 01 -two............ 01 FF FF FF FF FF FF 02 FF FF FF FF FF FF FF FF ................ FF FF FF FF FF FF FF FF 01 84 80 FF FF FF 80 80 ................ 00 . ^-- 1: (BetaWest) MoNk 2: (BetaWest) MoNk-e 3: (BetaWest) MoNk-ee 4: (BetaWest) MoNk-two ff 37 4e 00 00 00 00 00 08 00 00 00 01 00 00 00 .7N............. 42 65 74 61 57 65 73 74 2c 4c 69 66 65 6c 69 6b BetaWest,Lifelik 65 00 87 80 01 01 01 01 01 ff ff ff 01 01 ff ff e............... ff ff ff ff 03 ff ff ff ff ff ff ff ff ff ff ff ................ ff ff ff ff ff 01 80 80 ff ff ff 80 80 00 .............. ff 37 4e 00 00 00 00 00 08 00 00 00 01 00 00 00 .7N............. 42 65 74 61 57 65 73 74 2c 51 6c 65 78 54 45 53 BetaWest,QlexTES 54 00 83 80 ff ff ff ff ff 30 ff 1b ff ff ff ff T........0...... ff ff ff ff 04 ff ff ff ff ff ff ff ff ff ff ff ................ ff ff ff ff ff 01 80 80 80 80 ff ff ff 00 .............. from bnetd-0.3.23pre18 to Diablo II 1.03 "Char1 {BNE}" [lvl 20, amaz] "Char2 {BNE}" [lvl 21, sorc] "Char3 {BNE}" [lvl 22, necro] FF 37 D9 00 00 00 00 00 08 00 00 00 03 00 Gv.7............ 00 00 51 61 72 61 74 68 52 65 61 6C 6D 2C 43 68 ..QarathRealm,Ch 61 72 31 00 87 80 01 01 01 01 01 01 01 01 01 01 ar1............. 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ................ 01 01 01 01 01 01 01 14 85 86 01 FF FF FF FF 42 ...............B 4E 45 54 44 00 51 61 72 61 74 68 52 65 61 6C 6d NETD.QarathRealm 2C 43 68 61 72 32 00 87 80 01 01 01 01 01 01 01 ,Char2.......... 01 01 01 01 01 01 01 01 01 02 01 01 01 01 01 01 ................ 01 01 01 01 01 01 01 01 01 01 15 85 86 01 FF FF ................ FF FF 42 4E 45 54 44 00 51 61 72 61 74 68 52 65 ..BNETD.QarathRe 61 6C 6D 2C 43 68 61 72 33 00 87 80 01 01 01 01 alm,Char3....... 01 01 01 01 01 01 01 01 01 01 01 01 03 01 01 01 ................ 01 01 01 01 01 01 01 01 01 01 01 01 01 16 85 86 ................ 01 FF FF FF FF 42 4E 45 54 44 00 .....BNETD. */ #define SERVER_UNKNOWN_37 0x37ff typedef struct /* character list reply? */ { t_bnet_header h; bn_int unknown1; bn_int unknown2; /* _bucky_: max chars allowed? */ bn_int count; /* # of chars, same number of */ /* t_char_info to follow in */ /* packet */ /* d2char_info blocks */ } PACKED_ATTR() t_server_unknown_37; #define SERVER_UNKNOWN_37_UNKNOWN1 0x00000000 #define SERVER_UNKNOWN_37_UNKNOWN2 0x00000008 /* The ONLY 0x00 that should appear should be the terminating NUL for */ /* the character name string and the guild tag string, they're used as */ /* delimiters to separate character name and the character structure */ /* If you got any other NUL's in here the next character's info will */ /* be royally fucked up - using 0x01 or 0xff for unknowns seem to work */ /* well */ typedef struct { /* "RealmName,CharacterName" - for closed characters */ /* - OR - */ /* "CharacterName" - for open characters */ /* - strlen(CharacterName) must be <= 15 - */ bn_byte unknownb1; /* 0x83, 0x87? */ bn_byte unknownb2; /* 0x80...? */ bn_byte helmgfx; bn_byte bodygfx; bn_byte leggfx; bn_byte lhandweapon; bn_byte lhandgfx; bn_byte rhandweapon; /* Partial weapon code list: 0x2f: 1H Axe 0x30: 1H Sword 0x50: 2H Staff 0x51: Another 2H Staff 0x52: Another 2H Staff 0x53: Another 2H Staff 0x54: 2H Axe 0x55: Scythe 0x56: empty? 0x57: Another 2H Axe 0x58: Halberd? 0x59: empty? 0x5a: Another 2H Axe 0x5b: Another Halberd 0x5c: empty? 0x5d: 1H club? 0x5e: empty? 0x5f: empty? */ bn_byte rhandgfx; bn_byte unknownb3; bn_byte unknownb4; bn_byte unknownb5; bn_byte unknownb6; bn_byte unknownb7; bn_byte unknownb8; bn_byte unknownb9; bn_byte unknownb10; bn_byte unknownb11; bn_byte chclass; /* 0x01=Amazon, 0x02=Sor, 0x03=Nec, 0x04=Pal, 0x05=Bar */ bn_int unknown1; bn_int unknown2; bn_int unknown3; bn_int unknown4; bn_byte level; /* yes, byte, not short/int/long */ bn_byte status; /* 0x01-03 = Norm & alive */ /* 0x04-07 = HC & alive */ /* 0x08-0b = Norm & "dead"? */ /* 0x0c+ = HC & dead, chat only */ /* Add 0x80 to get same effect */ bn_byte title; /* 0x01=none 0x02=Sir/Dame? 0x03=Sir/Dame? 0x04=Lord? 0x05=Lord? 0x06=Baron? 0x07=Baron? */ /* Same codes for HC chars */ /* Add 0x80 to get same effect */ bn_byte unknownb13; bn_byte emblembgc; /* Guild emblem background colour */ bn_byte emblemfgc; /* Guild emblem foreground colour */ bn_byte emblemnum; /* Guild emblem type number */ /* emblem number corresponds to D2DATA.MPQ/data/global/ui/Emblems/iconXXa.dc6 */ /* where XX = emblem number - 1 (ie, 0x0A corresponds to icon09a.dc6) use */ /* for dummy values seem safe... 0x01 won't work, you'll get an emblem... */ bn_byte unknownb14; /* Guild Tag */ /* must not be longer than 3 chars */ } PACKED_ATTR() t_d2char_info; #define D2CHAR_INFO_UNKNOWNB1 0x83 #define D2CHAR_INFO_UNKNOWNB2 0x80 #define D2CHAR_INFO_FILLER 0xff /* non-zero padding */ #define D2CHAR_INFO_CLASS_AMAZON 0x01 #define D2CHAR_INFO_CLASS_SORCERESS 0x02 #define D2CHAR_INFO_CLASS_NECROMANCER 0x03 #define D2CHAR_INFO_CLASS_PALADIN 0x04 #define D2CHAR_INFO_CLASS_BARBARIAN 0x05 #define D2CHAR_INFO_CLASS_DRUID 0x06 #define D2CHAR_INFO_CLASS_ASSASSIN 0x07 /******************************************************/ /******************************************************/ /* D2 packet... not sent very often and the client doesn't * seem to expect an answer */ /* FIXME: what the hell does this one do? */ /* FF 39 13 00 42 65 74 61 57 65 73 74 2C 62 75 73 .9..BetaWest,bus 74 61 00 ta. this one was sent after a closed character was deleted on the auth server... maybe a notifier for the gateway server? FF 39 17 00 42 6F 62 73 57 6F 72 6C 64 2C 63 68 .9..BobsWorld,ch 61 72 6E 61 6D 65 00 arname. */ #define CLIENT_UNKNOWN_39 0x39ff typedef struct { t_bnet_header h; /* character name */ /* what about open chars? */ } PACKED_ATTR() t_client_unknown_39; /******************************************************/ /******************************************************/ /* FF 3A 2E 00 58 4C F2 00 19 C2 08 00 D7 33 37 D3 .:..XL.......37. 42 8C 92 37 C2 26 08 A9 3E 92 05 28 A1 5A 18 B9 B..7.&..>..(.Z.. 6D 61 73 74 6F 64 6F 6E 74 66 69 6C 6D 00 mastodontfilm. FF 3A 28 00 2B 73 1C 01 88 91 F2 0D AF 22 43 25 .:(.+s......."C% BF E4 2D 45 42 37 04 DB AF 95 66 71 16 85 67 60 ..-EB7....fq..g` 51 6C 65 78 53 5A 47 00 QlexSZG. */ #define CLIENT_LOGINREQ2 0x3aff typedef struct { t_bnet_header h; bn_int ticks; /* is it really? */ bn_int sessionkey; bn_int password_hash2[5]; /* player name */ } PACKED_ATTR() t_client_loginreq2; /******************************************************/ /******************************************************/ /* # 21 packet from client: type=0x46ff(unknown) length=8 class=bnet 0000: FF 46 08 00 00 00 00 00 .F...... */ #define CLIENT_MOTD_W3 0x46ff typedef struct { t_bnet_header h; bn_int last_news_time; /* date of the last news item the client has */ } PACKED_ATTR() t_client_motd_w3; /******************************************************/ /******************************************************/ /* # 22 packet from server: type=0x46ff(unknown) length=225 class=bnet 0000: FF 46 E1 00 01 16 3A 6C 3C FF FF FF FF 00 00 00 .F....:l<....... 0010: 00 00 00 00 00 57 65 6C 63 6F 6D 65 20 74 6F 20 .....Welcome to 0020: 42 61 74 74 6C 65 2E 6E 65 74 21 0A 54 68 69 73 Battle.net!.This 0030: 20 73 65 72 76 65 72 20 69 73 20 68 6F 73 74 65 server is hoste 0040: 64 20 62 79 20 41 54 26 54 2E 0A 54 68 65 72 65 d by AT&T..There 0050: 20 61 72 65 20 63 75 72 72 65 6E 74 6C 79 20 36 are currently 6 0060: 32 38 20 75 73 65 72 73 20 70 6C 61 79 69 6E 67 28 users playing 0070: 20 31 35 39 20 67 61 6D 65 73 20 6F 66 20 57 61 159 games of Wa 0080: 72 63 72 61 66 74 20 49 49 49 2C 20 61 6E 64 20 rcraft III, and 0090: 31 37 37 33 34 36 20 75 73 65 72 73 20 70 6C 61 177346 users pla 00A0: 79 69 6E 67 20 37 37 38 33 37 20 67 61 6D 65 73 ying 77837 games 00B0: 20 6F 6E 20 42 61 74 74 6C 65 2E 6E 65 74 2E 0A on Battle.net.. 00C0: 4C 61 73 74 20 6C 6F 67 6F 6E 3A 20 54 68 75 20 Last logon: Thu 00D0: 46 65 62 20 31 34 20 20 35 3A 32 38 20 50 4D 0A Feb 14 5:28 PM. 00E0: 00 . # Match 4, 2002 # 92 packet from server: type=0x46ff(unknown) length=859 class=bnet 0000: FF 46 5B 03 01 B4 B2 82 3C 20 B6 83 3C 20 B6 83 .F[.....< ..< .. 0010: 3C 20 B6 83 3C 57 65 20 68 61 76 65 20 62 65 65 < .. ws-2-11.1038: P 190:262(72) ack 272 win 65264 ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** FF 53 48 00 00 00 00 00 .SH..... 4B A8 FF 5D 1E 5D 2D 50 D1 2B B2 95 74 AD 5F 4E K..].]-P.+..t._N 88 A4 88 48 18 27 89 50 F1 AA 1B D5 D7 B6 47 BC ...H.'.P......G. 30 8B 2A 54 AA 99 23 96 75 8A 5E 67 35 8E 5B 22 0.*T..#.u.^g5.[" 2C 0E 68 2E C2 95 E9 D7 A1 82 F1 2C 1E 2B 28 36 ,.h........,.+(6 */ #define SERVER_LOGINREPLY_W3 0x53ff typedef struct { t_bnet_header h; bn_int message; /* seems to be response to client-challenge */ bn_byte salt[32]; bn_byte server_public_key[32]; } PACKED_ATTR() t_server_loginreply_w3; #define SERVER_LOGINREPLY_W3_MESSAGE_SUCCESS 0x00000000 #define SERVER_LOGINREPLY_W3_MESSAGE_ALREADY 0x00000001 /* Account already logged on */ #define SERVER_LOGINREPLY_W3_MESSAGE_BADACCT 0x00000001 /* Accoutn does not exist */ /******************************************************/ /******************************************************/ /* single player crack based: # 34 packet from server: type=0x54ff(unknown) length=40 class=bnet 0000: FF 54 28 00 00 00 00 00 00 00 00 00 00 00 00 00 .T(............. 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0020: 00 00 00 00 00 00 00 00 ........ * Password Checksum ? * -- client -- 0x54ff - 2 bytes size - 2 bytes (0x0018) unknown1 - 20 bytes -- server -- 0x54ff - 2 bytes size - 2 bytes msgid - 4 bytes { 0x00000000 accept 0x00000002 password incorrect } unknown1 - 20 bytes Packet #13 0x0000 FF 54 1C 00 00 00 00 00-3A D5 B9 B1 2B D9 B5 D9 T......:Õ¹?Ùµ? 0x0010 87 3B 2B 3D 28 57 C0 2E-02 93 5F 8B ?+=(W?._? */ #define CLIENT_LOGONPROOFREQ 0x54ff typedef struct { t_bnet_header h; bn_byte client_password_proof[20]; } PACKED_ATTR() t_client_logonproofreq; #define SERVER_LOGONPROOFREPLY 0x54ff typedef struct { t_bnet_header h; bn_int response; bn_byte server_password_proof[20]; } PACKED_ATTR() t_server_logonproofreply; #define SERVER_LOGONPROOFREPLY_RESPONSE_OK 0x00000000 #define SERVER_LOGONPROOFREPLY_RESPONSE_BADPASS 0x00000002 #define SERVER_LOGONPROOFREPLY_RESPONSE_EMAIL 0x0000000E #define SERVER_LOGONPROOFREPLY_RESPONSE_CUSTOM 0x0000000F /******************************************************/ /******************************************************/ #define CLIENT_PASSCHANGEREQ 0x55ff typedef struct { t_bnet_header h; bn_byte client_public_key[32]; /* username */ } PACKED_ATTR() t_client_passchangereq; #define SERVER_PASSCHANGEREPLY 0x55ff typedef struct { t_bnet_header h; bn_int message; bn_byte salt[32]; bn_byte server_public_key[32]; } PACKED_ATTR() t_server_passchangereply; #define SERVER_PASSCHANGEREPLY_MESSAGE_ACCEPT 0x00000000 #define SERVER_PASSCHANGEREPLY_MESSAGE_REJECT 0x00000001 /* No such account */ /******************************************************/ /******************************************************/ #define CLIENT_PASSCHANGEPROOFREQ 0x56ff typedef struct { t_bnet_header h; bn_byte client_password_proof[20]; bn_byte salt[32]; bn_byte password_verifier[32]; } PACKED_ATTR() t_client_passchangeproofreq; #define SERVER_PASSCHANGEPROOFREPLY 0x56ff typedef struct { t_bnet_header h; bn_int response; bn_byte server_password_proof[20]; } PACKED_ATTR() t_server_passchangeproofreply; #define SERVER_PASSCHANGEPROOFREPLY_RESPONSE_OK 0x00000000 #define SERVER_PASSCHANGEPROOFREPLY_RESPONSE_BADPASS 0x00000002 /******************************************************/ /******************************************************/ /* # 13 packet from client: type=0x52ff(unknown) length=83 class=bnet 0000: FF 52 53 00 2B 63 B9 05 CA F3 E1 BA 58 5C ED 65 .RS.+c......X\.e 0010: BE 8F 0E 89 A9 B8 C7 FE 75 2C 44 10 AE 19 B5 14 ........u,D..... 0020: E8 CA E9 C7 37 50 7D 0F 9A 89 00 FF 2F 10 BB EE ....7P}...../... 0030: A8 0C 81 64 AD AF DC C7 3F 58 F1 20 A1 05 E2 38 ...d....?X. ...8 0040: 18 87 85 5B 74 68 65 61 63 63 6F 75 6E 74 6E 61 ...[theaccountna 0050: 6D 65 00 me. */ #define CLIENT_CREATEACCOUNT_W3 0x52ff typedef struct { t_bnet_header h; bn_byte salt[32]; bn_byte password_verifier[32]; /* player name */ } PACKED_ATTR() t_client_createaccount_w3; /******************************************************/ /******************************************************/ /******************************************************/ /* # 20 packet from client: type=0x45ff(unknown) length=6 class=bnet 0000: FF 45 06 00 E0 17 .E.... */ #define CLIENT_CHANGEGAMEPORT 0x45ff typedef struct { t_bnet_header h; bn_short port; } PACKED_ATTR() t_client_changegameport; /******************************************************/ /* RECV-> 0000 FF 52 08 00 00 00 00 00 .R...... */ #define SERVER_CREATEACCOUNT_W3 0x52ff typedef struct { t_bnet_header h; bn_int result; } PACKED_ATTR() t_server_createaccount_w3; #define SERVER_CREATEACCOUNT_W3_RESULT_OK 0x00000000 #define SERVER_CREATEACCOUNT_W3_RESULT_EXIST 0x00000004 #define SERVER_CREATEACCOUNT_W3_RESULT_EMPTY 0x00000007 #define SERVER_CREATEACCOUNT_W3_RESULT_INVALID 0x00000008 #define SERVER_CREATEACCOUNT_W3_RESULT_BANNED 0x00000009 #define SERVER_CREATEACCOUNT_W3_RESULT_SHORT 0x0000000A #define SERVER_CREATEACCOUNT_W3_RESULT_PUNCTUATION 0x0000000B #define SERVER_CREATEACCOUNT_W3_RESULT_PUNCTUATION2 0x0000000C /******************************************************/ /******************************************************/ /* FF 3A 08 00 00 00 00 00 .:...... */ #define SERVER_LOGINREPLY2 0x3aff typedef struct { t_bnet_header h; bn_int message; /* reason for account lock */ } PACKED_ATTR() t_server_loginreply2; #define SERVER_LOGINREPLY2_MESSAGE_SUCCESS 0x00000000 #define SERVER_LOGINREPLY2_MESSAGE_NONEXIST 0x00000001 /* Account does not exist */ #define SERVER_LOGINREPLY2_MESSAGE_BADPASS 0x00000002 /* Bad password */ #define SERVER_LOGINREPLY2_MESSAGE_LOCKED 0x00000006 /* Account is locked */ /******************************************************/ /******************************************************/ /* Diablo II 1.03 */ /* sent when registering new player with open Battle.net */ /* and when logging in with closed Battle.net */ /* (closed) b.net login: enter name/password -> 3dff packet (open) b.net login: enter name/password -> "login" packet (bad account) <-- CORRECT "Create new account" -> TOS grab -> Enter password -> 3dff packet */ /* FF 3D 20 00 B8 C0 A1 2F 56 B1 47 65 CF 55 09 62 .= ..../V.Ge.U.b 8E 21 3C 59 57 BC E8 EA 45 6C 66 6C 6F 72 64 00 .!= 1.10: * "%s %u %u %u %u %u %u %u %u %u %s" * client tag (RATS, PXES, RHSS) * rating * number (ladder rank) * stars (normal wins) * spawned (1 of spawned, 0 otherwise) * unknown4 (always zero?) * highest ladder rating * unknown6 (always zero?) * unknown7 (always zero?) * icon tag (usually client tag) * * for DRTL: * "%s %u %u %u %u %u %u %u %u %u" * client tag (LTRD) * level * class (0==warrior, 1==rogue, 2==sorcerer) * dots (times killed diablo) * strength * magic * dexterity * vitality * gold * unknown2 (always zero?) * * for D2DV: * "%s%s,%s," * client tag (VD2D) * realm * character name * 43 unknown bytes */ #define PLAYERINFO_DRTL_CLASS_WARRIOR 0 #define PLAYERINFO_DRTL_CLASS_ROGUE 1 #define PLAYERINFO_DRTL_CLASS_SORCERER 2 /******************************************************/ /******************************************************/ #define CLIENT_PROGIDENT2 0x0bff typedef struct { t_bnet_header h; bn_int clienttag; } PACKED_ATTR() t_client_progident2; /******************************************************/ /******************************************************/ #define CLIENT_JOINCHANNEL 0x0cff typedef struct { t_bnet_header h; bn_int channelflag; } PACKED_ATTR() t_client_joinchannel; #define CLIENT_JOINCHANNEL_NORMAL 0x00000000 #define CLIENT_JOINCHANNEL_GENERIC 0x00000001 #define CLIENT_JOINCHANNEL_CREATE 0x00000002 /******************************************************/ /******************************************************/ #define SERVER_CHANNELLIST 0x0bff typedef struct { t_bnet_header h; /* channel names */ } PACKED_ATTR() t_server_channellist; /******************************************************/ /******************************************************/ /* We don't use this for now. It makes the client put the list of IPs/hostnames into the registry. FF 04 8F 00 00 00 00 00 32 30 39 2E 36 37 2E 31 ........209.67.1 33 36 2E 31 37 34 3B 32 30 37 2E 36 39 2E 31 39 36.174;207.69.19 34 2E 32 31 30 3B 32 30 37 2E 36 39 2E 31 39 34 4.210;207.69.194 2E 31 38 39 3B 32 31 36 2E 33 32 2E 37 33 2E 31 .189;216.32.73.1 37 34 3B 32 30 39 2E 36 37 2E 31 33 36 2E 31 37 74;209.67.136.17 31 3B 32 30 36 2E 37 39 2E 32 35 34 2E 31 39 32 1;206.79.254.192 3B 32 30 37 2E 31 33 38 2E 33 34 2E 33 3B 32 30 ;207.138.34.3;20 39 2E 36 37 2E 31 33 36 2E 31 37 32 3B 65 78 6F 9.67.136.172;exo 64 75 73 2E 62 61 74 74 6C 65 2E 6E 65 74 00 dus.battle.net. */ #define SERVER_SERVERLIST 0x04ff typedef struct { t_bnet_header h; bn_int unknown1; /* 00 00 00 00 */ /* list */ } PACKED_ATTR() t_server_serverlist; #define SERVER_SERVERLIST_UNKNOWN1 0x00000000 /******************************************************/ /******************************************************/ /* FF 0F 30 00 01 00 00 00 00 00 00 00 00 00 00 00 ..0............. 00 00 00 00 00 00 00 00 00 00 00 00 52 6F 73 73 ............Ross 00 52 41 54 53 20 30 20 30 20 30 20 30 20 30 00 .RATS 0 0 0 0 0. FF 0F 38 00 07 00 00 00 21 00 00 00 64 00 00 00 ..8.....!...d... 00 00 00 00 D8 94 F6 07 B3 2C 6E 02 4D 6F 4E 6B .........,n.MoNk 32 6B 00 44 69 61 62 6C 6F 20 49 49 20 42 65 74 2k.Diablo II Bet 61 57 65 73 74 2D 31 00 aWest-1. MT_ADD: 0x0000: ff 0a 53 00 4d 6f 4e 6b 32 6b 00 56 44 32 44 42 ..S.MoNk2k.VD2DB 0x0010: 65 74 61 57 65 73 74 2c 4d 6f 4e 6b 2d 65 65 2c etaWest,MoNk-ee, 0x0020: 83 80 06 01 01 01 01 ff 4c ff 02 02 ff ff ff ff ........L....... 0x0030: ff ff 01 ff 48 48 48 48 ff a6 ff 48 48 ff ff ff ....HHHH...HH... 0x0040: ff ff ff 10 80 80 80 80 ff ff ff 00 4d 6f 4e 6b ............MoNk 0x0050: 32 6b 00 ff 0f 38 00 07 00 00 00 21 00 00 00 6d 2k...8.....!...m 0x0060: 00 00 00 00 00 00 00 d8 94 f6 08 a4 46 6e 02 4d ............Fn.M 0x0070: 6f 4e 6b 32 6b 00 44 69 61 62 6c 6f 20 49 49 20 oNk2k.Diablo II 0x0080: 42 65 74 61 57 65 73 74 2d 31 00 ff 0f 62 00 01 BetaWest-1...b.. 0x0090: 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 d8 .......(........ 0x00a0: 94 f6 07 69 fb 6d 02 4e 6f 72 62 62 6f 00 56 44 ...i.m.Norbbo.VD 0x00b0: 32 44 42 65 74 61 57 65 73 74 2c 44 6f 6f 73 68 2DBetaWest,Doosh 0x00c0: 2c 87 80 05 02 01 01 01 2b ff 1b 02 02 ff ff ff ,.......+....... 0x00d0: ff ff ff 03 ff ff ff ff ff ff ff ff ff ff ff ff ................ 0x00e0: ff ff ff ff 0a 80 80 80 80 ff ff ff 00 ff 0f 65 ...............e 0x00f0: 00 01 00 00 00 00 00 00 00 32 00 00 00 00 00 00 .........2...... 0x0100: 00 d8 94 f6 07 23 00 6e 02 6e 6a 67 6f 61 6c 69 .....#.n.njgoali 0x0110: 65 00 56 44 32 44 42 65 74 61 57 65 73 74 2c 73 e.VD2DBetaWest,s 0x0120: 68 65 69 6b 61 2c 83 80 05 02 01 02 02 ff 4c ff heika,........L. 0x0130: 02 02 ff ff ff ff ff ff 02 ff ff ff ff ff ff ff ................ 0x0140: ff ff ff ff ff ff ff ff ff 09 80 80 80 80 ff ff ................ 0x0150: ff 00 ff 0f 67 00 01 00 00 00 00 00 00 00 1f 00 ....g........... 0x0160: 00 00 00 00 00 00 d8 94 f6 09 eb 24 6e 02 72 6f ...........$n.ro 0x0170: 62 6d 6d 73 64 00 56 44 32 44 42 65 74 61 57 65 bmmsd.VD2DBetaWe 0x0180: 73 74 2c 56 61 6e 63 6f 75 76 65 72 2c 83 80 05 st,Vancouver,... 0x0190: 02 02 01 01 30 ff 1b 02 02 ff ff ff ff ff ff 04 ....0........... 0x01a0: 4f ff ff ff ff ff ff a9 ff ff ff ff ff ff ff ff O............... 0x01b0: 09 80 80 80 80 ff ff ff 00 ff 0f 62 00 01 00 00 ...........b.... 0x01c0: 00 00 00 00 00 6e 00 00 00 00 00 00 00 ce 4f fe .....n........O. 0x01d0: c0 f9 02 15 01 4c 79 63 74 68 69 73 00 56 44 32 .....Lycthis.VD2 0x01e0: 44 42 65 74 61 57 65 73 74 2c 45 6c 6c 65 2c 83 DBetaWest,Elle,. 0x01f0: 80 04 02 01 01 01 ff 4d ff 02 02 ff ff ff ff ff .......M........ 0x0200: ff 01 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 0x0210: ff ff 06 80 80 80 80 ff ff ff 00 ff 0f 6b 00 01 .............k.. 0x0220: 00 00 00 00 00 00 00 19 01 00 00 00 00 00 00 ce ................ 0x0230: 4f fe c1 e2 1b 9c 00 52 6f 62 4d 69 74 63 68 65 O......RobMitche 0x0240: 6c 6c 00 56 44 32 44 42 65 74 61 57 65 73 74 2c ll.VD2DBetaWest, 0x0250: 53 6f 6f 6e 65 72 64 65 64 2c 83 80 06 02 02 01 Soonerded,...... 0x0260: 01 46 46 ff 02 02 ff ff ff ff ff ff 05 ff ff ff .FF............. 0x0270: ff ff ff 29 ff ff ff ff ff ff ff ff ff 11 80 82 ...)............ 0x0280: 80 80 ff ff ff 00 ff 0f 66 00 01 00 00 00 00 00 ........f....... 0x0290: 00 00 bc 00 00 00 00 00 00 00 d8 94 f6 09 8f 3f ...............? 0x02a0: 6e 02 4d 61 72 6c 6f 63 6b 31 00 56 44 32 44 42 n.Marlock1.VD2DB 0x02b0: 65 74 61 57 65 73 74 2c 6d 61 72 6c 6f 63 6b 2c etaWest,marlock, 0x02c0: 83 80 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 0x02d0: ff ff 05 ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 0x02e0: ff ff ff 01 80 80 80 80 ff ff ff 00 ff 0f 63 00 ..............c. 0x02f0: 01 00 00 00 00 00 00 00 c8 00 00 00 00 00 00 00 ................ 0x0300: d1 43 88 aa bd ce 3c 00 42 2d 57 61 74 74 7a 00 .C....<.B-Wattz. From bnetd-0.4.23pre18 to Diablo II 1.03 FF 0F 69 00 09 00 00 00 00 00 00 00 12 00 I@..i... ........ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 6C ........ ......El 66 6C 6F 72 64 00 56 44 32 44 51 61 72 61 74 68 flord.VD 2DQarath 52 65 61 6C 6D 2C 46 61 6B 65 43 68 61 72 2C 83 Realm,Fa keChar,. 80 FF FF FF FF FF 2F FF FF FF FF FF FF FF FF FF ....../. ........ FF 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF ........ ........ FF FF 07 80 80 80 80 FF FF FF 00 ........ ... */ #define SERVER_MESSAGE 0x0fff typedef struct { t_bnet_header h; bn_int type; bn_int flags; /* player flags (or channel flags for MT_CHANNEL) */ bn_int latency; bn_int player_ip; /* always zero? */ bn_int account_num; /* player's IP (big endian), no longer used, always 0D F0 AD BA */ bn_int reg_auth; /* server ip and/or reg auth? CD key and/or account number? */ /* player name */ /* text */ } PACKED_ATTR() t_server_message; #define SERVER_MESSAGE_PLAYER_IP_DUMMY 0x00000000 /* nok */ #define SERVER_MESSAGE_REG_AUTH 0xBAADF00D /* 0D F0 AD BA */ /* #define SERVER_MESSAGE_REG_AUTH 0x07f694d8 */ #define SERVER_MESSAGE_ACCOUNT_NUM 0x0df0adba /* For MT_ADD, MT_JOIN, the text portion looks like: * * for STAR, SEXP, SSHR: * "%4c %u %u %u %u %u" * client tag (RATS, PXES, RHSS) * rating * number (ladder rank) * stars (normal wins) * unknown3 (always zero?) * unknown4 (always zero?) * * for DRTL: * "%4c %u %u %u %u %u %u %u %u %u" * client tag (LTRD) FIXME: RHSD? * level * class (0==warrior, 1==rogue, 2==sorcerer) * dots (times killed diablo) * strength * magic * dexterity * vitality * gold * unknown2 (always zero?) * * for CHAT: * FIXME: ??? "%4c" * client tag (TAHC) * * FIXME: Warcraft II? * * for D2DV: * open: * "%4c" * client tag (VD2D) * closed: * "%4c%s,%s,%s" * client tag (VD2D) * realm name * character name * character info */ #define SERVER_MESSAGE_TYPE_ADDUSER 0x00000001 /* ADD,USER,SHOWUSER */ #define SERVER_MESSAGE_TYPE_JOIN 0x00000002 #define SERVER_MESSAGE_TYPE_PART 0x00000003 /* LEAVE */ #define SERVER_MESSAGE_TYPE_WHISPER 0x00000004 #define SERVER_MESSAGE_TYPE_TALK 0x00000005 /* MESSAGE */ #define SERVER_MESSAGE_TYPE_BROADCAST 0x00000006 #define SERVER_MESSAGE_TYPE_CHANNEL 0x00000007 /* JOINING */ /* unused? 0x00000008 */ #define SERVER_MESSAGE_TYPE_USERFLAGS 0x00000009 #define SERVER_MESSAGE_TYPE_WHISPERACK 0x0000000a /* WHISPERSENT */ /* unused? 0x0000000b */ /* unused? 0x0000000c */ #define SERVER_MESSAGE_TYPE_CHANNELFULL 0x0000000d #define SERVER_MESSAGE_TYPE_CHANNELDOESNOTEXIST 0x0000000e #define SERVER_MESSAGE_TYPE_CHANNELRESTRICTED 0x0000000f /* unused? 0x00000010 */ /* unused? 0x00000011 */ #define SERVER_MESSAGE_TYPE_INFO 0x00000012 #define SERVER_MESSAGE_TYPE_ERROR 0x00000013 /* unused? 0x00000014 */ /* unused? 0x00000015 */ /* unused? 0x00000016 */ #define SERVER_MESSAGE_TYPE_EMOTE 0x00000017 /****** Player Flags ******/ /* flag bits for above struct */ /* ADDED BY UNDYING SOULZZ 4/7/02 */ #define W3_ICON_SET 0x00000000 #define MAX_STR_RACELEN 20 #define MAX_STR_ACCTPASSLEN 10 #define W3_RACE_RANDOM 32 #define W3_RACE_HUMANS 1 #define W3_RACE_ORCS 2 #define W3_RACE_UNDEAD 8 #define W3_RACE_NIGHTELVES 4 #define W3_RACE_DEMONS 16 #define W3_ICON_RANDOM 0 /* - Although when client presses random in PG and it sends "32" its "0" for icon */ #define W3_ICON_HUMANS 1 #define W3_ICON_ORCS 2 #define W3_ICON_UNDEAD 3 /* - Although when client presses undead in PG and it sends "8" its "3" for icon */ #define W3_ICON_NIGHTELVES 4 #define W3_ICON_DEMONS 5 /* Icon setup 3RAW then */ /* Races: 1 = human, 2 = orc, 8 = undead, 4 = nightelf 32 = random */ /* Misc icons are 6-9 * - There might be some icons not defined*/ /* If you find them let us know pse, forums.cheatlist.com in War3 Hacking/Development */ /*Human Icons*/ #define W3_ICON_HUMAN_FOOTMAN "3RAW 1 1 11" #define W3_ICON_HUMAN_KNIGHT "3RAW 1 1 100" #define W3_ICON_HUMAN_ARCHMAGE "3RAW 1 1 250" #define W3_ICON_HUMAN_HERO "3RAW 1 1 1000" /*Orc Icons*/ #define W3_ICON_ORC_PEON "3RAW 1 2 00" /* default icon , unless u change it in code*/ #define W3_ICON_ORC_GRUNT "3RAW 1 2 10" #define W3_ICON_ORC_TAUREN "3RAW 1 2 100" #define W3_ICON_ORC_FARSEER "3RAW 1 2 250" #define W3_ICON_ORC_HERO "3RAW 1 2 1000" /*Undead Icons*/ #define W3_ICON_UNDEAD_GHOUL "3RAW 1 3 10" #define W3_ICON_UNDEAD_ABOM "3RAW 1 3 100" #define W3_ICON_UNDEAD_LICH "3RAW 1 3 250" #define W3_ICON_UNDEAD_HERO "3RAW 1 3 1000" /*Night Elf Icons*/ #define W3_ICON_ELF_ARCHER "3RAW 1 4 10" #define W3_ICON_ELF_DRUIDCLAW "3RAW 1 4 100" #define W3_ICON_ELF_PRIESMOON "3RAW 1 4 250" #define W3_ICON_ELF_HERO "3RAW 1 4 1000" /*Random Icons , like NPC's, Creeps*/ #define W3_ICON_RANDOM_GREENDRAGON "3RAW 1 9 10" #define W3_ICON_RANDOM_BLACKDRAGON "3RAW 1 9 100" #define W3_ICON_RANDOM_REDDRAGON "3RAW 1 9 250" #define W3_ICON_RANDOM_BLUEDRAGON "3RAW 1 9 1000" /* End of Undying Edits */ /* Blizzard Entertainment employee */ #define MF_BLIZZARD 0x00000001 /* blue Blizzard logo */ /* Channel operator */ #define MF_GAVEL 0x00000002 /* gavel */ /* Speaker in moderated channel */ #define MF_VOICE 0x00000004 /* megaphone */ /* System operator */ #define MF_BNET 0x00000008 /* (old: blue Blizzard, new: green b.net) or red BNETD logo */ /* Chat bot or other user without UDP support */ #define MF_PLUG 0x00000010 /* tiny plug to right of icon, no UDP */ /* Squelched/Ignored user */ #define MF_X 0x00000020 /* big red X */ /* Special guest of Blizzard Entertainment */ #define MF_SHADES 0x00000040 /* sunglasses */ /* unused 0x00000080 */ /* Use BEL character in error codes. Some bots use it as a flag. Battle.net */ /* stopped supporting it recently. */ #define MF_BEEP 0x00000100 /* no change in icon */ /* Registered Professional Gamers League player */ #define MF_PGLPLAY 0x00000200 /* PGL player logo */ /* Registered Professional Gamers League official */ #define MF_PGLOFFL 0x00000400 /* PGL official logo */ /* Registered KBK player */ #define MF_KBKPLAY 0x00000800 /* KBK player logo */ /* Official KBK Referee */ #define MF_KBKREF 0x00001000 /* KBK referee logo */ /* FIXME: this number may be wrong */ /* unused... FIXME: how many bits work? 16 or 32? */ /****** Channel Flags ******/ /* flag bits for MT_CHANNEL message */ #define CF_PUBLIC 0x00000001 /* public channel */ #define CF_MODERATED 0x00000002 /* moderated channel */ #define CF_RESTRICTED 0x00000004 /* ? restricted channel ? */ #define CF_THEVOID 0x00000008 /* "The Void" */ #define CF_SYSTEM 0x00000020 /* system channel */ #define CF_OFFICIAL 0x00001000 /* official channel */ /* * Examples: * 0x00001003 Blizzard Tech Support * 0x00001001 Open Tech Support * 0x00000021 Diablo II USA-1 or War2BNE USA-1 * 0x0000000D warez * 0x00000009 The Void * 0x00000001 War2 Ladder Challenges or Diablo II PvP * 0x00000000 clan randomchannel * 0x00000000 randomchannel */ /******************************************************/ /******************************************************/ #define CLIENT_MESSAGE 0x0eff typedef struct { t_bnet_header h; /* text */ } PACKED_ATTR() t_client_message; /******************************************************/ /******************************************************/ /* FF 09 17 00 03 00 00 00 FF FF 00 00 00 00 00 00 ................ 19 00 00 00 00 00 00 ....... FF 09 24 00 00 00 00 00 00 00 00 00 00 00 00 00 ..$............. 01 00 00 00 4C 61 64 64 65 72 20 31 20 6F 6E 20 ....Ladder 1 on 31 00 00 00 1... */ #define CLIENT_GAMELISTREQ 0x09ff typedef struct { t_bnet_header h; bn_short gametype; bn_short unknown1; bn_int unknown2; bn_int unknown3; bn_int maxgames; /* game name */ } PACKED_ATTR() t_client_gamelistreq; #define CLIENT_GAMELISTREQ_ALL 0x0000 #define CLIENT_GAMELISTREQ_MELEE 0x0002 #define CLIENT_GAMELISTREQ_FFA 0x0003 #define CLIENT_GAMELISTREQ_ONEONONE 0x0004 #define CLIENT_GAMELISTREQ_CTF 0x0005 #define CLIENT_GAMELISTREQ_GREED 0x0006 #define CLIENT_GAMELISTREQ_SLAUGHTER 0x0007 #define CLIENT_GAMELISTREQ_SDEATH 0x0008 #define CLIENT_GAMELISTREQ_LADDER 0x0009 #define CLIENT_GAMELISTREQ_IRONMAN 0x0010 #define CLIENT_GAMELISTREQ_MAPSET 0x000a #define CLIENT_GAMELISTREQ_TEAMMELEE 0x000b #define CLIENT_GAMELISTREQ_TEAMFFA 0x000c #define CLIENT_GAMELISTREQ_TEAMCTF 0x000d #define CLIENT_GAMELISTREQ_PGL 0x000e #define CLIENT_GAMELISTREQ_TOPVBOT 0x000f #define CLIENT_GAMELISTREQ_DIABLO 0x0409 /* FIXME: this should be the langid */ #define CLIENT_GAMELISTREQ_LOADED 0x0a00 /* FIXME: Diablo reports differently than it is listed in GAMELIST */ #define CLIENT_GAMETYPE_DIABLO_0 0x00000000 /* Level 1 Char */ #define CLIENT_GAMETYPE_DIABLO_1 0x00000001 /* Level 2 Char */ #define CLIENT_GAMETYPE_DIABLO_2 0x00000002 /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_3 0x00000003 /* Level 4 Char */ #define CLIENT_GAMETYPE_DIABLO_4 0x00000004 /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_5 0x00000005 /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_6 0x00000006 /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_7 0x00000007 /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_8 0x00000008 /* Level 20-24 Char */ #define CLIENT_GAMETYPE_DIABLO_9 0x00000009 /* Level 25-? Char */ #define CLIENT_GAMETYPE_DIABLO_a 0x0000000a /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_b 0x0000000b /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_c 0x0000000c /* Level ? Char */ #define CLIENT_GAMETYPE_DIABLO_d 0x0000000d /* Level ? Char */ /* list might continue - what is maximum diablo level ? */ /* FIXME: Not sure how Diablo II does things yet */ #define CLIENT_GAMETYPE_DIABLO2_CLOSE 0x00000000 /* close game */ #define CLIENT_GAMETYPE_DIABLO2_OPEN_NORMAL 0X00000008 /* open, normal difficulty */ #define CLIENT_GAMETYPE_DIABLO2_OPEN_NIGHTMARE 0X00000009 /* open, nightmare difficulty */ #define CLIENT_GAMETYPE_DIABLO2_OPEN_HELL 0X0000000a /* open, hell difficulty */ #define CLIENT_GAMETYPE_DIABLO2_OPEN_HARDCORE_NORMAL 0X0000000c /* open, hardcore, normal difficulty */ #define CLIENT_GAMETYPE_DIABLO2_OPEN_HARDCORE_NIGHTMARE 0X0000000d /* open, hardcore, nightmare difficulty */ #define CLIENT_GAMETYPE_DIABLO2_OPEN_HARDCORE_HELL 0X0000000e /* open, hardcore, hell difficulty */ /******************************************************/ /******************************************************/ /* FF 09 35 00 01 00 00 00 00 00 00 00 03 00 01 00 ..5............. 00 00 00 00 02 00 17 E0 80 7B 4F 0D 00 00 00 00 .........{O..... 00 00 00 00 04 00 00 00 64 00 00 00 4D 79 47 61 ........d...MyGa 6D 65 00 00 00 me... FF 09 5B 00 01 00 00 00 00 00 00 00 03 00 01 00 ..[............. 02 00 17 E0 80 7B 4F 0D 00 00 00 00 00 00 00 00 .....{O......... 04 00 00 00 2B 00 00 00 47 61 6D 65 00 50 61 73 ....+...Game.Pas 73 00 2C 33 34 2C 31 32 2C 35 2C 31 2C 33 2C 31 s.,34,12,5,1,3,1 2C 63 63 63 33 36 34 30 36 2C 2C 42 6F 62 0D 43 ,ccc36406,,Bob.C 68 61 6C 6C 65 6E 67 65 72 0D 00 hallenger.. FF 09 D4 03 0A 00 00 00 0C 00 00 00 09 04 00 00 ................ 02 00 17 E0 CD E8 B5 E1 00 00 00 00 00 00 00 00 ................ 00 00 00 00 3C 00 00 00 4A 65 73 73 65 27 73 20 ....<...Jesse's 57 6F 72 6C 64 00 00 32 0D 4C 69 7A 7A 69 65 2E World..2.Lizzie. 42 6F 72 64 65 6E 0D 4C 54 52 44 20 34 30 20 31 Borden.LTRD 40 1 20 33 20 31 32 31 20 31 32 36 20 33 30 36 20 31 3 121 126 306 1 33 36 20 35 34 37 30 32 20 30 00 ... FF 09 70 00 01 00 00 00 0F 00 04 00 09 04 00 00 ..p............. 02 00 17 E0 C6 0B 13 3C 00 00 00 00 00 00 00 00 .......<........ 04 00 00 00 C5 00 00 00 4C 61 64 64 65 72 20 31 ........Ladder 1 20 6F 6E 20 31 00 00 2C 2C 2C 36 2C 32 2C 66 2C on 1..,,,6,2,f, 34 2C 66 63 63 35 38 65 34 61 2C 37 32 30 30 2C 4,fcc58e4a,7200, 49 63 65 36 39 62 75 72 67 0D 46 6F 72 65 73 74 Ice69burg.Forest 20 54 72 61 69 6C 20 42 4E 45 2E 70 75 64 0D 00 Trail BNE.pud.. # war3 # 66 packet from server: type=0x09ff(SERVER_GAMELISTREPLY) length=131 class=bnet 0000: FF 09 83 00 01 00 00 00 01 00 00 00 09 04 00 00 ................ 0010: 02 00 17 E0 18 CF BF 9B 00 00 00 00 00 00 00 00 ................ 0020: 10 00 00 00 0F 00 00 00 33 20 6F 6E 20 33 20 64 ........3 on 3 d 0030: 61 72 6B 20 66 6F 72 65 73 74 00 00 35 31 30 30 ark forest..5100 0040: 30 30 30 30 30 01 03 01 01 81 01 81 01 73 27 25 00000........s'% 0050: 15 29 4D 61 71 53 73 5D 63 65 75 61 5D A9 29 37 .)MaqSs]ceua].)7 0060: 29 45 61 73 6B 69 21 47 6F 73 65 73 75 DD 2F 77 )Easki!Gosesu./w 0070: 33 6D 01 4B 61 D7 69 73 69 69 6F 5B 53 07 4B 5D 3m.Ka.isiio[S.K] 0080: 01 01 00 ... [23:32] <@nok-> 0000: FF 09 E9 01 04 00 00 00 01 00 00 00 09 04 00 00 ................ [23:32] <@nok-> 0010: 02 00 17 E0 40 69 1B 07 00 00 00 00 00 00 00 00 ....@i.......... 0000: FF 09 7A 01 03 00 00 00 01 00 00 00 09 04 00 00 ..z............. 0010: 02 00 17 E0 18 2C 7E 7B 00 00 00 00 00 00 00 00 .....,~{........ 0020: 10 00 00 00 09 00 00 00 34 20 6F 6E 20 34 20 4D ........4 on 4 M 0030: 69 73 74 00 00 37 31 30 30 30 30 30 30 30 01 03 ist..710000000.. 0040: 01 01 89 01 89 01 75 4D 7B 27 A1 4D 61 71 53 73 ......uM{'.MaqSs 0050: 5D 63 65 75 61 5D B9 29 39 29 47 6F 6D 65 17 6D ]ceua].)9)Gome.m 0060: 73 21 69 6F 21 75 75 69 65 21 4D 69 73 75 1D 2F s!io!uuie!Misu./ 0070: 77 33 6D 01 51 73 BB 69 6F 63 65 2D 4D 75 17 63 w3m.Qs.ioce-Mu.c 0080: 69 67 65 73 01 01 00 01 00 00 00 09 04 00 00 02 iges............ */ #define SERVER_GAMELISTREPLY 0x09ff typedef struct { t_bnet_header h; bn_int gamecount; bn_int sstatus; /* when reply with error to specific game */ /* games */ } PACKED_ATTR() t_server_gamelistreply; #define SERVER_GAMELISTREPLY_GAME_SSTATUS_NOTFOUND 0x0 /* but also any other value diff from the ones bellow */ #define SERVER_GAMELISTREPLY_GAME_SSTATUS_PASS 0x2 /* password incorrect */ #define SERVER_GAMELISTREPLY_GAME_SSTATUS_FULL 0x3 /* game full */ #define SERVER_GAMELISTREPLY_GAME_SSTATUS_STARTED 0x4 /* game started */ #define SERVER_GAMELISTREPLY_GAME_SSTATUS_NOSPAWNCDKEY 0x5 /* trying to use a spawn install join invalid cdkey creator game */ #define SERVER_GAMELISTREPLY_GAME_SSTATUS_LOADED 0x0a00 /* Loaded game */ typedef struct { /* if yak doesn't like this... then the client doesn't also =) (bbf) bn_int unknown7; */ bn_short gametype; bn_short unknown1; /* langid under Diablo... */ bn_short unknown3; /* bn_int deleted; */ /* they changed the structure at one point */ bn_short port; /* big endian byte order... at least they are consistent! */ bn_int game_ip; /* big endian byte order */ bn_int unknown4; bn_int unknown5; /* FIXME: got to figure out where latency is */ bn_int status; bn_int unknown6; /* game name */ /* clear password */ /* info */ } PACKED_ATTR() t_server_gamelistreply_game; #define SERVER_GAMELISTREPLY_GAME_UNKNOWN7 0x00000000 //0x00000409 // 0x0000000c #define SERVER_GAMELISTREPLY_GAME_UNKNOWN1 0x0001 //0x0000 //0x0001 // 0x0000 #define SERVER_GAMELISTREPLY_GAME_UNKNOWN3 0x0002 #define SERVER_GAMELISTREPLY_GAME_UNKNOWN4 0x00000000 #define SERVER_GAMELISTREPLY_GAME_UNKNOWN5 0x00000000 #define SERVER_GAMELISTREPLY_GAME_STATUS_OPEN 0x00000004 #define SERVER_GAMELISTREPLY_GAME_STATUS_FULL 0x00000006 #define SERVER_GAMELISTREPLY_GAME_STATUS_STARTED 0x0000000e #define SERVER_GAMELISTREPLY_GAME_STATUS_DONE 0x0000000c #define SERVER_GAMELISTREPLY_GAME_UNKNOWN6 0x0000002b /* latency? */ #define SERVER_GAMELISTREPLY_TYPE_DIABLO2_OPEN 0x0704 /* open game */ /******************************************************/ /******************************************************/ #define CLIENT_STARTGAME1 0x08ff /* original starcraft or shareware (1.01) */ typedef struct { t_bnet_header h; bn_int status; bn_int unknown3; bn_short gametype; bn_short unknown1; bn_int unknown4; bn_int unknown5; /* game name */ /* game password */ /* game info */ } PACKED_ATTR() t_client_startgame1; /* I have also seen 1,5,7,f */ #define CLIENT_STARTGAME1_STATUSMASK 0x0000000f #define CLIENT_STARTGAME1_STATUS_OPEN 0x00000004 #define CLIENT_STARTGAME1_STATUS_FULL 0x00000006 #define CLIENT_STARTGAME1_STATUS_STARTED 0x0000000e #define CLIENT_STARTGAME1_STATUS_DONE 0x0000000c /******************************************************/ /******************************************************/ /* FF 1B 14 00 02 00 17 E0 80 7B 3F 54 00 00 00 00 .........{?T.... 00 00 00 00 .... */ #define CLIENT_UNKNOWN_1B 0x1bff typedef struct { t_bnet_header h; bn_short unknown1; /* FIXME: This "2" is the same as in the game listings. What do they mean? */ bn_short port; /* big endian byte order */ bn_int ip; /* big endian byte order */ bn_int unknown2; bn_int unknown3; } PACKED_ATTR() t_client_unknown_1b; #define CLIENT_UNKNOWN_1B_UNKNOWN1 0x0002 #define CLIENT_UNKNOWN_1B_UNKNOWN2 0x00000000 #define CLIENT_UNKNOWN_1B_UNKNOWN3 0x00000000 /******************************************************/ /******************************************************/ /* FF 1A 4C 00 01 00 00 00 00 00 00 00 00 00 00 00 ..L............. 0F 00 00 00 00 00 00 00 E0 17 00 00 61 6E 73 00 ............ans. 65 6C 6D 6F 00 30 0D 77 61 72 72 69 6F 72 0D 4C elmo.0.warrior.L 54 52 44 20 31 20 30 20 30 20 33 30 20 31 30 20 TRD 1 0 0 30 10 32 30 20 32 35 20 31 30 30 20 30 00 20 25 100 0. */ #define CLIENT_STARTGAME3 0x1aff /* Starcraft 1.03, Diablo 1.07 */ typedef struct { t_bnet_header h; bn_int status; bn_int unknown3; bn_short gametype; bn_short unknown1; bn_int unknown6; bn_int unknown4; /* port # under Diablo */ bn_int unknown5; /* game name */ /* game password */ /* game info */ } PACKED_ATTR() t_client_startgame3; #define CLIENT_STARTGAME3_STATUSMASK 0x0000000f #define CLIENT_STARTGAME3_STATUS_OPEN1 0x00000001 /* used by Diablo */ #define CLIENT_STARTGAME3_STATUS_OPEN 0x00000004 #define CLIENT_STARTGAME3_STATUS_FULL 0x00000006 #define CLIENT_STARTGAME3_STATUS_STARTED 0x0000000e #define CLIENT_STARTGAME3_STATUS_DONE 0x0000000c /******************************************************/ /******************************************************/ /* FF 1C 49 00 00 00 00 00 00 00 00 00 03 00 01 00 ..I............. 00 00 00 00 00 00 00 00 74 65 61 6D 6D 65 6C 65 ........teammele 65 00 00 2C 2C 2C 2C 31 2C 33 2C 31 2C 33 65 33 e..,,,,1,3,1,3e3 37 61 38 34 63 2C 37 2C 61 6E 73 65 6C 6D 6F 0D 7a84c,7,anselmo. 4F 63 74 6F 70 75 73 0D 00 Octopus.. Brood War 1.04 ladder, disconnect==loss FF 1C 45 00 10 00 00 00 00 00 00 00 09 00 02 00 ..E............. 00 00 00 00 01 00 00 00 54 45 53 54 00 00 2C 34 ........TEST..,4 34 2C 31 34 2C 2C 32 2C 39 2C 32 2C 33 65 33 37 4,14,,2,9,2,3e37 61 38 34 63 2C 33 2C 52 6F 73 73 0D 41 73 68 72 a84c,3,Ross.Ashr 69 67 6F 0D 00 igo.. Brood War 1.04 ladder, disconnect==disconnect FF 1C 45 00 00 00 00 00 00 00 00 00 09 00 01 00 ..E............. 00 00 00 00 01 00 00 00 54 45 53 54 00 00 2C 34 ........TEST..,4 34 2C 31 34 2C 2C 32 2C 39 2C 31 2C 33 65 33 37 4,14,,2,9,1,3e37 61 38 34 63 2C 33 2C 52 6F 73 73 0D 41 73 68 72 a84c,3,Ross.Ashr 69 67 6F 0D 00 igo.. Brood War 1.04 greed, minerals==10000 FF 1C 4C 00 00 00 00 00 00 00 00 00 06 00 04 00 ..L............. 00 00 00 00 00 00 00 00 74 65 73 74 31 30 30 30 ........test1000 30 00 00 2C 33 34 2C 31 32 2C 2C 31 2C 36 2C 34 0..,34,12,,1,6,4 2C 33 65 33 37 61 38 34 63 2C 2C 52 6F 73 73 0D ,3e37a84c,,Ross. 43 68 61 6C 6C 65 6E 67 65 72 0D 00 Challenger.. FF 1C 4F 00 ..O. 00 00 00 00 00 00 00 00 02 00 01 00 1F 00 00 00 ................ 00 00 00 00 74 65 73 74 00 00 2C 34 34 2C 31 34 ....test..,44,14 2C 35 2C 32 2C 32 2C 31 2C 32 31 30 34 62 62 33 ,5,2,2,1,2104bb3 36 2C 34 2C 48 6F 6D 65 72 0D 54 68 65 20 4C 6F 6,4,Homer.The.Lo 73 74 20 54 65 6D 70 6C 65 0D 00 st.Temple.. Diablo II 1.03 (level diff 0) FF 1C 20 00 00 00 00 00 00 00 00 00 00 00 .. ... ........ 00 00 00 00 00 00 00 00 00 00 54 65 73 74 00 00 ........ ..Test.. 31 00 1. */ #define CLIENT_STARTGAME4 0x1cff /* Brood War or newer Starcraft (1.04, 1.05) */ typedef struct { t_bnet_header h; bn_short status; /* 0x0001 - private war3 game */ bn_short flag; bn_int unknown2; /* 00 00 00 00 */ bn_short gametype; bn_short option; /* 01 00 */ bn_int unknown4; /* 00 00 00 00 */ bn_int unknown5; /* 00 00 00 00 */ /* game name */ /* game password */ /* game info */ } PACKED_ATTR() t_client_startgame4; #define CLIENT_STARTGAME4_UNKNOWN2 0x00000000 #define CLIENT_STARTGAME4_STATUSMASK_16 0x000000ff #define CLIENT_STARTGAME4_STATUSMASK_INIT_VALID 0x00000093 #define CLIENT_STARTGAME4_STATUSMASK_OPEN_VALID 0x0000009f #define CLIENT_STARTGAME4_STATUS_INIT 0x00000000 #define CLIENT_STARTGAME4_STATUS_PRIVATE 0x00000001 #define CLIENT_STARTGAME4_STATUS_FULL 0x00000002 #define CLIENT_STARTGAME4_STATUS_OPEN 0x00000004 #define CLIENT_STARTGAME4_STATUS_START 0x00000008 #define CLIENT_STARTGAME4_STATUS_DISC_IS_LOSS 0x00000010 #define CLIENT_STARTGAME4_STATUS_REPLAY 0x00000080 #define CLIENT_STARTGAME4_OPTION_MELEE_NORMAL 0x0001 #define CLIENT_STARTGAME4_OPTION_FFA_NORMAL 0x0001 #define CLIENT_STARTGAME4_OPTION_ONEONONE_NORMAL 0x0001 #define CLIENT_STARTGAME4_OPTION_CTF_NORMAL 0x0001 #define CLIENT_STARTGAME4_OPTION_GREED_10000 0x0004 #define CLIENT_STARTGAME4_OPTION_GREED_7500 0x0003 #define CLIENT_STARTGAME4_OPTION_GREED_5000 0x0002 #define CLIENT_STARTGAME4_OPTION_GREED_2500 0x0001 #define CLIENT_STARTGAME4_OPTION_SLAUGHTER_60 0x0004 #define CLIENT_STARTGAME4_OPTION_SLAUGHTER_45 0x0003 #define CLIENT_STARTGAME4_OPTION_SLAUGHTER_30 0x0002 #define CLIENT_STARTGAME4_OPTION_SLAUGHTER_15 0x0001 #define CLIENT_STARTGAME4_OPTION_SDEATH_NORMAL 0x0001 #define CLIENT_STARTGAME4_OPTION_LADDER_COUNTASLOSS 0x0002 #define CLIENT_STARTGAME4_OPTION_LADDER_NOPENALTY 0x0001 #define CLIENT_STARTGAME4_OPTION_IRONMAN_ 0x000 /* FIXME */ #define CLIENT_STARTGAME4_OPTION_MAPSET_NORMAL 0x0001 #define CLIENT_STARTGAME4_OPTION_TEAMMELEE_4 0x0003 #define CLIENT_STARTGAME4_OPTION_TEAMMELEE_3 0x0002 #define CLIENT_STARTGAME4_OPTION_TEAMMELEE_2 0x0001 #define CLIENT_STARTGAME4_OPTION_TEAMFFA_4 0x0003 #define CLIENT_STARTGAME4_OPTION_TEAMFFA_3 0x0002 #define CLIENT_STARTGAME4_OPTION_TEAMFFA_2 0x0001 #define CLIENT_STARTGAME4_OPTION_TEAMCTF_4 0x0003 #define CLIENT_STARTGAME4_OPTION_TEAMCTF_3 0x0002 #define CLIENT_STARTGAME4_OPTION_TEAMCTF_2 0x0001 #define CLIENT_STARTGAME4_OPTION_PGL_ 0x000 /* FIXME */ #define CLIENT_STARTGAME4_OPTION_TOPVBOT_1 0x0001 /* 1 vs all [ 1x1, 1x2, 1x3 ...] */ #define CLIENT_STARTGAME4_OPTION_TOPVBOT_2 0x0002 /* f.e. for (8) The Hunters.scm 1 vs all */ #define CLIENT_STARTGAME4_OPTION_TOPVBOT_3 0x0003 /* means 1x7 */ #define CLIENT_STARTGAME4_OPTION_TOPVBOT_4 0x0004 /* 4 vs all */ #define CLIENT_STARTGAME4_OPTION_TOPVBOT_5 0x0005 #define CLIENT_STARTGAME4_OPTION_TOPVBOT_6 0x0006 #define CLIENT_STARTGAME4_OPTION_TOPVBOT_7 0x0007 #define CLIENT_STARTGAME4_UNKNOWN4 0x00000000 #define CLIENT_STARTGAME4_UNKNOWN5 0x00000000 #define CLIENT_STARTGAME4_OPTION_NONE 0x000 /* FIXME */ #define CLIENT_STARTGAME4_FLAG_PRIVATE 0x0001 #define CLIENT_STARTGAME4_FLAG_PRIVATE_PASSWORD "password" #define CLIENT_MAPTYPE_SELFMADE 0 #define CLIENT_MAPTYPE_BLIZZARD 1 #define CLIENT_MAPTYPE_LADDER 2 #define CLIENT_MAPTYPE_PGL 3 #define CLIENT_MAPTYPE_KBK 4 #define CLIENT_MAPTYPE_CompUSA 5 /* CLIENT_GAMESPEED_FAST is NULL for "fast" games, I convert it into 4 */ #define CLIENT_GAMESPEED_SLOWEST 0 #define CLIENT_GAMESPEED_SLOWER 1 #define CLIENT_GAMESPEED_SLOW 2 #define CLIENT_GAMESPEED_NORMAL 3 #define CLIENT_GAMESPEED_FAST 4 #define CLIENT_GAMESPEED_FASTER 5 #define CLIENT_GAMESPEED_FASTEST 6 /* The tileset is NULL for BADLANDS, I'm using zero here */ #define CLIENT_TILESET_BADLANDS 0 #define CLIENT_TILESET_SPACE 1 #define CLIENT_TILESET_INSTALLATION 2 #define CLIENT_TILESET_ASHWORLD 3 #define CLIENT_TILESET_JUNGLE 4 #define CLIENT_TILESET_DESERT 5 #define CLIENT_TILESET_ICE 6 #define CLIENT_TILESET_TWILIGHT 7 /* Diablo II Difficulty */ #define CLIENT_DIFFICULTY_NORMAL 1 #define CLIENT_DIFFICULTY_NIGHTMARE 2 #define CLIENT_DIFFICULTY_HELL 3 /* assumed */ #define CLIENT_DIFFICULTY_HARDCORE_NORMAL 4 #define CLIENT_DIFFICULTY_HARDCORE_NIGHTMARE 5 /* assumed */ #define CLIENT_DIFFICULTY_HARDCORE_HELL 6 /* assumed */ /******************************************************/ /******************************************************/ #define SERVER_STARTGAME1_ACK 0x08ff typedef struct { t_bnet_header h; bn_int reply; } PACKED_ATTR() t_server_startgame1_ack; #define SERVER_STARTGAME1_ACK_NO 0x00000000 #define SERVER_STARTGAME1_ACK_OK 0x00000001 /******************************************************/ /******************************************************/ #define SERVER_STARTGAME3_ACK 0x1aff typedef struct { t_bnet_header h; bn_int reply; } PACKED_ATTR() t_server_startgame3_ack; #define SERVER_STARTGAME3_ACK_NO 0x00000000 #define SERVER_STARTGAME3_ACK_OK 0x00000001 /******************************************************/ /******************************************************/ #define SERVER_STARTGAME4_ACK 0x1cff typedef struct { t_bnet_header h; bn_int reply; } PACKED_ATTR() t_server_startgame4_ack; #define SERVER_STARTGAME4_ACK_NO 0x00000001 #define SERVER_STARTGAME4_ACK_OK 0x00000000 /******************************************************/ /******************************************************/ #define CLIENT_CLOSEGAME 0x02ff #define CLIENT_CLOSEGAME2 0x1fff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_closegame; /******************************************************/ /******************************************************/ #define CLIENT_LEAVECHANNEL 0x10ff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_leavechannel; /******************************************************/ /* packets 0x60ff - 0x64ff moved to anongame_protocal.h [Omega] */ /******************************************************/ /* FF 32 2A 00 1A 29 25 72 77 C3 3C 25 6B 4D 7A A4 .2*..)%rw.<%kMz. 3B 92 38 D5 01 F4 A5 6B 28 32 29 43 68 61 6C 6C ;.8....k(2)Chall 65 6E 67 65 72 2E 73 63 6D 00 enger.scm. FF 32 2C 00 21 F8 16 2D 99 D9 BC A4 A6 5C BA 60 .2,.!..-.....\.` 71 DE 6D 64 6F BC A5 03 28 34 29 44 69 72 65 20 q.mdo...(4)Dire 53 74 72 61 69 74 73 2E 73 63 6D 00 Straits.scm. */ #define CLIENT_MAPAUTHREQ1 0x32ff typedef struct { t_bnet_header h; bn_int file_checksum[5]; /* mapfile */ } PACKED_ATTR() t_client_mapauthreq1; /******************************************************/ /******************************************************/ /* FF 32 08 00 01 00 00 00 .2...... */ #define SERVER_MAPAUTHREPLY1 0x32ff typedef struct { t_bnet_header h; bn_int response; } PACKED_ATTR() t_server_mapauthreply1; #define SERVER_MAPAUTHREPLY1_NO 0x00000000 #define SERVER_MAPAUTHREPLY1_OK 0x00000001 #define SERVER_MAPAUTHREPLY1_LADDER_OK 0x00000002 /******************************************************/ /******************************************************/ /* From BW1.08alpha: FF 3C 31 00 7A 20 01 00 3B B7 C6 27 0D 61 C3 79 .<1.z ..;..'.a.y 79 BE 24 5E 9C 07 05 7D 0B 6A A0 78 28 35 29 4A y.$^...}.j.x(5)J 65 77 65 6C 65 64 20 52 69 76 65 72 2E 73 63 6D eweled River.scm 00 . */ #define CLIENT_MAPAUTHREQ2 0x3cff typedef struct { t_bnet_header h; bn_int unknown; bn_int file_hash[5]; /* mapfile */ } PACKED_ATTR() t_client_mapauthreq2; /******************************************************/ /******************************************************/ /* assuming it looks like the REPLY1 packet.... */ #define SERVER_MAPAUTHREPLY2 0x3cff typedef struct { t_bnet_header h; bn_int response; } PACKED_ATTR() t_server_mapauthreply2; #define SERVER_MAPAUTHREPLY2_NO 0x00000000 /* FIXME: these values are guesses */ #define SERVER_MAPAUTHREPLY2_OK 0x00000001 #define SERVER_MAPAUTHREPLY2_LADDER_OK 0x00000002 /******************************************************/ /******************************************************/ /* FF 15 14 00 36 38 58 49 52 41 54 53 00 00 00 00 ....68XIRATS.... AF 14 55 36 ..U6 */ #define CLIENT_ADREQ 0x15ff typedef struct { t_bnet_header h; bn_int archtag; bn_int clienttag; bn_int prev_adid; /* zero if first request */ bn_int ticks; /* Unix-style time in seconds */ } PACKED_ATTR() t_client_adreq; /******************************************************/ /******************************************************/ /* Sent in response to a CLIENT_ADREQ to tell the client which banner to display next FF 15 3A 00 72 00 00 00 2E 70 63 78 50 15 7A 1C ..:.r....pcxP.z. CE 0F BD 01 61 64 30 30 30 30 37 32 2E 70 63 78 ....ad000072.pcx 00 68 74 74 70 3A 2F 2F 77 77 77 2E 62 6C 69 7A .http://www.bliz 7A 61 72 64 2E 63 6F 6D 2F 00 zard.com/. FF 15 3C 00 C3 00 00 00 2E 73 6D 6B 00 9B 36 A6 ..<......smk..6. 8F 5B BE 01 61 64 30 30 30 30 63 33 2E 73 6D 6B .[..ad0000c3.smk 00 68 74 74 70 3A 2F 2F 77 77 77 2E 66 61 74 68 .http://www.fath 65 72 68 6F 6F 64 2E 6F 72 67 2F 00 erhood.org/. FF 15 36 00 2B 51 02 00 ..6.+Q.. 2E 70 63 78 00 00 00 00 58 01 B2 00 61 64 30 32 .pcx....X...ad02 35 31 32 62 2E 70 63 78 00 68 74 74 70 3A 2F 2F 512b.pcx.http:// 77 77 77 2E 66 73 67 73 2E 63 6F 6D 2F 00 www.fsgs.com/. */ #define SERVER_ADREPLY 0x15ff typedef struct { t_bnet_header h; bn_int adid; bn_int extensiontag; /* unlike other tags, this one is "forward" */ bn_long timestamp; /* file modification time? */ /* filename */ /* link URL */ } PACKED_ATTR() t_server_adreply; /******************************************************/ /******************************************************/ #define CLIENT_ADACK 0x21ff /* Sent after client has displayed the banner 0000: FF 21 36 00 36 38 58 49 52 41 54 53 72 00 00 00 .!6.68XIRATSr... 0010: 61 64 30 30 30 30 37 32 2E 70 63 78 00 68 74 74 ad000072.pcx.htt 0020: 70 3A 2F 2F 77 77 77 2E 62 6C 69 7A 7A 61 72 64 p://www.blizzard 0030: 2E 63 6F 6D 2F 00 .com/. 0000: FF 21 38 00 36 38 58 49 4C 54 52 44 C3 00 00 00 .!8.68XILTRD.... 0010: 61 64 30 30 30 30 63 33 2E 73 6D 6B 00 68 74 74 ad0000c3.smk.htt 0020: 70 3A 2F 2F 77 77 77 2E 66 61 74 68 65 72 68 6F p://www.fatherho 0030: 6F 64 2E 6F 72 67 2F 00 od.org/. */ typedef struct { t_bnet_header h; bn_int archtag; bn_int clienttag; bn_int adid; /* adfile */ /* adlink */ } PACKED_ATTR() t_client_adack; /******************************************************/ /******************************************************/ /* Sent if the user clicks on the adbanner */ #define CLIENT_ADCLICK 0x16ff typedef struct { t_bnet_header h; bn_int adid; bn_int unknown1; } PACKED_ATTR() t_client_adclick; /******************************************************/ /******************************************************/ /* first seen in Diablo II? */ /* FF 41 08 00 01 00 00 00 .A...... */ #define CLIENT_ADCLICK2 0x41ff typedef struct { t_bnet_header h; bn_int adid; } PACKED_ATTR() t_client_adclick2; /******************************************************/ /******************************************************/ /* first seen in Diablo II? */ /* FF 41 2C 00 0B 20 00 00 68 74 .qT....A,.. ..ht 74 70 3A 2F 2F 77 77 77 2E 62 6C 69 7A 7A 61 72 tp://www.blizzar 64 2E 63 6F 6D 2F 64 69 61 62 6C 6F 32 65 78 70 d.com/diablo2exp 2F 00 /. */ #define SERVER_ADCLICKREPLY2 0x41ff typedef struct { t_bnet_header h; bn_int adid; /* link URL */ } PACKED_ATTR() t_server_adclickreply2; /******************************************************/ /******************************************************/ /* seen in SC107a */ #define CLIENT_READMEMORY 0x17ff typedef struct { t_bnet_header h; bn_int request_id; /* Memory */ } PACKED_ATTR() t_client_readmemory; #define SERVER_READMEMORY 0x17ff typedef struct { t_bnet_header h; bn_int request_id; bn_int address; bn_int length; } PACKED_ATTR() t_server_readmemory; /******************************************************/ /******************************************************/ /* seen in SC107a */ #define CLIENT_UNKNOWN_24 0x24ff typedef struct { t_bnet_header h; /* FIXME: what is in here... is there a cooresponding server packet? */ } PACKED_ATTR() t_client_unknown_24; /******************************************************/ /******************************************************/ /* FF 2E 18 00 4E 42 32 57 01 00 00 00 00 00 00 00 ....NB2W........ 00 00 00 00 0A 00 00 00 ........ */ #define CLIENT_LADDERREQ 0x2eff typedef struct { t_bnet_header h; bn_int clienttag; bn_int id; /* (AKA ladder type) 1==standard, 3==ironman */ bn_int type; /* (AKA ladder sort) */ bn_int startplace; /* start listing on this entries */ bn_int count; /* how many entries to list */ } PACKED_ATTR() t_client_ladderreq; #define CLIENT_LADDERREQ_ID_STANDARD 0x00000001 #define CLIENT_LADDERREQ_ID_IRONMAN 0x00000003 #define CLIENT_LADDERREQ_TYPE_HIGHESTRATED 0x00000000 #define CLIENT_LADDERREQ_TYPE_MOSTWINS 0x00000002 #define CLIENT_LADDERREQ_TYPE_MOSTGAMES 0x00000003 /******************************************************/ /******************************************************/ /* Sent in repsonse to CLIENT_LADDERREQ FF 2E AB 03 52 41 54 53 01 00 00 00 00 00 00 00 ....RATS........ 00 00 00 00 0A 00 00 00 27 00 00 00 01 00 00 00 ........'....... 01 00 00 00 74 06 00 00 00 00 00 00 27 00 00 00 ....t.......'... 01 00 00 00 01 00 00 00 74 06 00 00 00 00 00 00 ........t....... 00 00 00 00 00 00 00 00 FF FF FF FF 74 06 00 00 ............t... 00 00 00 00 00 00 00 00 12 27 4E FF 2D DD BE 01 .........'N.-... 12 27 4E FF 2D DD BE 01 6E 65 6D 62 69 3A 29 6B .'N.-...nembi:)k 69 6C 6C 65 72 00 1D 00 00 00 03 00 00 00 00 00 iller........... 00 00 67 06 00 00 00 00 00 00 1D 00 00 00 03 00 ..g............. 00 00 00 00 00 00 67 06 00 00 00 00 00 00 01 00 ......g......... 00 00 00 00 00 00 FF FF FF FF 67 06 00 00 01 00 ..........g..... 00 00 00 00 00 00 D8 5B 9E D0 32 DD BE 01 D8 5B .......[..2....[ 9E D0 32 DD BE 01 53 4B 45 4C 54 4F 4E 00 1F 00 ..2...SKELTON... 00 00 03 00 00 00 00 00 00 00 EF 05 00 00 00 00 ................ 00 00 1F 00 00 00 03 00 00 00 00 00 00 00 EF 05 ................ 00 00 00 00 00 00 02 00 00 00 00 00 00 00 FF FF ................ FF FF EF 05 00 00 02 00 00 00 00 00 00 00 62 26 ..............b& 55 0C 51 DC BE 01 62 26 55 0C 51 DC BE 01 7A 69 U.Q...b&U.Q...zi 7A 69 62 65 5E 2E 7E 00 19 00 00 00 02 00 00 00 zibe^.~......... 00 00 00 00 FC 05 00 00 00 00 00 00 18 00 00 00 ................ 02 00 00 00 00 00 00 00 EE 05 00 00 00 00 00 00 ................ 03 00 00 00 00 00 00 00 FF FF FF FF FC 05 00 00 ................ 03 00 00 00 00 00 00 00 A0 25 4F 31 90 DE BE 01 .........%O1.... 8C F1 7F 9F 66 DD BE 01 59 4F 4F 4A 49 4E 27 53 ....f...YOOJIN'S 00 1D 00 00 00 02 00 00 00 00 00 00 00 EC 05 00 ................ 00 00 00 00 00 1D 00 00 00 02 00 00 00 00 00 00 ................ 00 EC 05 00 00 00 00 00 00 04 00 00 00 00 00 00 ................ 00 FF FF FF FF EC 05 00 00 03 00 00 00 00 00 00 ................ 00 F4 58 78 82 2F D7 BE 01 F4 58 78 82 2F D7 BE ..Xx./....Xx./.. 01 3D 7B 5F 7C 5F 7D 3D 00 1A 00 00 00 00 00 00 .={_|_}=........ 00 00 00 00 00 E2 05 00 00 00 00 00 00 1A 00 00 ................ 00 00 00 00 00 00 00 00 00 E2 05 00 00 00 00 00 ................ 00 05 00 00 00 00 00 00 00 FF FF FF FF E2 05 00 ................ 00 05 00 00 00 00 00 00 00 F2 DC 0D 1F 6C DD BE .............l.. 01 F2 DC 0D 1F 6C DD BE 01 5B 53 50 41 43 45 5D .....l...[SPACE] 2D 31 2D 54 2E 53 2E 4A 00 15 00 00 00 02 00 00 -1-T.S.J........ 00 01 00 00 00 E0 05 00 00 00 00 00 00 15 00 00 ................ 00 02 00 00 00 01 00 00 00 E0 05 00 00 00 00 00 ................ 00 06 00 00 00 00 00 00 00 FF FF FF FF E0 05 00 ................ 00 06 00 00 00 00 00 00 00 7A C9 F6 6E 0B DE BE .........z..n... 01 7A C9 F6 6E 0B DE BE 01 5B 46 65 77 5D 2D 44 .z..n....[Few]-D 2E 73 00 23 00 00 00 00 00 00 00 00 00 00 00 DF .s.#............ 05 00 00 00 00 00 00 23 00 00 00 00 00 00 00 00 .......#........ 00 00 00 DF 05 00 00 00 00 00 00 07 00 00 00 00 ................ 00 00 00 FF FF FF FF E2 05 00 00 04 00 00 00 00 ................ 00 00 00 6E D9 DC 3A E2 DA BE 01 6E D9 DC 3A E2 ...n..:....n..:. DA BE 01 5B 4C 2E 73 5D 2D 43 6F 6F 6C 00 1F 00 ...[L.s]-Cool... 00 00 07 00 00 00 02 00 00 00 DD 05 00 00 00 00 ................ 00 00 1F 00 00 00 07 00 00 00 02 00 00 00 DD 05 ................ 00 00 00 00 00 00 08 00 00 00 00 00 00 00 FF FF ................ FF FF 07 06 00 00 02 00 00 00 00 00 00 00 B6 CE ................ B1 D8 7A D8 BE 01 B6 CE B1 D8 7A D8 BE 01 52 6F ..z.......z...Ro 60 4C 65 58 7E 50 72 4F 27 5A 65 4E 00 18 00 00 `LeX~PrO'ZeN.... 00 04 00 00 00 00 00 00 00 DD 05 00 00 00 00 00 ................ 00 18 00 00 00 04 00 00 00 00 00 00 00 DD 05 00 ................ 00 00 00 00 00 09 00 00 00 00 00 00 00 FF FF FF ................ FF DD 05 00 00 04 00 00 00 00 00 00 00 50 76 4C .............PvL F7 AD D7 BE 01 50 76 4C F7 AD D7 BE 01 48 61 6E .....PvL.....Han 5F 65 53 54 68 65 72 2E 27 27 00 _eSTher.''. */ #define SERVER_LADDERREPLY 0x2eff typedef struct { t_bnet_header h; bn_int clienttag; bn_int id; /* (AKA ladder type) 1==standard, 3==ironman */ bn_int type; /* (AKA ladder sort) */ bn_int startplace; /* start listing on this entries */ bn_int count; /* how many entries to list */ /* ladder entry */ /* player name */ } PACKED_ATTR() t_server_ladderreply; #define CLIENT_LADDERREPLY_ID_STANDARD 0x00000001 #define CLIENT_LADDERREPLY_ID_IRONMAN 0x00000003 typedef struct { bn_int wins; bn_int loss; bn_int disconnect; bn_int rating; bn_int rank; } PACKED_ATTR() t_ladder_data; typedef struct { t_ladder_data current; t_ladder_data active; bn_int ttest[6]; /* 00 00 00 00 00 00 00 00 FF FF FF FF 74 06 00 00 00 00 00 00 00 00 00 00 */ bn_long lastgame_current; /* timestamp */ bn_long lastgame_active; /* timestamp */ } PACKED_ATTR() t_ladder_entry; /******************************************************/ /******************************************************/ /* FF 25 08 00 EA 7F DB 02 .%...... */ #define CLIENT_ECHOREPLY 0x25ff typedef struct { t_bnet_header h; bn_int ticks; } PACKED_ATTR() t_client_echoreply; /******************************************************/ /******************************************************/ #define SERVER_ECHOREQ 0x25ff typedef struct { t_bnet_header h; bn_int ticks; } PACKED_ATTR() t_server_echoreq; /******************************************************/ /******************************************************/ /* What I'm calling the ping happens every 90 seconds during gameplay. I'm not exactly sure what it is, but it didn't hurt that we didn't respond up to now... I went ahead and coded a response in. This packet is sent at other times as well, even before login. This is probably a keepalive packet and the UDP is sent to make sure the UDP entry on the NAT gateway remains valid. Prolix calls these null packets and says they are sent every 60 seconds. This seems to be associated with a UDP packet 7 from the client: 7: cli class=bnet[0x01] type=CLIENT_PINGREQ[0x00ff] length=4 0000: FF 00 04 00 .... 5: clt prot=udp[0x07] from=128.123.62.23:6112 to=128.123.62.23:6112 length=8 0000: 07 00 00 00 2E 95 9D 00 ........ 7: srv class=bnet[0x01] type=SERVER_PINGREPLY[0x00ff] length=4 0000: FF 00 04 00 .... FF 00 04 00 .... */ #define CLIENT_PINGREQ 0x00ff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_pingreq; /******************************************************/ /******************************************************/ /* FF 00 04 00 .... */ #define SERVER_PINGREPLY 0x00ff typedef struct { t_bnet_header h; } PACKED_ATTR() t_server_pingreply; /******************************************************/ /******************************************************/ /* FF 2C 3D 02 00 00 00 00 08 00 00 00 03 00 00 00 .,=............. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 52 6F 73 73 ............Ross 5F 43 4D 00 00 00 00 00 00 00 00 4F 6E 20 6D 61 _CM........On ma 70 20 22 43 68 61 6C 6C 65 6E 67 65 72 22 3A 0A p "Challenger":. 00 52 6F 73 73 5F 43 4D 20 77 61 73 20 50 72 6F .Ross_CM was Pro 74 6F 73 73 20 61 6E 64 20 70 6C 61 79 65 64 20 toss and played 66 6F 72 20 33 31 20 6D 69 6E 75 74 65 73 0A 0A for 31 minutes.. 20 20 4F 76 65 72 61 6C 6C 20 53 63 6F 72 65 20 Overall Score 32 38 31 30 35 0A 20 20 20 20 20 20 20 20 20 31 28105. 1 31 37 30 30 20 66 6F 72 20 55 6E 69 74 73 0A 20 1700 for Units. 20 20 20 20 20 20 20 20 20 31 34 37 35 20 66 6F 1475 fo 72 20 53 74 72 75 63 74 75 72 65 73 0A 20 20 20 r Structures. 20 20 20 20 20 20 31 34 39 33 30 20 66 6F 72 20 14930 for 52 65 73 6F 75 72 63 65 73 0A 0A 20 20 55 6E 69 Resources.. Uni 74 73 20 53 63 6F 72 65 20 31 31 37 30 30 0A 20 ts Score 11700. 20 20 20 20 20 20 20 20 20 20 20 37 30 20 55 6E 70 Pn */ #define CLIENT_GAME_REPORT 0x2cff typedef struct { t_bnet_header h; bn_int unknown1; /* 0x00000000 */ bn_int count; /* (number of player slots (will be 8 for now) */ /* results... */ /* names... */ /* report header */ /* report body */ } PACKED_ATTR() t_client_game_report; typedef struct { bn_int result; } PACKED_ATTR() t_client_game_report_result; #define CLIENT_GAME_REPORT_RESULT_PLAYING 0x00000000 #define CLIENT_GAME_REPORT_RESULT_WIN 0x00000001 #define CLIENT_GAME_REPORT_RESULT_LOSS 0x00000002 #define CLIENT_GAME_REPORT_RESULT_DRAW 0x00000003 #define CLIENT_GAME_REPORT_RESULT_DISCONNECT 0x00000004 #define CLIENT_GAME_REPORT_RESULT_OBSERVER 0x00000005 /******************************************************/ /******************************************************/ /* War Craft II BNE (original): FF 22 1B 00 4E 42 32 57 4B 00 00 00 4C 61 64 64 ."..NB2WK...Ladd 65 72 20 31 20 6F 6E 20 31 00 00 er 1 on 1.. */ #define CLIENT_JOIN_GAME 0x22ff typedef struct { t_bnet_header h; bn_int clienttag; bn_int versiontag; /* game name */ /* game password */ } PACKED_ATTR() t_client_join_game; /******************************************************/ /******************************************************/ /* FF 27 84 00 01 00 00 00 04 00 00 00 52 6F 73 73 .'..........Ross 5F 43 4D 00 70 72 6F 66 69 6C 65 5C 73 65 78 00 _CM.profile\sex. 70 72 6F 66 69 6C 65 5C 61 67 65 00 70 72 6F 66 profile\age.prof 69 6C 65 5C 6C 6F 63 61 74 69 6F 6E 00 70 72 6F ile\location.pro 66 69 6C 65 5C 64 65 73 63 72 69 70 74 69 6F 6E file\description 00 61 73 64 66 00 61 73 66 00 61 73 64 66 00 61 .asdf.asf.asdf.a 73 64 66 61 73 64 66 61 73 64 66 61 73 64 66 0D sdfasdfasdfasdf. 0A 61 73 64 0D 0A 66 61 73 64 0D 0A 66 61 73 64 .asd..fasd..fasd 66 0D 0A 00 f... */ #define CLIENT_STATSUPDATE 0x27ff typedef struct { t_bnet_header h; bn_int name_count; bn_int key_count; /* names... */ /* values... */ } PACKED_ATTR() t_client_statsupdate; /******************************************************/ /******************************************************/ #define CLIENT_REALMJOINREQ_109 0x3eff typedef struct { t_bnet_header h; bn_int seqno; bn_int seqnohash[5]; /* Realm Name */ } PACKED_ATTR() t_client_realmjoinreq_109; /******************************************************/ /******************************************************/ #define SERVER_REALMJOINREPLY_109 0x3eff typedef struct { t_bnet_header h; bn_int seqno; bn_int u1; bn_int bncs_addr1; bn_int sessionnum; bn_int addr; bn_short port; bn_short u3; bn_int sessionkey; /* zero */ bn_int u5; bn_int u6; bn_int clienttag; bn_int versionid; bn_int bncs_addr2; bn_int u7; /* zero */ bn_int secret_hash[5]; /* account name */ } PACKED_ATTR() t_server_realmjoinreply_109; /******************************************************/ #define CLIENT_SEARCH_LAN_GAMES 0x2ff7 typedef struct { t_bnet_header h; bn_byte game_tag[4]; /* 3WAR */ bn_int unknown1; bn_int unknown2; } PACKED_ATTR() t_client_search_lan_games; #define CLIENT_CHANGECLIENT 0x5cff typedef struct { t_bnet_header h; bn_int clienttag; } t_client_changeclient; #define CLIENT_SETEMAILREPLY 0x59ff typedef struct { t_bnet_header h; /* email address */ } PACKED_ATTR() t_client_setemailreply; #define SERVER_SETEMAILREQ 0x59ff /* send this packet to client before login ok packet will cause client to enter input email screen */ typedef struct { t_bnet_header h; } PACKED_ATTR() t_server_setemailreq; #define CLIENT_GETPASSWORDREQ 0x5aff typedef struct { t_bnet_header h; /* account name */ /* email address */ } PACKED_ATTR() t_client_getpasswordreq; #define CLIENT_CHANGEEMAILREQ 0x5bff typedef struct { t_bnet_header h; /* account name */ /* old email address */ /* new email address */ } PACKED_ATTR() t_client_changeemailreq; #define CLIENT_MOTDREQ 0x46ff typedef struct { t_bnet_header h; bn_int last_news_time; /* date of the last news item the client has */ } PACKED_ATTR() t_client_motdreq; /* this packet is sent right after cdkey and version auth reply success and crashdump exist 0x0000: ff 5d 14 00 01 01 00 27 00 0a 01 05 00 00 c0 00 .].....'........ 0x0010: 00 00 00 00 .... */ #define CLIENT_CRASHDUMP 0x5dff typedef struct { t_bnet_header h; /* crashdump file data */ /* contains data like client version, exception code, code address */ } PACKED_ATTR() t_client_crashdump; #define CLIENT_CLANINFOREQ 0x82ff typedef struct { t_bnet_header h; bn_int count; bn_int clantag; /* player name */ } PACKED_ATTR() t_client_claninforeq; #define SERVER_CLANINFOREPLY 0x82ff typedef struct { t_bnet_header h; bn_int count; bn_byte fail; /* string - clan name */ /* bn_byte - clan rank */ /* bn_int - join time */ } PACKED_ATTR() t_server_claninforeply; #define CLIENT_ARRANGEDTEAM_ACCEPT_INVITE 0xfdff typedef struct { t_bnet_header h; } PACKED_ATTR() t_client_arrangedteam_accept_invite; /* clan handling */ #define SERVER_CLAN_MEMBER_CHIEFTAIN 0x04 #define SERVER_CLAN_MEMBER_SHAMAN 0x03 #define SERVER_CLAN_MEMBER_GRUNT 0x02 #define SERVER_CLAN_MEMBER_PEON 0x01 #define SERVER_CLAN_MEMBER_NEW 0x00 #define SERVER_CLAN_MEMBER_OFFLINE 0x00 #define SERVER_CLAN_MEMBER_ONLINE 0x01 #define SERVER_CLAN_MEMBER_CHANNEL 0x02 #define SERVER_CLAN_MEMBER_GAME 0x03 #define SERVER_CLAN_MEMBER_PRIVATE_GAME 0x04 /*Paquet #267 0x0000 FF 75 0A 00 00 00 42 54-54 04 u....BTT. */ /* 300: recv class=bnet[0x02] type=unknown[0x70ff] length=12 0000: FF 70 0C 00 01 00 00 00 00 64 73 66 .p.......dsf 300: send class=bnet[0x02] type=unknown[0x70ff] length=56 0000: FF 70 38 00 01 00 00 00 00 09 44 4A 50 32 00 44 .p8.......DJP2.D 0010: 4A 50 33 00 44 4A 50 34 00 44 4A 50 35 00 44 4A JP3.DJP4.DJP5.DJ 0020: 50 36 00 44 4A 50 37 00 44 4A 50 38 00 44 4A 50 P6.DJP7.DJP8.DJP 0030: 39 00 44 4A 50 31 30 00 9.DJP10. 300: recv class=bnet[0x02] type=CLIENT_FRIENDINFOREQ[0x66ff] length=5 0000: FF 66 05 00 01 .f... 300: send class=bnet[0x02] type=unknown[0x66ff] length=12 0000: FF 66 0C 00 01 00 00 00 00 00 00 00 .f.......... 300: recv class=bnet[0x02] type=unknown[0x71ff] length=64 0000: FF 71 40 00 01 00 00 00 74 65 73 74 00 00 64 73 .q@.....test..ds 0010: 66 09 44 4A 50 32 00 44 4A 50 33 00 44 4A 50 34 f.DJP2.DJP3.DJP4 0020: 00 44 4A 50 35 00 44 4A 50 36 00 44 4A 50 37 00 .DJP5.DJP6.DJP7. 0030: 44 4A 50 38 00 44 4A 50 39 00 44 4A 50 31 30 00 DJP8.DJP9.DJP10. 300: send class=bnet[0x02] type=unknown[0x71ff] length=10 0000: FF 71 0A 00 01 00 00 00 00 00 .q........ 300: send class=bnet[0x02] type=unknown[0x75ff] length=12 0000: FF 75 0C 00 00 00 00 00 64 73 66 00 .u......dsf. 300: recv class=bnet[0x02] type=CLIENT_FRIENDINFOREQ[0x66ff] length=5 0000: FF 66 05 00 02 .f... 300: send class=bnet[0x02] type=unknown[0x66ff] length=12 0000: FF 66 0C 00 02 00 00 00 00 00 00 00 .f.......... */ #define CLIENT_CLAN_CREATEREQ 0x70ff typedef struct{ t_bnet_header h; bn_int count; bn_int clantag; } PACKED_ATTR() t_client_clan_createreq; #define SERVER_CLAN_CREATEREPLY 0x70ff typedef struct{ t_bnet_header h; bn_int count; bn_byte check_result; bn_byte friend_count; /* player name in chan or mutual char player_name[sizeof (friend_name)]; */ } PACKED_ATTR() t_server_clan_createreply; #define SERVER_CLAN_CREATEREPLY_CHECK_OK 0x00 #define SERVER_CLAN_CREATEREPLY_CHECK_ALLREADY_IN_USE 0x01 #define SERVER_CLAN_CREATEREPLY_CHECK_TIME_LIMIT 0x02 #define SERVER_CLAN_CREATEREPLY_CHECK_EXCEPTION 0x04 #define SERVER_CLAN_CREATEREPLY_CHECK_INVALID_CLAN_TAG 0x0a /* 3852: recv class=bnet[0x02] type=unknown[0x71ff] length=70 0000: FF 71 46 00 01 00 00 00 53 75 62 57 61 72 5A 6F .qF.....SubWarZo 0010: 6E 65 00 00 5A 57 53 09 44 4A 50 32 00 44 4A 50 ne..ZWS.DJP2.DJP 0020: 33 00 44 4A 50 34 00 44 4A 50 35 00 44 4A 50 36 3.DJP4.DJP5.DJP6 0030: 00 44 4A 50 37 00 44 4A 50 38 00 44 4A 50 31 30 .DJP7.DJP8.DJP10 0040: 00 44 4A 50 39 00 .DJP9. */ #define CLIENT_CLAN_CREATEINVITEREQ 0x71ff typedef struct{ t_bnet_header h; bn_int count; /* Clan Name (\0 terminated string) bn_int clantag; bn_byte friend_count; //Number of friend selected Name of friend (\0 terminated string) */ } PACKED_ATTR() t_client_clan_createinvitereq; /*3756: send class=bnet[0x02] type=unknown[0x71ff] length=14 0000: FF 71 0E 00 02 00 00 00 05 44 4A 50 32 00 .q.......DJP2. <- Unable to receive invitation ( PG search, already in a clan, etc... )*/ /*3756: Paquet #266 0x0000 FF 71 0A 00 05 00 00 00-00 00 q........ <- Clan invitation = Sucessfully done */ #define SERVER_CLAN_CREATEINVITEREPLY 0x71ff typedef struct{ t_bnet_header h; bn_int count; bn_byte status; /* 0x05 = Cannot contact(not in channel screen) or already in clan | 0x04 = Decline | 0x00 = OK :) Name of failed member(\0 terminated string) */ } PACKED_ATTR() t_server_clan_createinvitereply; #define SERVER_CLAN_CREATEINVITEREQ 0x72ff typedef struct{ t_bnet_header h; bn_int count; bn_int clantag; /* Clan Name (\0 terminated string) Clan Creator (\0 terminated string) bn_byte friend_count; //Number of friend selected Name of friend (\0 terminated string) */ } PACKED_ATTR() t_server_clan_createinvitereq; #define CLIENT_CLAN_CREATEINVITEREPLY 0x72ff typedef struct{ t_bnet_header h; bn_int count; bn_int clantag; /* Clan Creator (\0 terminated string) bn_byte reply */ /* 0x04--decline 0x05--Cannot contact(not in channel screen) or already in clan 0x06--accept*/ } PACKED_ATTR() t_client_clan_createinvitereply; /* 3876: recv class=bnet[0x02] type=unknown[0x73ff] length=8 0000: FF 73 08 00 01 00 00 00 .s...... */ #define CLIENT_CLAN_DISBANDREQ 0x73ff typedef struct{ t_bnet_header h; bn_int count; } PACKED_ATTR() t_client_clan_disbandreq; #define SERVER_CLAN_DISBANDREPLY 0x73ff typedef struct{ t_bnet_header h; bn_int count; bn_byte result; /* 0-- Success 1-- Exception raised 2-- Clan exists less than 1 week, cannot remove */ } PACKED_ATTR() t_server_clan_disbandreply; #define SERVER_CLAN_DISBANDREPLY_RESULT_OK 0x0 #define SERVER_CLAN_DISBANDREPLY_RESULT_EXCEPTION 0x1 #define SERVER_CLAN_DISBANDREPLY_RESULT_FAILED 0x2 #define CLIENT_CLAN_MEMBERNEWCHIEFREQ 0x74ff typedef struct{ t_bnet_header h; bn_int count; /*Player_Name deleted(\0 terminated) */ } PACKED_ATTR() t_client_clan_membernewchiefreq; #define SERVER_CLAN_MEMBERNEWCHIEFREPLY 0x74ff typedef struct{ t_bnet_header h; bn_int count; bn_byte result; /* 0-successful 1-failed */ } PACKED_ATTR() t_server_clan_membernewchiefreply; #define SERVER_CLAN_MEMBERNEWCHIEFREPLY_SUCCESS 0x00 #define SERVER_CLAN_MEMBERNEWCHIEFREPLY_FAILED 0x01 #define SERVER_CLAN_CLANACK 0x75ff typedef struct{ t_bnet_header h; bn_byte unknow1; /* 0x00 */ bn_int clantag; bn_byte status; /* member status */ } PACKED_ATTR() t_server_clan_clanack; #define SERVER_CLANQUITNOTIFY 0x76ff typedef struct{ t_bnet_header h; bn_byte status; } PACKED_ATTR() t_server_clanquitnotify; #define SERVER_CLANQUITNOTIFY_STATUS_REMOVED_FROM_CLAN 0x01 /* 3876: recv class=bnet[0x02] type=unknown[0x77ff] length=13 0000: FF 77 0D 00 01 00 00 00 44 4A 50 31 00 .w......DJP1. */ #define CLIENT_CLAN_INVITEREQ 0x77ff typedef struct{ t_bnet_header h; bn_int count; /*Player_Name invited */ } PACKED_ATTR() t_client_clan_invitereq; #define SERVER_CLAN_INVITEREPLY 0x77ff typedef struct{ t_bnet_header h; bn_int count; bn_byte result; /* 0x04--decline 0x05--Cannot contact(not in channel screen) or already in clan */ } PACKED_ATTR() t_server_clan_invitereply; #define CLIENT_CLANMEMBER_REMOVE_REQ 0x78ff typedef struct{ t_bnet_header h; bn_int count; /*Player_Name deleted(\0 terminated) */ } PACKED_ATTR() t_client_clanmember_remove_req; #define SERVER_CLANMEMBER_REMOVE_REPLY 0x78ff typedef struct{ t_bnet_header h; bn_int count; bn_byte result; /* 0-successful 1-failed */ } PACKED_ATTR() t_server_clanmember_remove_reply; #define SERVER_CLANMEMBER_REMOVE_SUCCESS 0x00 #define SERVER_CLANMEMBER_REMOVE_FAILED 0x01 #define SERVER_CLAN_INVITEREQ 0x79ff typedef struct{ t_bnet_header h; bn_int count; bn_int clantag; /*Clan_Name (\0 terminated) Player_Name inviter (\0 terminated) */ } PACKED_ATTR() t_server_clan_invitereq; #define CLIENT_CLAN_INVITEREPLY 0x79ff typedef struct{ t_bnet_header h; bn_int count; bn_int clantag; /*Player_Name inviter (\0 terminated) bn_byte reply */ } PACKED_ATTR() t_client_clan_invitereply; /* clan codes according to bnetdocs 0x00: Success 0x01: In use 0x02: Too soon 0x03: Not enough members 0x04: Invitation was declined 0x05: Decline 0x06: Accept 0x07: Not authorized 0x08: User not found 0x09: Clan is full 0x0A: Bad tag 0x0B: Bad name 0x0C: User not found in that clan */ #define CLAN_RESPONSE_SUCCESS 0x00 #define CLAN_RESPONSE_FAIL 0x01 #define CLAN_RESPONSE_TOO_SOON 0x02 #define CLAN_RESPONSE_TOO_SMALL 0x03 #define CLAN_RESPONSE_DECLINED 0x04 #define CLAN_RESPONSE_DECLINE 0x05 #define CLAN_RESPONSE_ACCEPT 0x06 #define CLAN_RESPONSE_NOT_AUTHORIZED 0x07 #define CLAN_RESPONSE_NOT_FOUND 0x08 #define CLAN_RESPONSE_CLAN_FULL 0x09 #define CLAN_RESPONSE_BAD_TAG 0x0a #define CLAN_RESPONSE_BAD_NAME 0x0b #define CLAN_RESPONSE_NOT_MEMBER 0x0c #define CLIENT_CLANMEMBER_RANKUPDATE_REQ 0x7aff typedef struct{ t_bnet_header h; bn_int count; /*Player_Name invited(\0 terminated) Player_Status(bn_byte: 1~4) */ } PACKED_ATTR() t_client_clanmember_rankupdate_req; #define SERVER_CLANMEMBER_RANKUPDATE_REPLY 0x7aff typedef struct{ t_bnet_header h; bn_int count; bn_byte result; /* 0-successful 1-failed */ } PACKED_ATTR() t_server_clanmember_rankupdate_reply; #define SERVER_CLANMEMBER_RANKUPDATE_SUCCESS 0x00 #define SERVER_CLANMEMBER_RANKUPDATE_FAILED 0x01 #define CLIENT_CLAN_MOTDCHG 0x7bff typedef struct{ t_bnet_header h; bn_int unknow1; /* Motd en string ^^ */ } PACKED_ATTR() t_client_clan_motdchg; #define SERVER_CLAN_MOTDREPLY_UNKNOW1 0x00000000 #define SERVER_CLAN_MOTDREPLY 0x7cff typedef struct{ t_bnet_header h; bn_int count; bn_int unknow1; /* 0x00000000 */ /* MOTD */ } PACKED_ATTR() t_server_clan_motdreply; #define CLIENT_CLAN_MOTDREQ 0x7cff typedef struct{ t_bnet_header h; bn_int count; } PACKED_ATTR() t_client_clan_motdreq; /* Paquet #52 0x0000 FF 7D 10 01 01 00 00 00-13 4D 79 73 74 69 2E 53 }.......Mysti.S 0x0010 77 5A 00 04 01 00 73 61-75 72 6F 6E 2E 73 77 7A wZ....sauron.swz 0x0020 00 03 00 00 73 69 6D 6F-6E 2E 53 77 5A 00 03 00 ....simon.SwZ... 0x0030 00 4E 65 6F 2D 56 61 67-72 61 6E 74 2E 73 77 7A .Neo-Vagrant.swz 0x0040 00 02 00 00 77 4D 7A 00-02 00 00 6B 61 74 6E 6F ....wMz....katno 0x0050 6D 61 64 2E 53 77 5A 00-02 00 00 47 7A 62 65 75 mad.SwZ....Gzbeu 0x0060 68 2E 53 77 5A 00 02 00-00 53 69 6C 76 65 72 2E h.SwZ....Silver. 0x0070 53 77 5A 00 02 00 00 4D-61 67 67 65 75 73 00 02 SwZ....Maggeus.. 0x0080 00 00 4F 6E 69 2D 4D 75-73 68 61 2E 53 77 5A 00 ..Oni-Musha.SwZ. 0x0090 02 00 00 4D 61 67 67 65-75 53 2E 53 77 5A 00 02 ...MaggeuS.SwZ.. 0x00A0 00 00 53 69 72 65 5F 4C-6F 75 70 00 02 00 00 6B ..Sire_Loup....k 0x00B0 69 6C 6C 69 62 6F 79 00-03 00 00 52 65 64 2E 44 illiboy....Red.D 0x00C0 72 61 4B 65 00 02 00 00-53 69 72 65 2E 53 77 5A raKe....Sire.SwZ 0x00D0 00 03 00 00 73 74 72 61-69 67 68 74 5F 63 6F 75 ....straight_cou 0x00E0 67 61 72 00 02 00 00 52-65 64 44 72 61 6B 65 2E gar....RedDrake. 0x00F0 53 77 5A 00 02 00 00 54-72 6F 6C 6C 6F 00 02 00 SwZ....Trollo... 0x0100 00 53 69 6C 76 65 72 62-65 61 72 64 00 00 00 00 .Silverbeard.... */ #define SERVER_CLANMEMBERLIST_REPLY 0x7dff typedef struct{ t_bnet_header h; bn_int count; bn_byte member_count; /* player repeat start * Name of player(\0 terminated string) * bn_byte CHIEFTAIN = 0x04 * SHAMANS = 0x03 * GRUNT = 0x02 * PEON = 0x01 * NEW_MEMBER = 0x00 <- can't be promoted/devoted * bn_byte online status * unknown(always \0) * repeat end */ } PACKED_ATTR() t_server_clanmemberlist_reply; /* Paquet #51 0x0000 FF 7D 08 00 01 00 00 00- }...... */ #define CLIENT_CLANMEMBERLIST_REQ 0x7dff typedef struct{ t_bnet_header h; bn_int count; } PACKED_ATTR() t_client_clanmemberlist_req; #define SERVER_CLANMEMBER_REMOVED_NOTIFY 0x7eff typedef struct{ t_bnet_header h; /* Player_Name deleted(\0 terminated) */ } PACKED_ATTR() t_server_clanmember_removed_notify; #define SERVER_CLANMEMBERUPDATE 0x7fff typedef struct{ t_bnet_header h; /* Player_Name invited(\0 terminated) * Player_Status(bn_byte: 1~4) * Player_Online(bn_short: 0x0/0x1) */ } PACKED_ATTR() t_server_clanmemberupdate; #define SERVER_MESSAGEBOX 0x19ff typedef struct { t_bnet_header h; bn_int style; /* Text */ /* Caption */ } PACKED_ATTR() t_server_messagebox; #define SERVER_MESSAGEBOX_OK 0x00000000 #define SERVER_MESSAGEBOX_OKCANCEL 0x00000001 #define SERVER_MESSAGEBOX_YESNO 0x00000004 #define SERVER_REQUIREDWORK 0x4Cff typedef struct { t_bnet_header h; /* FileName */ } PACKED_ATTR() t_server_requiredwork; #define CLIENT_EXTRAWORK 0x4bff typedef struct { t_bnet_header h; bn_short gametype; bn_short length; /* Data */ } PACKED_ATTR() t_client_extrawork; } #endif pvpgn-1.99.7.2.1+dfsg/src/common/bnethash.cpp000066400000000000000000000212271462640461400205630ustar00rootroot00000000000000/* * Copyright (C) 1999 Descolada (dyn1-tnt9-237.chicago.il.ameritech.net) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/bnethash.h" #include #include #include #include "common/bn_type.h" #include "common/introtate.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { typedef enum { do_blizzard_hash, do_sha1_hash } t_hash_variant; static void hash_init(t_hash * hash); static void do_hash(t_hash * hash, std::uint32_t * tmp); static void hash_set_16(std::uint32_t * dst, unsigned char const * src, unsigned int count, t_hash_variant hash_variant); static void hash_init(t_hash * hash) { (*hash)[0] = 0x67452301; (*hash)[1] = 0xefcdab89; (*hash)[2] = 0x98badcfe; (*hash)[3] = 0x10325476; (*hash)[4] = 0xc3d2e1f0; } static void do_hash(t_hash * hash, std::uint32_t * tmp, t_hash_variant hash_variant) { unsigned int i; std::uint32_t a, b, c, d, e, g; for (i = 0; i < 64; i++) if (hash_variant == do_blizzard_hash) tmp[i + 16] = ROTL32(1, tmp[i] ^ tmp[i + 8] ^ tmp[i + 2] ^ tmp[i + 13]); else tmp[i + 16] = ROTL32(tmp[i] ^ tmp[i + 8] ^ tmp[i + 2] ^ tmp[i + 13], 1); a = (*hash)[0]; b = (*hash)[1]; c = (*hash)[2]; d = (*hash)[3]; e = (*hash)[4]; for (i = 0; i < 20 * 1; i++) { g = tmp[i] + ROTL32(a, 5) + e + ((b & c) | (~b & d)) + 0x5a827999; e = d; d = c; c = ROTL32(b, 30); b = a; a = g; } for (; i < 20 * 2; i++) { g = (d ^ c ^ b) + e + ROTL32(g, 5) + tmp[i] + 0x6ed9eba1; e = d; d = c; c = ROTL32(b, 30); b = a; a = g; } for (; i < 20 * 3; i++) { g = tmp[i] + ROTL32(g, 5) + e + ((c & b) | (d & c) | (d & b)) - 0x70e44324; e = d; d = c; c = ROTL32(b, 30); b = a; a = g; } for (; i < 20 * 4; i++) { g = (d ^ c ^ b) + e + ROTL32(g, 5) + tmp[i] - 0x359d3e2a; e = d; d = c; c = ROTL32(b, 30); b = a; a = g; } (*hash)[0] += g; (*hash)[1] += b; (*hash)[2] += c; (*hash)[3] += d; (*hash)[4] += e; } /* * Fill 16 elements of the array of 32 bit values with the bytes from * dst up to count in little endian order. Fill left over space with * zeros. In case of SHA1 hash variant a binary 1 is appended after * the actual data. */ static void hash_set_16(std::uint32_t * dst, unsigned char const * src, unsigned int count, t_hash_variant hash_variant) { unsigned int i; unsigned int pos; for (pos = 0, i = 0; i < 16; i++) { dst[i] = 0; if (hash_variant == do_blizzard_hash) { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]); } else { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]) << 24; else if (pos == count) dst[i] |= ((std::uint32_t)0x80000000); } pos++; if (hash_variant == do_blizzard_hash) { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]) << 8; } else { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]) << 16; else if (pos == count) dst[i] |= ((std::uint32_t)0x800000); } pos++; if (hash_variant == do_blizzard_hash) { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]) << 16; } else { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]) << 8; else if (pos == count) dst[i] |= ((std::uint32_t)0x8000); } pos++; if (hash_variant == do_blizzard_hash) { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]) << 24; } else { if (pos < count) dst[i] |= ((std::uint32_t)src[pos]); else if (pos == count) dst[i] |= ((std::uint32_t)0x80); } pos++; } } extern int bnet_hash(t_hash * hashout, unsigned int size, void const * datain) { std::uint32_t tmp[64 + 16]; const unsigned char* data; unsigned int inc; if (!hashout) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashout"); return -1; } if (size > 0 && !datain) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL datain with size={}", size); return -1; } hash_init(hashout); data = (const unsigned char*)datain; while (size > 0) { if (size > 64) inc = 64; else inc = size; hash_set_16(tmp, data, inc, do_blizzard_hash); do_hash(hashout, tmp, do_blizzard_hash); data += inc; size -= inc; } return 0; } static void hash_set_length(std::uint32_t * dst, unsigned int size){ std::uint32_t size_high = 0; std::uint32_t size_low = 0; unsigned int counter; for (counter = 0; counter < size; counter++){ size_low += 8; if (size_low == 0) size_high++; } dst[14] |= ((size_high >> 24) & 0xff) << 24; dst[14] |= ((size_high >> 16) & 0xff) << 16; dst[14] |= ((size_high >> 8) & 0xff) << 8; dst[14] |= ((size_high)& 0xff); dst[15] |= ((size_low >> 24) & 0xff) << 24; dst[15] |= ((size_low >> 16) & 0xff) << 16; dst[15] |= ((size_low >> 8) & 0xff) << 8; dst[15] |= ((size_low)& 0xff); } extern int sha1_hash(t_hash * hashout, unsigned int size, void const * datain) { std::uint32_t tmp[64 + 16]; unsigned char const * data; unsigned int inc; unsigned int orgSize; if (!hashout) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashout"); return -1; } if (size > 0 && !datain) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL datain with size={}", size); return -1; } hash_init(hashout); orgSize = size; data = (const unsigned char*)datain; while (size > 0) { if (size >= 64) inc = 64; else inc = size; if (size >= 64) { hash_set_16(tmp, data, inc, do_sha1_hash); do_hash(hashout, tmp, do_sha1_hash); } else if (size > 55){ hash_set_16(tmp, data, inc, do_sha1_hash); do_hash(hashout, tmp, do_sha1_hash); // now use blizz variant as we only wanna fill in zeros hash_set_16(tmp, data, 0, do_blizzard_hash); hash_set_length(tmp, orgSize); do_hash(hashout, tmp, do_sha1_hash); } else{ hash_set_16(tmp, data, inc, do_sha1_hash); hash_set_length(tmp, orgSize); do_hash(hashout, tmp, do_sha1_hash); } data += inc; size -= inc; } return 0; } extern int little_endian_sha1_hash(t_hash * hashout, unsigned int size, void const * datain) { bn_int value; unsigned int i; sha1_hash(hashout, size, datain); for (i = 0; i < 5; i++) { bn_int_nset(&value, (*hashout)[i]); (*hashout)[i] = bn_int_get(value); } return 0; } extern int hash_eq(t_hash const h1, t_hash const h2) { unsigned int i; if (!h1 || !h2) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hash"); return -1; } for (i = 0; i < 5; i++) if (h1[i] != h2[i]) return 0; return 1; } extern char const * hash_get_str(t_hash const hash) { static char temp[8 * 5 + 1]; /* each of 5 ints to 8 chars + null */ unsigned int i; if (!hash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hash"); return NULL; } for (i = 0; i < 5; i++) std::sprintf(&temp[i * 8], "%08x", hash[i]); return temp; } extern char const * little_endian_hash_get_str(t_hash const hash) { bn_int value; t_hash be_hash; unsigned int i; for (i = 0; i < 5; i++) { bn_int_nset(&value, hash[i]); be_hash[i] = bn_int_get(value); } return hash_get_str(be_hash); } extern int hash_set_str(t_hash * hash, char const * str) { unsigned int i; if (!hash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hash pointer"); return -1; } if (!*hash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hash"); return -1; } if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL str"); return -1; } if (std::strlen(str) != 5 * 8) { eventlog(eventlog_level_error, __FUNCTION__, "got string with length {} (should be {})", std::strlen(str), 5 * 8); return -1; } for (i = 0; i < 5; i++) if (std::sscanf(&str[i * 8], "%8x", &(*hash)[i]) != 1) { eventlog(eventlog_level_error, __FUNCTION__, "got bad string"); return -1; } return 0; } } pvpgn-1.99.7.2.1+dfsg/src/common/bnethash.h000066400000000000000000000031371462640461400202300ustar00rootroot00000000000000/* * Copyright (C) 1999 Descolada (dyn1-tnt9-237.chicago.il.ameritech.net) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_BNETHASH_TYPES #define INCLUDED_BNETHASH_TYPES #include namespace pvpgn { using t_hash = std::uint32_t[5]; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_BNETHASH_PROTOS #define INCLUDED_BNETHASH_PROTOS namespace pvpgn { extern int bnet_hash(t_hash * hashout, unsigned int size, void const * data); extern int sha1_hash(t_hash * hashout, unsigned int size, void const * data); extern int little_endian_sha1_hash(t_hash * hashout, unsigned int size, void const * data); extern int hash_eq(t_hash const h1, t_hash const h2); extern char const * hash_get_str(t_hash const hash); extern char const * little_endian_hash_get_str(t_hash const hash); extern int hash_set_str(t_hash * hash, char const * str); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/bnethashconv.cpp000066400000000000000000000032511462640461400214460ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/bnethashconv.h" #include "common/bnethash.h" #include "common/bn_type.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { extern void bnhash_to_hash(bn_int const * bnhash, t_hash * hash) { unsigned int i; if (!bnhash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL bnhash"); return; } if (!hash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hash"); return; } for (i = 0; i < 5; i++) (*hash)[i] = bn_int_get(bnhash[i]); } extern void hash_to_bnhash(t_hash const * hash, bn_int * bnhash) { unsigned int i; if (!bnhash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL bnhash"); return; } if (!hash) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hash"); return; } for (i = 0; i < 5; i++) bn_int_set(&bnhash[i], (*hash)[i]); } } pvpgn-1.99.7.2.1+dfsg/src/common/bnethashconv.h000066400000000000000000000022161462640461400211130ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_BNETHASHCONV_PROTOS #define INCLUDED_BNETHASHCONV_PROTOS #define JUST_NEED_TYPES #include "common/bn_type.h" #include "common/bnethash.h" #undef JUST_NEED_TYPES namespace pvpgn { extern void bnhash_to_hash(bn_int const * bnhash, t_hash * hash); extern void hash_to_bnhash(t_hash const * hash, bn_int * bnhash); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/bnetsrp3.cpp000066400000000000000000000171121462640461400205250ustar00rootroot00000000000000/* * Class that implements the SRP-3 based authentication schema * used by Blizzards WarCraft 3. Implementations is based upon * public information available under * http://www.javaop.com/@ron/documents/SRP.html * * Copyright (C) 2008 - Olaf Freyer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. */ #include "common/setup_before.h" #include "bnetsrp3.h" #include #include #include #include #include #include #include #include #include #include "common/bigint.h" #include "common/bnethash.h" #include "common/eventlog.h" #include "common/util.h" #include "common/xalloc.h" #include "common/xstring.h" #include "common/setup_after.h" namespace pvpgn { std::uint8_t bnetsrp3_g = 0x2F; const unsigned char bnetsrp3_N[] = { 0xF8, 0xFF, 0x1A, 0x8B, 0x61, 0x99, 0x18, 0x03, 0x21, 0x86, 0xB6, 0x8C, 0xA0, 0x92, 0xB5, 0x55, 0x7E, 0x97, 0x6C, 0x78, 0xC7, 0x32, 0x12, 0xD9, 0x12, 0x16, 0xF6, 0x65, 0x85, 0x23, 0xC7, 0x87 }; const unsigned char bnetsrp3_I[] = { 0xF8, 0x01, 0x8C, 0xF0, 0xA4, 0x25, 0xBA, 0x8B, 0xEB, 0x89, 0x58, 0xB1, 0xAB, 0x6B, 0xF9, 0x0A, 0xED, 0x97, 0x0E, 0x6C }; BigInt BnetSRP3::N = BigInt(bnetsrp3_N, 32); BigInt BnetSRP3::g = BigInt(bnetsrp3_g); BigInt BnetSRP3::I = BigInt(bnetsrp3_I, 32); int BnetSRP3::init(const char* username_, const char* password_, BigInt* salt_) { unsigned int i; const char* source; char* symbol; if (!username_) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL username_"); return -1; } username_length = std::strlen(username_); username = (char*)xmalloc(username_length + 1); source = username_; symbol = username; for (i = 0; i < username_length; i++) { *(symbol++) = safe_toupper(*(source++)); } if (!((password_ == NULL) ^ (salt_ == NULL))) { eventlog(eventlog_level_error, __FUNCTION__, "need to init with EITHER password_ OR salt_"); return -1; } if (password_ != NULL) { password_length = std::strlen(password_); password = (char*)xmalloc(password_length + 1); source = password_; symbol = password; for (i = 0; i < password_length; i++) { *(symbol++) = safe_toupper(*(source++)); } a = BigInt::random(32) % N; s = BigInt::random(32); } else { password = NULL; password_length = 0; b = BigInt::random(32) % N; s = *salt_; } B = NULL; s.getData(raw_salt, 32); return 0; } BnetSRP3::BnetSRP3(const char* username_, BigInt& salt) { init(username_, NULL, &salt); } BnetSRP3::BnetSRP3(const std::string& username_, BigInt& salt) { init(username_.c_str(), NULL, &salt); } BnetSRP3::BnetSRP3(const char* username_, const char* password_) { init(username_, password_, NULL); } BnetSRP3::BnetSRP3(const std::string& username_, const std::string& password_) { init(username_.c_str(), password_.c_str(), NULL); } BnetSRP3::~BnetSRP3() { if (username) xfree(username); if (password) xfree(password); delete B; } BigInt BnetSRP3::getClientPrivateKey() const { char* userpass; // username,':',password t_hash userpass_hash; char private_value[32 + 20]; // s, H(userpass) t_hash private_value_hash; userpass = (char*)xmalloc(username_length + 1 + password_length + 1); std::memcpy(userpass, username, username_length); userpass[username_length] = ':'; std::memcpy(userpass + username_length + 1, password, password_length); userpass[username_length + 1 + password_length] = '\0'; little_endian_sha1_hash(&userpass_hash, username_length + 1 + password_length, userpass); xfree(userpass); std::memcpy(&private_value[0], raw_salt, 32); std::memcpy(&private_value[32], userpass_hash, 20); little_endian_sha1_hash(&private_value_hash, 52, private_value); return BigInt((unsigned char const*)private_value_hash, 20, 1, false); } BigInt BnetSRP3::getScrambler(BigInt& B) const { unsigned char raw_B[32]; std::uint32_t scrambler; t_hash hash; B.getData(raw_B, 32, 4, false); sha1_hash(&hash, 32, raw_B); scrambler = *(std::uint32_t*)hash; return BigInt(scrambler); } BigInt BnetSRP3::getClientSecret(BigInt& B) const { BigInt x = getClientPrivateKey(); BigInt u = getScrambler(B); return (N + B - g.powm(x, N)).powm((x*u) + a, N); } BigInt BnetSRP3::getServerSecret(BigInt& A, BigInt& v) { BigInt B = getServerSessionPublicKey(v); BigInt u = getScrambler(B); return ((A * v.powm(u, N)) % N).powm(b, N); } BigInt BnetSRP3::hashSecret(BigInt& secret) const { int i; unsigned char* raw_secret; unsigned char odd[16], even[16], hashedSecret[40]; unsigned char* secretPointer; unsigned char* oddPointer; unsigned char* evenPointer; t_hash odd_hash, even_hash; raw_secret = secret.getData(32, 4, false); secretPointer = raw_secret; oddPointer = odd; evenPointer = even; for (i = 0; i < 16; i++) { *(oddPointer++) = *(secretPointer++); *(evenPointer++) = *(secretPointer++); } xfree(raw_secret); little_endian_sha1_hash(&odd_hash, 16, odd); little_endian_sha1_hash(&even_hash, 16, even); secretPointer = hashedSecret; oddPointer = (unsigned char*)odd_hash; evenPointer = (unsigned char*)even_hash; for (i = 0; i < 20; i++) { *(secretPointer++) = *(oddPointer++); *(secretPointer++) = *(evenPointer++); } return BigInt(hashedSecret, 40, 1, false); } BigInt BnetSRP3::getVerifier() const { return g.powm(getClientPrivateKey(), N); } BigInt BnetSRP3::getSalt() const { return s; } void BnetSRP3::setSalt(BigInt salt_) { s = salt_; s.getData(raw_salt, 32); } BigInt BnetSRP3::getClientSessionPublicKey() const { return g.powm(a, N); } BigInt BnetSRP3::getServerSessionPublicKey(BigInt& v) { if (!B) B = new BigInt((v + g.powm(b, N)) % N); return *B; } BigInt BnetSRP3::getHashedClientSecret(BigInt& B) const { BigInt clientSecret = getClientSecret(B); return hashSecret(clientSecret); } BigInt BnetSRP3::getHashedServerSecret(BigInt& A, BigInt& v) { BigInt serverSecret = getServerSecret(A, v); return hashSecret(serverSecret); } BigInt BnetSRP3::getClientPasswordProof(BigInt& A, BigInt& B, BigInt& K) const { unsigned char proofData[176]; t_hash usernameHash, proofHash; little_endian_sha1_hash(&usernameHash, username_length, username); I.getData(&proofData[0], 20, 4, false); std::memcpy(&proofData[20], usernameHash, 20); s.getData(&proofData[40], 32); A.getData(&proofData[72], 32, 4, false); B.getData(&proofData[104], 32, 4, false); K.getData(&proofData[136], 40, 4, false); little_endian_sha1_hash(&proofHash, 176, proofData); return BigInt((unsigned char*)proofHash, 20, 1, false); } BigInt BnetSRP3::getServerPasswordProof(BigInt& A, BigInt& M, BigInt& K) const { unsigned char proofData[92]; t_hash proofHash; A.getData(&proofData[0], 32, 4, false); M.getData(&proofData[32], 20, 4, false); K.getData(&proofData[52], 40, 4, false); little_endian_sha1_hash(&proofHash, 92, proofData); return BigInt((unsigned char*)proofHash, 20, 1, false); } } pvpgn-1.99.7.2.1+dfsg/src/common/bnetsrp3.h000066400000000000000000000044301462640461400201710ustar00rootroot00000000000000/* * Class that implements the SRP-3 based authentication schema * used by Blizzards WarCraft 3. Implementations is based upon * public information available under * http://www.javaop.com/@ron/documents/SRP.html * * Copyright (C) 2008 - Olaf Freyer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. */ #ifndef __BNET_SRP3_INCLUDED__ #define __BNET_SRP3_INCLUDED__ #include #include "common/bigint.h" namespace pvpgn { class BnetSRP3 { public: BnetSRP3(const char* username, BigInt& salt); BnetSRP3(const std::string& username, BigInt& salt); BnetSRP3(const char* username, const char* password); BnetSRP3(const std::string& username, const std::string& password); ~BnetSRP3(); BigInt getVerifier() const; BigInt getSalt() const; void setSalt(BigInt salt); BigInt getClientSessionPublicKey() const; BigInt getServerSessionPublicKey(BigInt& v); BigInt getHashedClientSecret(BigInt& B) const; BigInt getHashedServerSecret(BigInt& A, BigInt& v); BigInt getClientPasswordProof(BigInt& A, BigInt& B, BigInt& K) const; BigInt getServerPasswordProof(BigInt& A, BigInt& M, BigInt& K) const; private: int init(const char* username, const char* password, BigInt* salt); BigInt getClientPrivateKey() const; BigInt getScrambler(BigInt& B) const; BigInt getClientSecret(BigInt& B) const; BigInt getServerSecret(BigInt& A, BigInt& v); BigInt hashSecret(BigInt& secret) const; static BigInt N; // modulus static BigInt g; // generator static BigInt I; // H(g) xor H(N) where H() is standard SHA1 BigInt a; // client session private key BigInt b; // server session private key BigInt s; // salt BigInt *B; // server public key cache char* username; size_t username_length; char* password; size_t password_length; unsigned char raw_salt[32]; }; } #endif /* __BNET_SRP3_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/common/bnettime.cpp000066400000000000000000000140061462640461400205730ustar00rootroot00000000000000/* * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define BNETTIME_INTERNAL_ACCESS #include "common/setup_before.h" #include "common/bnettime.h" #include #include #include #include #include "compat/gettimeofday.h" #include "common/eventlog.h" #include "common/bn_type.h" #include "common/setup_after.h" namespace pvpgn { /* * By comparing the hex numbers from packet dumps with the times displayed * on the login screen, bnettime seems to be in units of 10E-7 seconds. It * is stored in a 64bit value, and represented as two distinct numbers when * it is used as a string in the protocol. * * examples (client in GMT timezone): * 11/04/99 05:16 0x01 "29304451 3046165090" * 01/03/99 11:19 0x01 "29243146 3825346784" * 08/02/99 21:28 0x01 "29285677 4283311890" * 12/12/99 01:55 0x01 "29312068 151587081" * * The top number seems to be in units of about 7 minutes because the bottom * number rolls over every 2^32 10E-7 seconds, which is: * (2^32)*.0000001 * 429.4967296 // seconds * 429/60 * 7.15827882666666666666 // minutes * * The epoch was determined using a binary search looking for Jan 1, 1970 * (GMT) to be displayed after setting the last game time to a pair of * numbers. It was determined through all 64 bits by looking for the exact * number where adding one pushes it over to 00:01. It was determined to be: * "27111902 3577643008" * * It appears that this is the same format as is used in MS-DOS to store file * times. The format is a 64 bit number telling how many 100 nanosecond * intervals since Jan 1, 1601. * * If I was clever I should be able to do this without floating point math. * I don't feel like being clever though :) (Refer to the function * DOSFS_UnixTimeToFileTime() in dosfs.c in the WINE source code for an * example that I wish I had seen before writing this). * * Update... from looking at the WINE sources VAP has determined that these * are actually in MS-Windows FileTime format. * * "In the WINE listing, you can see that * FileTime = (UnixTime * 10 000 000) + 116 444 736 000 000 000 + remainder * These dates are represented from 1 Jan 1601." */ #define SEC_PER_USEC .0000001 #define UNIX_EPOCH 11644473600. /* seconds the Unix epoch is from the bnettime epoch */ #define SEC_PER_UPPER 429.4967296 /* (2^32)*10E-7 */ #define SEC_PER_LOWER .0000001 #define UPPER_PER_SEC .00232830643653869628 /* 10E7/(2^32) */ #define LOWER_PER_SEC 10000000. extern t_bnettime secs_to_bnettime(double secs) { t_bnettime temp; temp.u = (unsigned int)(secs*UPPER_PER_SEC); temp.l = (unsigned int)(secs*LOWER_PER_SEC); return temp; } extern double bnettime_to_secs(t_bnettime bntime) { return ((double)bntime.u)*SEC_PER_UPPER + ((double)bntime.l)*SEC_PER_LOWER; } extern t_bnettime time_to_bnettime(std::time_t stdtime, unsigned int usec) { return secs_to_bnettime((double)stdtime + (double)usec*SEC_PER_USEC + UNIX_EPOCH); } extern std::time_t bnettime_to_time(t_bnettime bntime) { return (std::time_t)(bnettime_to_secs(bntime) - UNIX_EPOCH); } /* return current time as bnettime */ extern t_bnettime bnettime(void) { struct timeval tv; if (gettimeofday(&tv, NULL) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not get time (gettimeofday: {})", std::strerror(errno)); return time_to_bnettime(std::time(NULL), 0); } return time_to_bnettime((std::time_t)tv.tv_sec, tv.tv_usec); } /* FIXME: the string functions here should probably go in account_wrap */ extern char const * bnettime_get_str(t_bnettime bntime) { static char temp[1024]; std::sprintf(temp, "%u %u", bntime.u, bntime.l); return temp; } extern int bnettime_set_str(t_bnettime * bntime, char const * timestr) { if (!bntime) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL bntime"); return -1; } if (!timestr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL timestr"); return -1; } if (std::sscanf(timestr, "%u %u", &bntime->u, &bntime->l) != 2) return -1; return 0; } extern void bnettime_to_bn_long(t_bnettime in, bn_long * out) { if (!out) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL out"); return; } bn_long_set_a_b(out, in.u, in.l); } extern void bn_long_to_bnettime(bn_long in, t_bnettime * out) { if (!out) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL out"); return; } out->u = bn_long_get_a(in); out->l = bn_long_get_b(in); } extern int local_tzbias(void) /* in minutes */ { std::time_t now; std::time_t test; std::time_t testloc; struct std::tm * temp; now = std::time(NULL); if (!(temp = std::gmtime(&now))) return 0; if ((test = std::mktime(temp)) == (std::time_t)(-1)) return 0; if (!(temp = std::localtime(&now))) return 0; if ((testloc = std::mktime(temp)) == (std::time_t)(-1)) return 0; if (testloc > test) /* std::time_t is probably unsigned... */ return -(int)(testloc - test) / 60; return (int)(test - testloc) / 60; } extern t_bnettime bnettime_add_tzbias(t_bnettime bntime, int tzbias) { return secs_to_bnettime(bnettime_to_secs(bntime) - (double)tzbias*60.0); } } pvpgn-1.99.7.2.1+dfsg/src/common/bnettime.h000066400000000000000000000037541462640461400202500ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_BNETTIME_TYPES #define INCLUDED_BNETTIME_TYPES namespace pvpgn { typedef struct { #ifdef BNETTIME_INTERNAL_ACCESS unsigned int u; unsigned int l; #else unsigned int _private1; unsigned int _private2; #endif } t_bnettime; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_BNETTIME_PROTOS #define INCLUDED_BNETTIME_PROTOS #include #define JUST_NEED_TYPES #include "common/bn_type.h" #undef JUST_NEED_TYPES namespace pvpgn { extern t_bnettime secs_to_bnettime(double secs); extern double bnettime_to_secs(t_bnettime bntime); extern t_bnettime time_to_bnettime(std::time_t stdtime, unsigned int usec); extern std::time_t bnettime_to_time(t_bnettime bntime); extern t_bnettime bnettime(void); extern char const * bnettime_get_str(t_bnettime bntime); extern int bnettime_set_str(t_bnettime * bntime, char const * timestr); extern int local_tzbias(void); extern t_bnettime bnettime_add_tzbias(t_bnettime bntime, int tzbias); extern void bnettime_to_bn_long(t_bnettime in, bn_long * out); extern void bn_long_to_bnettime(bn_long in, t_bnettime * out); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/bot_protocol.h000066400000000000000000000061561462640461400211450ustar00rootroot00000000000000/* * Copyright (C) 1999,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_BOT_PROTOCOL_PROTOS #define INCLUDED_BOT_PROTOCOL_PROTOS /* * The bot protocol has no real structure. It basically different ASCII * formatted lines. */ /*********************************** */ #define EID_SHOWUSER 1001 /***********************************/ /*********************************** */ #define EID_JOIN 1002 /***********************************/ /*********************************** */ #define EID_LEAVE 1003 /***********************************/ /*********************************** */ #define EID_WHISPER 1004 /***********************************/ /*********************************** */ #define EID_TALK 1005 /***********************************/ /*********************************** */ #define EID_BROADCAST 1006 /***********************************/ /*********************************** */ #define EID_CHANNEL 1007 /***********************************/ /*********************************** */ #define EID_USERFLAGS 1009 /***********************************/ /*********************************** */ #define EID_WHISPERSENT 1010 /***********************************/ /*********************************** */ #define EID_CHANNELFULL 1013 /***********************************/ /*********************************** */ #define EID_CHANNELDOESNOTEXIST 1014 /***********************************/ /*********************************** */ #define EID_CHANNELRESTRICTED 1015 /***********************************/ /*********************************** 1018 INFO "You are Anonymous#11, using Chat in a private game." */ #define EID_INFO 1018 /* 1016? */ /***********************************/ /*********************************** 1019 ERROR "That is not a valid command. Type /help or /? for more info." */ #define EID_ERROR 1019 /***********************************/ /*********************************** */ #define EID_EMOTE 1023 /***********************************/ /*********************************** 2000 NULL */ #define EID_NULL 2000 /***********************************/ /*********************************** 2010 NAME Anonymous#11 */ #define EID_UNIQUENAME 2010 /***********************************/ #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/conf.cpp000066400000000000000000000145311462640461400177140ustar00rootroot00000000000000/* * Copyright (C) 2004,2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/conf.h" #include #include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "common/util.h" #include "common/setup_after.h" namespace pvpgn { extern int conf_set_int(unsigned *pint, const char *valstr, unsigned def) { if (!valstr) *pint = def; else { unsigned temp; if (str_to_uint(valstr, &temp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "invalid integer value '{}'", valstr); return -1; } else *pint = temp; } return 0; } extern int conf_set_bool(unsigned *pbool, const char *valstr, unsigned def) { if (!valstr) *pbool = def; else { switch (str_get_bool(valstr)) { case 1: *pbool = 1; break; case 0: *pbool = 0; break; default: eventlog(eventlog_level_error, __FUNCTION__, "invalid boolean value '{}'", valstr); return -1; } } return 0; } extern int conf_set_str(const char **pstr, const char *valstr, const char *def) { if (*pstr) xfree((void*)*pstr); if (!valstr && !def) *pstr = NULL; else *pstr = xstrdup(valstr ? valstr : def); return 0; } extern int conf_set_timestr(std::time_t* ptime, const char *valstr, std::time_t def) { if (!valstr) *ptime = def; else if (timestr_to_time(valstr, ptime) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "invalid timestr value '{}'", valstr); return -1; } return 0; } extern const char* conf_get_int(unsigned ival) { static char buffer[128] = {}; std::snprintf(buffer, sizeof buffer, "%u", ival); return buffer; } extern const char* conf_get_bool(unsigned ival) { return ival ? "true" : "false"; } static void _conf_reset_defaults(t_conf_entry *conftab) { t_conf_entry *curr; assert(conftab); for (curr = conftab; curr->name; curr++) curr->setdef(); } static char * _get_value(char *str, unsigned lineno) { char *cp, prev; if (*str == '"') { for (cp = ++str, prev = '\0'; *cp; cp++) { switch (*cp) { case '"': if (prev != '\\') break; prev = '"'; continue; case '\\': if (prev == '\\') prev = '\0'; else prev = '\\'; continue; default: prev = *cp; continue; } break; } if (*cp != '"') { eventlog(eventlog_level_error, __FUNCTION__, "missing end quota at line {}", lineno); return NULL; } *cp = '\0'; cp = str_skip_space(cp + 1); if (*cp) { eventlog(eventlog_level_error, __FUNCTION__, "extra characters in value after ending quote at line {}", lineno); return NULL; } } else { cp = str_skip_word(str); if (*cp) { *cp = '\0'; cp = str_skip_space(cp + 1); if (*cp) { eventlog(eventlog_level_error, __FUNCTION__, "extra characters after the value at line {}", lineno); return NULL; } } } return str; } static void _process_option(const char *key, const char *val, t_conf_entry *conftab) { t_conf_entry *curr; for (curr = conftab; curr->name; curr++) if (!std::strcmp(key, curr->name)) { curr->set(val); return; } eventlog(eventlog_level_error, __FUNCTION__, "option '{}' unknown", key); } extern int conf_load_file(std::FILE *fd, t_conf_entry *conftab) { char *buff, *directive, *value, *cp; unsigned cflag, lineno; if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL file"); return -1; } if (!conftab) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conftab"); return -1; } /* restore defaults */ _conf_reset_defaults(conftab); for (lineno = 1; (buff = file_get_line(fd)); lineno++) { cflag = 1; for (cp = buff; *cp; cp++) { switch (*cp) { case '\\': if (!*(++cp)) cflag = 0; break; case '"': switch (cflag) { case 1: cflag = 2; break; case 2: cflag = 1; break; } break; case '#': if (cflag == 1) cflag = 0; break; } if (!cflag) break; } if (*cp == '#') *cp = '\0'; cp = str_skip_space(buff); if (!*cp) continue; directive = cp; cp = str_skip_word(cp + 1); if (*cp) *(cp++) = '\0'; cp = str_skip_space(cp); if (*cp != '=') { eventlog(eventlog_level_error, __FUNCTION__, "missing = on line {}", lineno); continue; } cp = str_skip_space(cp + 1); if (!*cp) { eventlog(eventlog_level_error, __FUNCTION__, "missing value at line {}", lineno); continue; } value = _get_value(cp, lineno); if (!value) continue; _process_option(directive, value, conftab); } return 0; } extern void conf_unload(t_conf_entry *conftab) { t_conf_entry *curr; if (!conftab) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conftab"); return; } for (curr = conftab; curr->name; curr++) curr->set(NULL); } extern int conf_load_cmdline(int argc, char **argv, t_conf_entry *conftab) { int i; char *key, *val, *newkey; if (!conftab) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL conftab"); return -1; } /* restore defaults */ _conf_reset_defaults(conftab); for (i = 1; i < argc; ++i) { newkey = NULL; key = argv[i]; if (*(key++) != '-') /* skip non options */ continue; if (*key == '-') key++; /* allow both - and -- options */ if ((val = std::strchr(key, '='))) { /* we got option=value format */ newkey = xstrdup(key); key = newkey; val = std::strchr(key, '='); *(val++) = '\0'; } else if (i + 1 < argc && argv[i + 1][0] != '-') { val = argv[++i]; } if (!val) /* option without argument, so it's like boolean */ val = (char *)("true"); _process_option(key, val, conftab); if (newkey) xfree(newkey); } return 0; } } pvpgn-1.99.7.2.1+dfsg/src/common/conf.h000066400000000000000000000036571462640461400173700ustar00rootroot00000000000000/* * Copyright (C) 2004,2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __CONF_H_TYPES__ #define __CONF_H_TYPES__ namespace pvpgn { /* a conf entry object with a set and a get method */ typedef struct { const char *name; int(*set)(const char *valstr); /* called with NULL for cleanup */ const char * (*get)(void); int(*setdef)(void); } t_conf_entry; } #endif /* __CONF_H_TYPES__ */ #ifndef __CONF_H_PROTOS__ #define __CONF_H_PROTOS__ #include #include namespace pvpgn { /* helpfull utility functions for common conf types like bool, int and str */ extern int conf_set_bool(unsigned *pbool, const char *valstr, unsigned def); extern int conf_set_int(unsigned *pint, const char *valstr, unsigned def); extern int conf_set_str(const char **pstr, const char *valstr, const char *def); extern int conf_set_timestr(std::time_t* ptime, const char *valstr, std::time_t def); extern const char* conf_get_int(unsigned ival); extern const char* conf_get_bool(unsigned ival); /* loading/unloading functions */ extern int conf_load_file(std::FILE *fd, t_conf_entry *conftab); extern int conf_load_cmdline(int argc, char **argv, t_conf_entry *conftab); extern void conf_unload(t_conf_entry *conftab); } #endif /* __CONF_H_PROTOS__ */ pvpgn-1.99.7.2.1+dfsg/src/common/d2char_checksum.cpp000066400000000000000000000024121462640461400220070ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "d2char_checksum.h" #include "common/setup_after.h" namespace pvpgn { extern int d2charsave_checksum(unsigned char const * data, unsigned int len, unsigned int offset) { int checksum; unsigned int i; unsigned int ch; if (!data) return 0; checksum = 0; for (i = 0; i < len; i++) { if (i >= offset && i < offset + sizeof(int)) ch = 0; else ch = *data; ch += (checksum < 0); checksum = 2 * checksum + ch; data++; } return checksum; } } pvpgn-1.99.7.2.1+dfsg/src/common/d2char_checksum.h000066400000000000000000000017741462640461400214660ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CHAR_CHECKSUM_H #define INCLUDED_D2CHAR_CHECKSUM_H #define D2CHARSAVE_CHECKSUM_OFFSET 0x0C namespace pvpgn { extern int d2charsave_checksum(unsigned char const * data, unsigned int len, unsigned int offset); } #endif pvpgn-1.99.7.2.1+dfsg/src/common/d2char_file.h000066400000000000000000000041631462640461400205760ustar00rootroot00000000000000#ifndef INCLUDED_COMMON_D2CHAR_FILE #define INCLUDED_COMMON_D2CHAR_FILE #define D2CHARFILE_PADBYTE 0xff namespace pvpgn { typedef struct { /* Finish copying from bnet_protocol.h/t_d2_char_info */ bn_long experience; bn_byte reservedxp[30]; bn_long invgold; bn_long bankgold; bn_long stashgold; bn_long reservedgold[8]; bn_byte skilllvls[30]; bn_byte reservedskills[30]; bn_short strength; bn_short vitaility; bn_short dexterity; bn_short energy; bn_short reservedattr[4]; bn_byte questflags[4]; /* 1 byte/8 bits each act */ bn_byte reservedquestflags[16]; bn_byte waypoints[4][3]; /* 3 bytes/24 bits each act */ bn_byte reservedwaypoints[4][3]; /* stuff like HP, Mana are calculated dynamically... */ bn_byte inventory[80]; /* backpack got 40 spaces, double that to be safe */ bn_byte reservedinv[80]; /* space for expanding */ bn_byte belt[32]; /* largest belt holds 16, double that to be safe */ bn_byte reservedbelt[32]; /* space for expanding */ bn_int deathcount; bn_byte body1unknownb1; /* For dead body... */ bn_byte body1unknownb2; bn_byte body1helmgfx; bn_byte body1bodygfx; bn_byte body1leggfx; bn_byte body1lhandweapon; bn_byte body1lhandgfx; bn_byte body1rhandweapon; bn_byte body1rhandgfx; bn_byte body1unknownb3; bn_byte body1unknownb4; bn_byte body1unknownb5; bn_byte body1unknownb6; bn_byte body1unknownb7; bn_byte body1unknownb8; bn_byte body1unknownb9; bn_byte body1unknownb10; bn_byte body1unknownb11; bn_byte body1inventory[80]; /* backpack got 40 spaces, double that to be safe */ bn_byte body1reservedinv[80]; /* space for expanding */ bn_byte body1belt[32]; /* largest belt holds 16, double that to be safe */ bn_byte body1reservedbelt[32]; /* space for expanding */ bn_byte stashinv[100]; /* Forgot how big the stash is.... */ bn_byte reservedstash[200]; } t_d2char_record; } /* item description base type (1 byte) item quality (unknown size) Magic (blue name) modifer level magic dword 1 magic dword 2 */ #endif pvpgn-1.99.7.2.1+dfsg/src/common/d2cs_bnetd_protocol.h000066400000000000000000000053421462640461400223640ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CS_BNETD_PROTOCOL_H #define INCLUDED_D2CS_BNETD_PROTOCOL_H #include "common/bn_type.h" namespace pvpgn { typedef struct { bn_short size; bn_short type; bn_int seqno; /* seqno, set by the sender */ } t_d2cs_bnetd_header; typedef struct { t_d2cs_bnetd_header h; } t_d2cs_bnetd_generic; #define BNETD_D2CS_AUTHREQ 0x01 typedef struct { t_d2cs_bnetd_header h; bn_int sessionnum; } t_bnetd_d2cs_authreq; #define D2CS_BNETD_AUTHREPLY 0x02 typedef struct { t_d2cs_bnetd_header h; bn_int version; /* realm name */ } t_d2cs_bnetd_authreply; #define BNETD_D2CS_AUTHREPLY 0x02 typedef struct { t_d2cs_bnetd_header h; bn_int reply; } t_bnetd_d2cs_authreply; #define BNETD_D2CS_AUTHREPLY_SUCCEED 0x00 #define BNETD_D2CS_AUTHREPLY_BAD_VERSION 0x01 #define D2CS_BNETD_ACCOUNTLOGINREQ 0x10 typedef struct { t_d2cs_bnetd_header h; bn_int seqno; bn_int sessionnum; bn_int sessionkey; bn_int secret_hash[5]; /* account name */ } t_d2cs_bnetd_accountloginreq; #define BNETD_D2CS_ACCOUNTLOGINREPLY 0x10 typedef struct { t_d2cs_bnetd_header h; bn_int reply; } t_bnetd_d2cs_accountloginreply; #define BNETD_D2CS_ACCOUNTLOGINREPLY_SUCCEED 0x00 #define BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED 0x01 #define D2CS_BNETD_CHARLOGINREQ 0x11 typedef struct { t_d2cs_bnetd_header h; bn_int sessionnum; /* character name */ /* character portrait */ } t_d2cs_bnetd_charloginreq; #define BNETD_D2CS_CHARLOGINREPLY 0x11 typedef struct { t_d2cs_bnetd_header h; bn_int reply; } t_bnetd_d2cs_charloginreply; #define BNETD_D2CS_CHARLOGINREPLY_SUCCEED 0x00 #define BNETD_D2CS_CHARLOGINREPLY_FAILED 0x01 #define BNETD_D2CS_GAMEINFOREQ 0x12 typedef struct { t_d2cs_bnetd_header h; /* gamename */ } t_bnetd_d2cs_gameinforeq; #define D2CS_BNETD_GAMEINFOREPLY 0x12 typedef struct { t_d2cs_bnetd_header h; bn_byte difficulty; /* gamename */ } t_d2cs_bnetd_gameinforeply; } #endif pvpgn-1.99.7.2.1+dfsg/src/common/d2cs_d2dbs_ladder.h000066400000000000000000000031141462640461400216530ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CS_D2DBS_LADDER_H #define INCLUDED_D2CS_D2DBS_LADDER_H #include "common/bn_type.h" #include "common/field_sizes.h" namespace pvpgn { typedef struct { bn_int type; bn_int offset; bn_int number; } t_d2ladderfile_ladderindex; typedef struct { bn_int experience; bn_short status; bn_byte level; bn_byte chclass; char charname[MAX_CHARNAME_LEN]; } t_d2ladderfile_ladderinfo; typedef struct { bn_int maxtype; bn_int checksum; } t_d2ladderfile_header; } #define LADDER_FILE_PREFIX "ladder" #define D2LADDER_HC_OVERALL 0x00 #define D2LADDER_STD_OVERALL 0x09 #define D2LADDER_EXP_HC_OVERALL 0x13 #define D2LADDER_EXP_STD_OVERALL 0x1B const unsigned D2CHAR_CLASS_MAX = 0x04; const unsigned D2CHAR_EXP_CLASS_MAX = 0x06; #endif pvpgn-1.99.7.2.1+dfsg/src/common/d2cs_d2gs_character.h000066400000000000000000000050001462640461400222110ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn) * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CS_D2GS_CHARACTER_H #define INCLUDED_D2CS_D2GS_CHARACTER_H #include "common/bn_type.h" #include "common/field_sizes.h" #ifdef D2GS # include "bn_types.h" #pragma pack(push, character_h, 1) #endif #define D2CHARINFO_MAGICWORD 0x12345678 #define D2CHARINFO_VERSION 0x00010000 #define D2CHARINFO_PORTRAIT_PADSIZE 30 namespace pvpgn { typedef struct { bn_int magicword; /* static for check */ bn_int version; /* charinfo file version */ bn_int create_time; /* character creation time */ bn_int last_time; /* character last access time */ bn_int checksum; bn_int total_play_time; /* total in game play time */ bn_int reserved[6]; unsigned char charname[MAX_CHARNAME_LEN]; unsigned char account[MAX_USERNAME_LEN]; unsigned char realmname[MAX_REALMNAME_LEN]; } t_d2charinfo_header; typedef struct { bn_int experience; bn_int charstatus; bn_int charlevel; bn_int charclass; } t_d2charinfo_summary; typedef struct { bn_short header; /* 0x84 0x80 */ bn_byte gfx[11]; bn_byte chclass; bn_byte color[11]; bn_byte level; bn_byte status; bn_byte u1[3]; bn_byte ladder; /* need not be 0xff and 0 to make character displayed as ladder character */ /* client only check this bit */ bn_byte u2[2]; bn_byte end; /* 0x00 */ } t_d2charinfo_portrait; typedef struct { t_d2charinfo_header header; t_d2charinfo_portrait portrait; bn_byte pad[D2CHARINFO_PORTRAIT_PADSIZE]; t_d2charinfo_summary summary; } t_d2charinfo_file; } #ifdef D2GS #pragma pack(pop, character_h) #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/d2cs_d2gs_protocol.h000066400000000000000000000115161462640461400221270ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CS_D2GS_PROTOCOL_H #define INCLUDED_D2CS_D2GS_PROTOCOL_H /* * there is only little comments in this header file * check the documents for more details */ /* include your own header files here */ #include "common/bn_type.h" #ifdef D2GS # include "bn_types.h" #pragma pack(push, pack01, 1) #endif namespace pvpgn { typedef struct { bn_short size; bn_short type; bn_int seqno; /* seqno, set by the sender */ } t_d2cs_d2gs_header; typedef struct { t_d2cs_d2gs_header h; } t_d2cs_d2gs_generic; #define D2CS_D2GS_AUTHREQ 0x10 typedef struct { t_d2cs_d2gs_header h; bn_int sessionnum; bn_int signlen; /* realm name */ /* key check sum, maybe 128 bytes */ } t_d2cs_d2gs_authreq; #define D2GS_D2CS_AUTHREPLY 0x11 typedef struct { t_d2cs_d2gs_header h; bn_int version; bn_int checksum; bn_int randnum; bn_int signlen; bn_basic sign[128]; } t_d2gs_d2cs_authreply; #define D2CS_D2GS_AUTHREPLY 0x11 typedef struct { t_d2cs_d2gs_header h; bn_int reply; } t_d2cs_d2gs_authreply; #define D2CS_D2GS_AUTHREPLY_SUCCEED 0x00 #define D2CS_D2GS_AUTHREPLY_BAD_VERSION 0x01 #define D2CS_D2GS_AUTHREPLY_BAD_CHECKSUM 0x02 #define D2GS_D2CS_SETGSINFO 0x12 typedef struct { t_d2cs_d2gs_header h; bn_int maxgame; bn_int gameflag; } t_d2gs_d2cs_setgsinfo; #define D2CS_D2GS_SETGSINFO 0x12 typedef struct { t_d2cs_d2gs_header h; bn_int maxgame; bn_int gameflag; } t_d2cs_d2gs_setgsinfo; #define D2CS_D2GS_ECHOREQ 0x13 typedef struct { t_d2cs_d2gs_header h; } t_d2cs_d2gs_echoreq; #define D2GS_D2CS_ECHOREPLY 0x13 typedef struct { t_d2cs_d2gs_header h; } t_d2gs_d2cs_echoreply; #define D2CS_D2GS_CONTROL 0x14 typedef struct { t_d2cs_d2gs_header h; bn_int cmd; bn_int value; } t_d2cs_d2gs_control; #define D2CS_D2GS_CONTROL_CMD_RESTART 0x01 #define D2CS_D2GS_CONTROL_CMD_SHUTDOWN 0x02 #define D2CS_D2GS_CONTROL_VALUE_DEFAULT 0x00 #define D2CS_D2GS_SETINITINFO 0x15 typedef struct { t_d2cs_d2gs_header h; bn_int time; bn_int gs_id; bn_int ac_version; /* ac_checksum */ } t_d2cs_d2gs_setinitinfo; #define D2CS_D2GS_SETCONFFILE 0x16 typedef struct { t_d2cs_d2gs_header h; bn_int size; bn_int reserved1; /* conf file (null terminated string) */ } t_d2cs_d2gs_setconffile; #define D2CS_D2GS_CREATEGAMEREQ 0x20 typedef struct { t_d2cs_d2gs_header h; bn_byte ladder; bn_byte expansion; bn_byte difficulty; bn_byte hardcore; /* gamename */ /* gamepass */ /* gamedesc */ /* create by acctname */ /* create by charname */ /* create by ip address(string) */ } t_d2cs_d2gs_creategamereq; #define D2GAME_DIFFICULTY_NORMAL 0 #define D2GAME_DIFFICULTY_NIGHTMARE 1 #define D2GAME_DIFFICULTY_HELL 2 #define D2GS_D2CS_CREATEGAMEREPLY 0x20 typedef struct { t_d2cs_d2gs_header h; bn_int result; bn_int gameid; } t_d2gs_d2cs_creategamereply; #define D2GS_D2CS_CREATEGAME_SUCCEED 0 #define D2GS_D2CS_CREATEGAME_FAILED 1 #define D2CS_D2GS_JOINGAMEREQ 0x21 typedef struct { t_d2cs_d2gs_header h; bn_int gameid; bn_int token; /* character name */ /* account name */ /* client ip address(string) */ } t_d2cs_d2gs_joingamereq; #define D2GS_D2CS_JOINGAMEREPLY 0x21 typedef struct { t_d2cs_d2gs_header h; bn_int result; bn_int gameid; } t_d2gs_d2cs_joingamereply; #define D2GS_D2CS_JOINGAME_SUCCEED 0 #define D2GS_D2CS_JOINGAME_FAILED 1 #define D2GS_D2CS_JOINGAME_GAME_FULL 2 #define D2GS_D2CS_UPDATEGAMEINFO 0x22 typedef struct { t_d2cs_d2gs_header h; bn_int flag; bn_int gameid; bn_int charlevel; bn_int charclass; /* charname */ } t_d2gs_d2cs_updategameinfo; #define D2GS_D2CS_UPDATEGAMEINFO_FLAG_UPDATE 0 #define D2GS_D2CS_UPDATEGAMEINFO_FLAG_ENTER 1 #define D2GS_D2CS_UPDATEGAMEINFO_FLAG_LEAVE 2 #define D2GS_D2CS_CLOSEGAME 0x23 typedef struct { t_d2cs_d2gs_header h; bn_int gameid; } t_d2gs_d2cs_closegame; #ifdef D2GS #pragma pack(pop, pack01) #endif } #endif pvpgn-1.99.7.2.1+dfsg/src/common/d2cs_protocol.h000066400000000000000000000233301462640461400212050ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CS_PROTOCOL_H #define INCLUDED_D2CS_PROTOCOL_H #include "common/bn_type.h" namespace pvpgn { typedef struct { bn_short size; bn_byte type; } t_d2cs_client_header; typedef struct { t_d2cs_client_header h; } t_d2cs_client_generic; #define CLIENT_D2CS_LOGINREQ 0x01 typedef struct { t_d2cs_client_header h; bn_int seqno; bn_int u1; bn_int bncs_addr1; bn_int sessionnum; bn_int sessionkey; /* always zero,self define */ bn_int cdkey_id; bn_int u5; bn_int clienttag; bn_int bnversion; bn_int bncs_addr2; bn_int u6; /* zero */ bn_int secret_hash[5]; /* account name */ } t_client_d2cs_loginreq; #define D2CS_CLIENT_LOGINREPLY 0x01 typedef struct { t_d2cs_client_header h; bn_int reply; } t_d2cs_client_loginreply; #define D2CS_CLIENT_LOGINREPLY_SUCCEED 0x00 #define D2CS_CLIENT_LOGINREPLY_BADPASS 0x0c #define CLIENT_D2CS_CREATECHARREQ 0x02 typedef struct { t_d2cs_client_header h; bn_short chclass; /* character class */ bn_short u1; /* always zero */ bn_short status; /* same as in .d2s file */ /* character name */ } t_client_d2cs_createcharreq; #define D2CS_CLIENT_CREATECHARREPLY 0x02 typedef struct { t_d2cs_client_header h; bn_int reply; } t_d2cs_client_createcharreply; #define D2CS_CLIENT_CREATECHARREPLY_SUCCEED 0x00 #define D2CS_CLIENT_CREATECHARREPLY_FAILED 0x01 #define D2CS_CLIENT_CREATECHARREPLY_ALREADY_EXIST 0x14 #define D2CS_CLIENT_CREATECHARREPLY_NAME_REJECT 0x15 #define CLIENT_D2CS_CREATEGAMEREQ 0x03 typedef struct { t_d2cs_client_header h; bn_short seqno; bn_int gameflag; /* just difficulty is set here */ bn_byte u1; /* always 1 */ bn_byte leveldiff; /* Only allow people of +/- n level to join */ bn_byte maxchar; /* Maximum number of chars allowed in game */ /* game name */ /* game pass */ /* game desc */ } t_client_d2cs_creategamereq; #define D2CS_CLIENT_CREATEGAMEREPLY 0x03 typedef struct { t_d2cs_client_header h; bn_short seqno; bn_short gameid; bn_short u1; /* always zero */ bn_int reply; } t_d2cs_client_creategamereply; #define D2CS_CLIENT_CREATEGAMEREPLY_SUCCEED 0x00 #define D2CS_CLIENT_CREATEGAMEREPLY_FAILED 0x01 #define D2CS_CLIENT_CREATEGAMEREPLY_INVALID_NAME 0x1e #define D2CS_CLIENT_CREATEGAMEREPLY_NAME_EXIST 0x1f #define D2CS_CLIENT_CREATEGAMEREPLY_SERVER_DOWN 0x20 #define D2CS_CLIENT_CREATEGAMEREPLY_NOT_AVAILABLE 0x32 #define D2CS_CLIENT_CREATEGAMEREPLY_U1 0x33 #define CLIENT_D2CS_JOINGAMEREQ 0x04 typedef struct { t_d2cs_client_header h; bn_short seqno; /* game name */ /* game pass */ } t_client_d2cs_joingamereq; #define D2CS_CLIENT_JOINGAMEREPLY 0x04 typedef struct { t_d2cs_client_header h; bn_short seqno; bn_short gameid; bn_short u1; /* always zero */ bn_int addr; bn_int token; bn_int reply; } t_d2cs_client_joingamereply; #define D2CS_CLIENT_JOINGAMEREPLY_SUCCEED 0x00 #define D2CS_CLIENT_JOINGAMEREPLY_BAD_PASS 0x29 #define D2CS_CLIENT_JOINGAMEREPLY_NOT_EXIST 0x2a #define D2CS_CLIENT_JOINGAMEREPLY_GAME_FULL 0x2b #define D2CS_CLIENT_JOINGAMEREPLY_LEVEL_LIMIT 0x2c #define D2CS_CLIENT_JOINGAMEREPLY_HARDCORE_SOFTCORE 0x71 #define D2CS_CLIENT_JOINGAMEREPLY_NORMAL_NIGHTMARE 0x73 #define D2CS_CLIENT_JOINGAMEREPLY_NIGHTMARE_HELL 0x74 #define D2CS_CLIENT_JOINGAMEREPLY_CLASSIC_EXPANSION 0x78 #define D2CS_CLIENT_JOINGAMEREPLY_EXPANSION_CLASSIC 0x79 #define D2CS_CLIENT_JOINGAMEREPLY_NORMAL_LADDER 0x7D #define D2CS_CLIENT_JOINGAMEREPLY_FAILED 0x01 #define CLIENT_D2CS_GAMELISTREQ 0x05 typedef struct { t_d2cs_client_header h; bn_short seqno; bn_int gameflag; /* only hardcore is set here */ /* bn_byte u1; string game name to search? */ } t_client_d2cs_gamelistreq; #define D2CS_CLIENT_GAMELISTREPLY 0x05 typedef struct { t_d2cs_client_header h; bn_short seqno; bn_int token; bn_byte currchar; /* current number of characters in game */ bn_int gameflag; /* 0x04 is always set here */ /* game name */ /* game desc */ } t_d2cs_client_gamelistreply; #define CLIENT_D2CS_GAMEINFOREQ 0x06 typedef struct { t_d2cs_client_header h; bn_short seqno; /* game name */ } t_client_d2cs_gameinforeq; #define D2CS_CLIENT_GAMEINFOREPLY 0x06 typedef struct { t_d2cs_client_header h; bn_short seqno; bn_int gameflag; bn_int etime; bn_byte charlevel; bn_byte leveldiff; bn_byte maxchar; bn_byte currchar; bn_byte chclass[16]; /* 16 character class */ bn_byte level[16]; /* 16 character level */ /* game description */ /* currchar number of character names */ } t_d2cs_client_gameinforeply; #define CLIENT_D2CS_CHARLOGINREQ 0x07 typedef struct { t_d2cs_client_header h; /* character name */ } t_client_d2cs_charloginreq; #define D2CS_CLIENT_CHARLOGINREPLY 0x07 typedef struct { t_d2cs_client_header h; bn_int reply; } t_d2cs_client_charloginreply; #define D2CS_CLIENT_CHARLOGINREPLY_SUCCEED 0x00 #define D2CS_CLIENT_CHARLOGINREPLY_FAILED 0x01 #define D2CS_CLIENT_CHARLOGINREPLY_NOT_FOUND 0x46 #define D2CS_CLIENT_CHARLOGINREPLY_EXPIRED 0x7b #define CLIENT_D2CS_DELETECHARREQ 0x0a typedef struct { t_d2cs_client_header h; bn_short u1; /* always zero */ /* character name */ } t_client_d2cs_deletecharreq; #define D2CS_CLIENT_DELETECHARREPLY 0x0a typedef struct { t_d2cs_client_header h; bn_short u1; /* always zero */ bn_int reply; } t_d2cs_client_deletecharreply; #define D2CS_CLIENT_DELETECHARREPLY_SUCCEED 0x00 #define D2CS_CLIENT_DELETECHARREPLY_FAILED 0x01 #define CLIENT_D2CS_LADDERREQ 0x11 typedef struct { t_d2cs_client_header h; bn_byte type; /* jadder type request */ bn_short start_pos; /* list ladder from what position */ } t_client_d2cs_ladderreq; #define D2CS_CLIENT_LADDERREPLY 0x11 typedef struct { t_d2cs_client_header h; bn_byte type; /* ladder type request */ bn_short total_len; /* total length of the ladder data */ bn_short curr_len; /* length of ladder data in this packet */ bn_short cont_len; /* length of ladder data in previous packets */ /* length here include the header but exclude this packet */ } t_d2cs_client_ladderreply; typedef struct { bn_short start_pos; /* start position of ladder */ bn_short u1; /* always zero */ bn_int count1; /* always 0x10 */ } t_d2cs_client_ladderheader; typedef struct { bn_int count2; /* 0x10 for first packet or 0x0 for continue */ } t_d2cs_client_ladderinfoheader; typedef struct { bn_int explow; bn_int exphigh; /* always zero */ bn_short status; bn_byte level; bn_byte u1; /* always zero */ char charname[16]; } t_d2cs_client_ladderinfo; #define LADDERSTATUS_FLAG_DEAD 0x10 #define LADDERSTATUS_FLAG_HARDCORE 0x20 #define LADDERSTATUS_FLAG_EXPANSION 0x40 #define LADDERSTATUS_FLAG_DIFFICULTY 0x0f00 #define CLIENT_D2CS_MOTDREQ 0x12 typedef struct { t_d2cs_client_header h; } t_client_d2cs_motdreq; #define D2CS_CLIENT_MOTDREPLY 0x12 typedef struct { t_d2cs_client_header h; bn_byte u1; /* message */ } t_d2cs_client_motdreply; #define CLIENT_D2CS_CANCELCREATEGAME 0x13 typedef struct { t_d2cs_client_header h; } t_client_d2cs_cancelcreategame; #define D2CS_CLIENT_CREATEGAMEWAIT 0x14 typedef struct { t_d2cs_client_header h; bn_int position; } t_d2cs_client_creategamewait; #define D2CS_CLIENT_CHARLADDERREQ 0x16 typedef struct { t_d2cs_client_header h; bn_int hardcore; bn_int expansion; /* character name */ } t_client_d2cs_charladderreq; /* use 0x11 LADDER reply for this request */ #define CLIENT_D2CS_CHARLISTREQ 0x17 typedef struct { t_d2cs_client_header h; bn_short maxchar; bn_short u1; } t_client_d2cs_charlistreq; #define D2CS_CLIENT_CHARLISTREPLY 0x17 typedef struct { t_d2cs_client_header h; bn_short maxchar; bn_short currchar; bn_short u1; /* always zero */ bn_short currchar2; /* character name */ /* character portrait blocks */ /* each block is 0x22 bytes static length */ } t_d2cs_client_charlistreply; #define CLIENT_D2CS_CONVERTCHARREQ 0x18 typedef struct { t_d2cs_client_header h; /* character name */ } t_client_d2cs_convertcharreq; #define D2CS_CLIENT_CONVERTCHARREPLY 0x18 typedef struct { t_d2cs_client_header h; bn_int reply; } t_d2cs_client_convertcharreply; #define D2CS_CLIENT_CONVERTCHARREPLY_SUCCEED 0x00 #define D2CS_CLIENT_CONVERTCHARREPLY_FAILED 0x01 #define CLIENT_D2CS_CHARLISTREQ_110 0x19 typedef struct { t_d2cs_client_header h; bn_short maxchar; bn_short u1; } t_client_d2cs_charlistreq_110; #define D2CS_CLIENT_CHARLISTREPLY_110 0x19 typedef struct { t_d2cs_client_header h; bn_short maxchar; bn_short currchar; bn_short u1; /* always zero */ bn_short currchar2; } t_d2cs_client_charlistreply_110; typedef struct { bn_int expire_time; /* character expire time (in seconds) */ /* character name */ /* character portrait blocks */ /* each block is 0x22 bytes static length */ } t_d2cs_client_chardata; } #endif pvpgn-1.99.7.2.1+dfsg/src/common/d2game_protocol.h000066400000000000000000000370761462640461400215250ustar00rootroot00000000000000/* * Copyright (C) 2000 Otto Chan (kenshin_@hotmail.com) * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2000 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2GAME_PROTOCOL_TYPES #define INCLUDED_D2GAME_PROTOCOL_TYPES #ifdef JUST_NEED_TYPES # include "bn_type.h" #else # define JUST_NEED_TYPES # include "bn_type.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { /* * The protocol for communicating between a Diablo II client * and game server. * FIXME: put the #define's into the PROTO section */ /******************************************************/ typedef struct { #ifdef NOTONLYER bn_short magic; bn_short size; bn_byte type; /* FIXME: the two sets of packets don't jive... did the beta have a different game protocol or am I just confused (could easily be the later). */ #else bn_byte type; #endif } t_d2game_header; /******************************************************/ /******************************************************/ typedef struct { t_d2game_header h; } t_d2game_generic; /******************************************************/ /******************************************************/ /* D2 00 05 00 00 */ #define SERVER_00 0x00 /* beta-only? */ typedef struct { t_d2game_header h; bn_byte unknown1; /* data request #? */ } PACKED_ATTR() t_server_00_req; #define SERVER_00 0x00 /******************************************************/ /******************************************************/ /* D2 00 20 00 01 E2 5E 13 BE 4C 02 04 CA 00 00 00 .........L...... 6C 69 6C 6A 6F 65 00 00 64 25 42 00 1C E9 50 00 liljoe..d.B...P. */ #define CLIENT_01 0x01 /* beta-only? */ typedef struct /* game select? */ { t_d2game_header h; bn_byte unknown1; /* data reply #? */ bn_int gameid1; /* same as in auth 04 reply */ bn_short gameid2; /* same as in auth 04 reply */ bn_byte unknown2[5]; /* character name */ /* 00 64 25 42 00 1C E9 50 00 unknown... string or numeric? */ } PACKED_ATTR() t_client_01; /******************************************************/ /******************************************************/ /* 0030 22 38 32 5D 00 00 97 20 20 20 20 20 "82]........ */ #define D2GAME_SERVER_WELCOME 0x97 typedef struct { t_d2game_header h; } t_d2game_server_welcome; /* 0000: 60 00 27 04 18 79 27 04 A8 00 00 00 07 02 00 00 `.'..y'......... 0010: B0 01 01 00 00 6F 6E 6C 79 65 72 00 B8 6A F7 BF .....onlyer..j.. 0020: 00 00 00 00 34 00 00 04 00 00 00 00 00 ....4........ 0000: 60 00 27 04 18 79 27 04 30 0D 00 00 07 02 00 00 `.'..y'.0....... 0010: B0 01 04 00 01 41 4C 42 45 52 54 00 B8 6A F7 BF .....ALBERT..j.. 0020: 00 00 00 00 34 00 00 04 00 00 00 00 00 ....4........ 0000: 60 00 27 04 18 79 27 04 A0 0A 00 00 07 02 00 00 `.'..y'......... 0010: B0 01 04 00 02 41 4C 42 45 52 54 00 B8 6A F7 BF .....ALBERT..j.. 0020: 00 00 00 00 34 00 00 04 00 00 00 00 00 ....4........ 0000: 60 00 AB 04 18 79 AB 04 DE 00 9F 00 0C 02 00 00 `....y.......... 0010: 60 01 04 00 00 62 62 62 00 71 DF 77 A6 C0 E6 77 `....bbb.q.w...w 0020: A6 C0 E6 77 34 05 00 04 00 00 00 00 00 ...w4........ */ #define D2GAME_CLIENT_CREATEGAMEREQ 0x60 typedef bn_basic bn_charname[16]; typedef struct { t_d2game_header h; char gamename[16]; bn_byte servertype; /* servertype=0, data not send to client and have no host */ /* creator changed to newbie data saved in server machine*/ /* servertype=1, data send to client and have host */ /* servertype=2, data send to client and have no host */ /* servertype=3, client all newbie,seems to be wrong */ /* data is saved in server machine */ /* client is loaded from server machine */ bn_byte chclass; bn_byte chtemplate; /* character template */ /* affect the reply in 0x01 */ /* should less than the lines of excel/CharTemplate.txt */ bn_byte difficulty; bn_charname charname; bn_short arena; bn_int gameflag; bn_byte unknownb2; /* unused */ bn_byte unknownb3; /* unused */ } t_d2game_client_creategamereq; typedef struct { bn_byte flag1; /* "test gameflag,06" should not be zero,or will be bad gameflag */ /* flag1 should be set with bit 0x04 or will fail or crash */ /* bit 0x10,0x20,0x08 is ignored in reply */ bn_byte flag2; /* hardcore and softcore */ /* 0x0 means softcore,0x1 template mode,0x8 means hardcore */ /* bit 0x02,0x04,0xF0 is ignored in reply */ /* have sth to do with char template */ /* opengame */ bn_byte flag3; /* guild data (not in reply) */ /* 0x01 means have guild */ /* others bits seems all unused */ bn_byte flag4; /* seems to be unused */ } t_d2game_gameflag; /* 0030 22 37 86 D7 00 00 61 65 00 00 00 00 00 01 03 00 "7....ae........ 0040 00 00 6F 6E 6C 79 65 72 2D 63 6E 61 61 00 24 E0 ..onlyer-cnaa.$. 0050 7B 05 {. */ #define D2GAME_CLIENT_JOINGAMEREQ 0x61 typedef struct { t_d2game_header h; bn_int token; bn_short gameid; bn_byte charclass; /* 00=Amazon 01=Sor 02=Nec 03=Pal 04=Bar */ bn_int version; bn_charname charname; /* 16 bytes playe name (including 0x0 ending) */ } t_d2game_client_joingamereq; /* 0030 22 1C C9 65 00 00 20 20 20 20 20 20 "..e........ */ #define D2GAME_SERVER_NOOP 0x20 typedef struct { t_d2game_header h; /* 5 \x20 noop */ } t_d2game_server_noop; /* 0030 22 37 17 07 00 00 66 91 4B A1 00 00 00 00 00 "7....f.K...... */ #define D2GAME_CLIENT_UNKNOWN_66 0x66 /* echo message? */ typedef struct { t_d2game_header h; bn_int unknown1; bn_int unknown2; } t_d2game_client_unknown_66; /* 0000: 8F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0020: 00 . */ #define D2GAME_SERVER_UNKNOWN_8F 0x8f /* echo reply? */ typedef struct { t_d2game_header h; bn_int unknown1; bn_int unknown2; bn_int unknown3; bn_int unknown4; bn_int unknown5; bn_int unknown6; bn_int unknown7; /* all here for open char is zero */ } t_d2game_server_unknown_8f; /* 0030 22 1C C4 53 00 00 01 00 04 00 00 00 00 02 "..S.......... */ #define D2GAME_SERVER_JOINOK 0x01 /* this message will appear after * 1. CLIENT_CREATEGAEM valid * 2. CLIENT_JOINGAME valid */ typedef struct { t_d2game_header h; bn_byte difficulty; bn_short gameflag; bn_byte chtemplate; bn_short unknown1; bn_short unknown2; } t_d2game_server_joinok; /* 0030 22 2F 65 38 00 00 64 "/e8..d */ #define D2GAME_CLIENT_JOINACTREQ 0x64 typedef struct { t_d2game_header h; /* none */ } t_d2game_client_joinactreq; #define D2GAME_SERVER_UNKNOWN_59 0x59 typedef struct { t_d2game_header h; } t_d2game_server_unknown_59; /* 0030 22 0E 0C 01 00 00 65 82 82 00 00 00 55 AA 55 AA ".....e.....U.U. 0040 47 00 00 00 6F 6E 6C 79 65 72 2D 63 6E 61 00 00 G...onlyer-cna.. 0050 00 00 00 00 01 00 00 00 DD 00 10 00 82 00 01 00 ................ 0060 01 00 01 01 01 01 01 FF FF FF 01 01 FF FF FF FF ................ 0070 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ 0080 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC ............... */ #define D2GAME_CLIENT_PLAYERSAVE 0x65 typedef struct { t_d2game_header h; bn_byte size; bn_int total_size; /* player save file */ /* an append */ } t_d2game_client_playersave; #define D2GAME_SERVER_ERROR 0x9c typedef struct { t_d2game_header h; bn_int errorno; } t_d2game_server_error; #define D2GAME_SERVER_ERROR_UNKNOWN_FAILURE 0 /* error biger than 22 is also unknown */ #define D2GAME_SERVER_ERROR_CHAR_VER 1 #define D2GAME_SERVER_ERROR_QUEST_DATA 2 #define D2GAME_SERVER_ERROR_WP_DATA 3 #define D2GAME_SERVER_ERROR_STAT_DATA 4 #define D2GAME_SERVER_ERROR_SKILL_DATA 5 #define D2GAME_SERVER_ERROR_UNABLE_ENTER 6 #define D2GAME_SERVER_ERROR_INVENTORY_DATA 7 #define D2GAME_SERVER_ERROR_DEAD_BODY 8 #define D2GAME_SERVER_ERROR_HEADER 9 #define D2GAME_SERVER_ERROR_HIREABLES 10 #define D2GAME_SERVER_ERROR_INTRO_DATA 11 #define D2GAME_SERVER_ERROR_ITEM 12 #define D2GAME_SERVER_ERROR_DEAD_BODY_ITEM 13 #define D2GAME_SERVER_ERROR_GENERIC_BAD_FILE 14 #define D2GAME_SERVER_ERROR_GAME_FULL 15 #define D2GAME_SERVER_ERROR_GAME_VER 16 #define D2GAME_SERVER_ERROR_NIGHTMARE 17 #define D2GAME_SERVER_ERROR_HELL 18 #define D2GAME_SERVER_ERROR_NORMAL_HARDCORE 19 #define D2GAME_SERVER_ERROR_HARDCORE_NORMAL 20 #define D2GAME_SERVER_ERROR_DEAD_HARDCORE 21 /* 0000: 15 01 00 69 20 77 69 6C 6C 20 67 6F 00 00 00 ...i will go... 0000: 15 01 00 74 68 61 6E 6B 73 20 66 6F 72 20 79 6F ...thanks for yo 0010: 75 72 20 68 65 6C 70 00 00 00 ur help... 0000: 15 01 00 73 65 65 20 75 00 00 00 ...see u... */ #define D2GAME_CLIENT_CHAT_MESSAGE 0x15 typedef struct { t_d2game_header h; bn_short unknown1; /* chat message */ } t_d2game_client_chat_message; /* 0000: 26 01 00 02 00 00 00 00 00 01 6F 6E 6C 79 65 72 &.........onlyer 0010: 2D 63 6E 61 61 00 69 20 77 69 6C 6C 20 67 6F 00 -cnaa.i will go. 0000: 26 01 00 02 00 00 00 00 00 01 6F 6E 6C 79 65 72 &.........onlyer 0010: 2D 63 6E 61 61 00 74 68 61 6E 6B 73 20 66 6F 72 -cnaa.thanks for 0020: 20 79 6F 75 72 20 68 65 6C 70 00 your help. 0000: 26 01 00 02 00 00 00 00 00 01 6F 6E 6C 79 65 72 &.........onlyer 0010: 2D 63 6E 61 61 00 73 65 65 20 75 00 -cnaa.see u. 0000: 26 01 00 02 00 00 00 00 00 17 63 63 00 62 79 65 &.........cc.bye 0010: 00 67 49 00 00 00 01 86 17 32 12 01 00 07 00 05 .gI......2...... */ #define D2GAME_SERVER_CHAT_MESSAGE 0x26 typedef struct { t_d2game_header h; bn_short unknown1; bn_int unknown2; bn_short unknown3; bn_byte unknown4; /* id or token? a fixed number for each char*/ /* player name */ /* message */ } t_d2game_server_chat_message; #define D2GAME_SERVER_CHAT_MESSAGE_UNKNOWN1 0x0001 #define D2GAME_SERVER_CHAT_MESSAGE_UNKNOWN2 0x00000002 #define D2GAME_SERVER_CHAT_MESSAGE_UNKNOWN3 0x0000 #define D2GAME_SERVER_CHAT_MESSAGE_UNKNOWN4 0x01 /* 0000: 62 b */ #define D2GAME_CLIENT_QUITGAME 0x62 typedef struct { t_d2game_header h; } t_d2game_client_quitgame; /* 0000: 9B FF 01 4E 03 00 00 55 AA 55 AA 47 00 00 00 6F ...N...U.U.G...o 0010: 6E 6C 79 65 72 2D 63 6E 61 61 00 00 00 00 00 00 nlyer-cnaa...... 0020: 00 00 00 DD 00 10 00 82 00 01 00 01 00 FF FF FF ................ 0030: FF FF 53 FF FF FF FF FF FF FF FF FF FF FF FF FF ..S............. 0040: FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 FF ................ 0050: 00 FF 00 FF 00 FF 00 FF 00 FF 00 FF 00 00 24 00 ..............$. 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0080: 00 00 00 00 00 5E 17 41 5F 57 6F 6F 21 06 00 00 .....^.A_Woo!... 0090: 00 2A 01 01 00 00 00 00 00 00 00 00 00 00 00 00 .*.............. 00A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0100: 00 00 00 00 00 00 ...... */ #define D2GAME_SERVER_PLAYERSAVE 0x9b typedef struct { t_d2game_header h; bn_byte size; bn_byte start; bn_int total_size; /* player save file */ } t_d2game_server_playersave; /* 0000: 98 05 06 */ #define D2GAME_SERVER_CLOSEGAME 0x98 typedef struct { t_d2game_header h; bn_short unknown1; } t_d2game_server_closegame; /* 0000: 5A 02 04 00 00 00 00 03 61 72 63 68 5F 6E 61 67 Z.......arch_nag 0010: 61 00 00 00 B0 FD B6 08 00 FF FF FF 78 07 39 04 a...........x.9. 0020: D5 16 2D 04 01 FD B6 08 ..-..... */ #define D2GAME_SERVER_JOINGAME_MESSAGE 0x5a typedef struct { t_d2game_header h; bn_byte unknown1; bn_byte unknown2; bn_int unknown3; bn_byte unknown4; /* char name */ } t_d2game_joingame_message; #define D2GAME_CLIENT_DIE 0x41 typedef struct { t_d2game_header h; } t_d2game_client_die; /* 0000: 02 73 00 00 00 4E 00 8B 0F 7B 14 00 00 .s...N...{... #define D2GAME_SERVER_UNKNOWN_2 0x2 */ /* 0000: 67 06 00 00 00 01 B2 0F 6B 14 01 00 07 00 05 g.......k...... 0000: 67 06 00 00 00 01 B2 0F 71 14 01 00 07 00 05 g.......q...... 0000: 67 0A 00 00 00 01 72 0F 63 14 01 00 07 00 05 g.....r.c...... #define D2GAME_SERVER_UNKNOWN_67 0x67 */ /* 0000: 6D 0A 00 00 00 76 0F 64 14 80 m....v.d.. 0000: 6D 0A 00 00 00 72 0F 63 14 80 m....r.c.. 0000: 6D 06 00 00 00 B2 0F 71 14 80 8A 01 0B 00 00 00 m......q........ 0000: 6D 06 00 00 00 B2 0F 6B 14 80 m......k.. #define D2GAME_SERVER_UNKNOWN_6D 0x6d */ /* 0000: 8A 01 0B 00 00 00 6D 0B 00 00 00 9B 0F 70 14 80 ......m......p.. 0010: 2C 01 0B 00 00 00 11 00 ,....... 0000: 8A 01 0B 00 00 00 ...... #define D2GAME_SERVER_UNKNOWN_8A 0x8a */ /* 0000: 96 59 80 CC 07 36 8A 4C 36 .Y...6.L6 #define D2GAME_SERVER_UNKNOWN_96 0x96 */ /* 0000: 24 62 00 00 00 $b... #define D2GAME_CLIENT_UNKNOWN_24 0x24 */ /* CLIENT: 0000: 2F 01 00 00 00 49 00 00 00 /....I... 0000: 31 49 00 00 00 00 00 00 00 1I....... 0000: 30 01 00 00 00 49 00 00 00 0....I... 0000: 03 8C 17 2E 12 ..... 0000: 03 87 17 27 12 ...'. command ? SERVER: */ } #endif pvpgn-1.99.7.2.1+dfsg/src/common/elist.h000066400000000000000000000156121462640461400175550ustar00rootroot00000000000000/* * Copyright (C) 2004,2006 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ELIST_TYPES #define INCLUDED_ELIST_TYPES namespace pvpgn { template class elist_node { public: elist_node() : info_(0), prev_(this), next_(this) {} ~elist_node() throw() { remove(); } void link_front(elist_node& where, T& info) { info_ = &info; where.next_->prev_ = this; next_ = where.next_; prev_ = &where; where.next_ = this; } void link_back(elist_node& where, T& info) { info_ = &info; where.prev_->next_ = this; next_ = &where; prev_ = where.prev_; where.prev_ = this; } void remove() { /* remove ourselves from any link */ prev_->next_ = next_; next_->prev_ = prev_; prev_ = next_ = this; /* poison this to find out bugs a lot earlier */ info_ = 0; } T& info() const { return *info_; } elist_node* next() const { return next_; } elist_node* prev() const { return prev_; } private: T* info_; elist_node *prev_, *next_; elist_node(const elist_node&); elist_node& operator=(const elist_node&); }; template class elist { public: class iterator { public: iterator() : ptr(0) {} iterator(const iterator& orig) :ptr(orig.ptr) {} ~iterator() throw() {} iterator& operator=(const iterator& i) { ptr = i.ptr; return *this; } bool operator==(const iterator& op) const { return ptr == op.ptr; } bool operator!=(const iterator& op) const { return ptr != op.ptr; } iterator& operator++() { ptr = ptr->next(); return *this; } iterator operator++(int) { elist_node* save = ptr; ptr = ptr->next(); return iterator(save); } T& operator*() const { return ptr->info(); } T* operator->() const { return &ptr->info(); } private: elist_node *ptr; explicit iterator(elist_node* ptr_) : ptr(ptr_) {} friend class elist; }; explicit elist(elist_node T::* node_) : node(node_) {} ~elist() throw() { clear(); } void push_back(T& obj) { (obj.*node).link_back(head, obj); } void push_front(T& obj) { (obj.*node).link_front(head, obj); } void remove(T& obj) { (obj.*node).remove(); } void remove(iterator& it) { it.ptr->remove(); } void clear() { while (!empty()) head.prev()->remove(); } bool empty() { return &head == head.next(); } T& front() const { return head.next()->info(); } T& back() const { return head.prev()->info(); } iterator begin() { return iterator(head.next()); } iterator end() { return iterator(&head); } private: /* the head of the list */ elist_node head; /* used to access the elist_node of a given T */ elist_node T::* node; elist(const elist&); elist& operator=(const elist&); }; typedef struct elist_struct { struct elist_struct *next, *prev; } t_elist; /* hlist are single linked elists */ typedef struct hlist_struct { struct hlist_struct *next; } t_hlist; } #endif /* INCLUDED_ELIST_TYPES */ #ifndef INCLUDED_ELIST_PROTOS #define INCLUDED_ELIST_PROTOS #include /* access to it's members */ #define elist_next(ptr) ((ptr)->next) #define elist_prev(ptr) ((ptr)->prev) #define __elist_init(elist,val) { (elist)->next = (elist)->prev = (val); } #define elist_init(elist) __elist_init(elist,elist) #define DECLARE_ELIST_INIT(var) \ t_elist var = { &var, &var } namespace pvpgn { /* link an new node just after "where" */ static inline void elist_add(t_elist *where, t_elist *what) { what->next = where->next; where->next->prev = what; what->prev = where; where->next = what; } /* link a new node just before "where" (usefull in creating queues) */ static inline void elist_add_tail(t_elist *where, t_elist *what) { what->prev = where->prev; where->prev->next = what; what->next = where; where->prev = what; } /* unlink "what" from it's list */ static inline void elist_del(t_elist *what) { what->next->prev = what->prev; what->prev->next = what->next; } /* finds out the container address by computing it from the list node * address substracting the offset inside the container of the list node * member */ #define elist_entry(ptr,type,member) ((type*)(((char*)ptr)-offsetof(type,member))) /* DONT remove while traversing with this ! */ #define elist_for_each(pos,head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define elist_for_each_rev(pos,head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /* safe for removals while traversing */ #define elist_for_each_safe(pos,head,save) \ for (pos = (head)->next, save = pos->next; pos != (head); \ pos = save, save = pos->next) #define elist_for_each_safe_rev(pos,head,save) \ for (pos = (head)->prev, save = pos->prev; pos != (head); \ pos = save, save = pos->prev) #define elist_empty(ptr) ((ptr)->next == (ptr)) #define hlist_next(ptr) elist_next(ptr) #define __hlist_init(hlist,val) { (hlist)->next = (val); } #define hlist_init(hlist) __hlist_init(hlist,hlist) #define DECLARE_HLIST_INIT(var) \ t_hlist var = { &var }; /* link an new node just after "where" */ static inline void hlist_add(t_hlist *where, t_hlist *what) { what->next = where->next; where->next = what; } /* unlink "what" from it's list, prev->next = what */ static inline void hlist_del(t_hlist *what, t_hlist *prev) { prev->next = what->next; } /* move "what" back in the list with one position, never NULL * prev: the previous element in the list, never NULL * prev2: the previous element of "prev", if NULL means "what" is first */ static inline void hlist_promote(t_hlist *what, t_hlist *prev, t_hlist *prev2) { if (prev2) { prev->next = what->next; prev2->next = what; what->next = prev; } } /* finds out the container address by computing it from the list node * address substracting the offset inside the container of the list node * member */ #define hlist_entry(ptr,type,member) elist_entry(ptr,type,member) /* DONT remove while traversing with this ! */ #define hlist_for_each(pos,head) elist_for_each(pos,head) /* safe for removals while traversing */ #define hlist_for_each_safe(pos,head,save) elist_for_each_safe(pos,head,save) #define hlist_empty(ptr) elist_empty(ptr) } #endif /* INCLUDED_ELIST_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/common/eventlog.cpp000066400000000000000000000217661462640461400206220ustar00rootroot00000000000000/* * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/eventlog.h" #include #include #include #include #include #include #include #include #include #include "compat/strcasecmp.h" #include "common/hexdump.h" #ifdef WIN32_GUI # include "common/gui_printf.h" #endif #include "common/setup_after.h" namespace pvpgn { static std::FILE * eventstrm = NULL; static unsigned currlevel = eventlog_level_debug | eventlog_level_info | eventlog_level_warn | eventlog_level_error | eventlog_level_fatal #ifdef WIN32GUI | eventlog_level_gui #endif ; /* FIXME: maybe this should be default for win32 */ static int eventlog_debugmode = 0; extern void eventlog_set_debugmode(int debugmode) { eventlog_debugmode = debugmode; } extern void eventlog_set(std::FILE * fp) { eventstrm = fp; } extern std::FILE * eventlog_get(void) { return eventstrm; } extern int eventlog_close(void) { std::fclose(eventstrm); return 0; } extern int eventlog_open(char const * filename) { std::FILE * temp; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(temp = std::fopen(filename, "a"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for appending (std::fopen: {})", filename, std::strerror(errno)); return -1; } if (eventstrm && eventstrm != stderr) /* close old one */ if (std::fclose(eventstrm) < 0) { eventstrm = temp; eventlog(eventlog_level_error, __FUNCTION__, "could not close previous logfile after writing (std::fclose: {})", std::strerror(errno)); return 0; } eventstrm = temp; return 0; } extern void eventlog_clear_level(void) { currlevel = eventlog_level_none; } extern int eventlog_add_level(char const * levelname) { if (!levelname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL levelname"); return -1; } if (strcasecmp(levelname, "trace") == 0) { currlevel |= eventlog_level_trace; return 0; } if (strcasecmp(levelname, "debug") == 0) { currlevel |= eventlog_level_debug; return 0; } if (strcasecmp(levelname, "info") == 0) { currlevel |= eventlog_level_info; return 0; } if (strcasecmp(levelname, "warn") == 0) { currlevel |= eventlog_level_warn; return 0; } if (strcasecmp(levelname, "error") == 0) { currlevel |= eventlog_level_error; return 0; } if (strcasecmp(levelname, "fatal") == 0) { currlevel |= eventlog_level_fatal; return 0; } #ifdef WIN32_GUI if (strcasecmp(levelname, "gui") == 0) { currlevel |= eventlog_level_gui; return 0; } #endif eventlog(eventlog_level_error, __FUNCTION__, "got bad levelname \"{}\"", levelname); return -1; } extern int eventlog_del_level(char const * levelname) { if (!levelname) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL levelname"); return -1; } if (strcasecmp(levelname, "trace") == 0) { currlevel &= ~eventlog_level_trace; return 0; } if (strcasecmp(levelname, "debug") == 0) { currlevel &= ~eventlog_level_debug; return 0; } if (strcasecmp(levelname, "info") == 0) { currlevel &= ~eventlog_level_info; return 0; } if (strcasecmp(levelname, "warn") == 0) { currlevel &= ~eventlog_level_warn; return 0; } if (strcasecmp(levelname, "error") == 0) { currlevel &= ~eventlog_level_error; return 0; } if (strcasecmp(levelname, "fatal") == 0) { currlevel &= ~eventlog_level_fatal; return 0; } #ifdef WIN32_GUI if (strcasecmp(levelname, "gui") == 0) { currlevel &= ~eventlog_level_gui; return 0; } #endif eventlog(eventlog_level_error, __FUNCTION__, "got bad levelname \"{}\"", levelname); return -1; } extern char const * eventlog_get_levelname_str(t_eventlog_level level) { switch (level) { case eventlog_level_trace: return "trace"; case eventlog_level_debug: return "debug"; case eventlog_level_info: return "info "; case eventlog_level_warn: return "warn "; case eventlog_level_error: return "error"; case eventlog_level_fatal: return "fatal"; #ifdef WIN32_GUI case eventlog_level_gui: return "gui"; #endif default: return "unknown"; } } extern void eventlog_hexdump_data(void const * data, unsigned int len) { unsigned int i; char dst[100]; unsigned char * datac; if (!data) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL data"); return; } for (i = 0, datac = (unsigned char*)data; i < len; i += 16, datac += 16) { hexdump_string(datac, (len - i < 16) ? (len - i) : 16, dst, i); std::fprintf(eventstrm, "%s\n", dst); #ifdef WIN32_GUI if (eventlog_level_gui&currlevel) gui_lvprintf(eventlog_level_info, "{}\n", dst); #endif if (eventlog_debugmode) { std::printf("%s\n", dst); } } if (eventlog_debugmode) std::fflush(stdout); std::fflush(eventstrm); } //template //extern void eventlog2(t_eventlog_level level, const char* module, const char* format, const Args& ... args) void eventlog(t_eventlog_level level, const char* module, const char* format, fmt::ArgList args) { if (!(level & currlevel)) { return; } if (!eventstrm) { return; } std::time_t now = std::time(nullptr); std::tm* tmnow = std::localtime(&now); std::string time; if (!tmnow) { time = "?"; } else { std::stringstream temp; temp << std::put_time(tmnow, EVENT_TIME_FORMAT); time = fmt::format("{}", temp.str()); } if (!module) { fmt::print(eventstrm, "{} [error] eventlog: got NULL module\n", time); #ifdef WIN32_GUI if (eventlog_level_gui & currlevel) gui_lvprintf(eventlog_level_error, "{} [error] eventlog: got NULL module\n", time); #endif std::fflush(eventstrm); return; } if (!format) { fmt::print(eventstrm, "{} [error] eventlog: got NULL fmt\n", time); #ifdef WIN32_GUI if (eventlog_level_gui&currlevel) gui_lvprintf(eventlog_level_error, "{} [error] eventlog: got NULL fmt\n", time); #endif std::fflush(eventstrm); return; } /****************************************************************/ try { fmt::print(eventstrm, "{} [{}] {}: ", time, eventlog_get_levelname_str(level), module); #ifdef WIN32_GUI if (eventlog_level_gui&currlevel) { gui_lvprintf(level, "{} [{}] {}: ", time, eventlog_get_levelname_str(level), module); } #endif fmt::print(eventstrm, format, args); #ifdef WIN32_GUI if (eventlog_level_gui & currlevel) { gui_lvprintf(level, format, args); } #endif fmt::print(eventstrm, "\n"); #ifdef WIN32_GUI if (eventlog_level_gui & currlevel) { gui_lvprintf(level, "\n"); } #endif if (eventlog_debugmode) { fmt::print("{} [{}] {}: {}\n", time, eventlog_get_levelname_str(level), module, fmt::format(format, args)); std::fflush(stdout); } } catch (const fmt::FormatError& e) { fmt::print(eventstrm, "Failed to format string ({})\n", e.what()); #ifdef WIN32_GUI if (eventlog_level_gui & currlevel) gui_lvprintf(eventlog_level_error, "Failed to format string ({})\n", e.what()); #endif } std::fflush(eventstrm); } extern void eventlog_step(char const * filename, t_eventlog_level level, char const * module, char const * fmt, ...) { std::va_list args; char time_string[EVENT_TIME_MAXLEN]; struct std::tm * tmnow; std::time_t now; std::FILE * fp; if (!(level&currlevel)) return; if (!eventstrm) return; if (!(fp = std::fopen(filename, "a"))) return; /* get the time before parsing args */ std::time(&now); if (!(tmnow = std::localtime(&now))) std::strcpy(time_string, "?"); else std::strftime(time_string, EVENT_TIME_MAXLEN, EVENT_TIME_FORMAT, tmnow); if (!module) { std::fprintf(fp, "%s [error] eventlog_step: got NULL module\n", time_string); std::fclose(fp); return; } if (!fmt) { std::fprintf(fp, "%s [error] eventlog_step: got NULL fmt\n", time_string); std::fclose(fp); return; } std::fprintf(fp, "%s [%s] %s: ", time_string, eventlog_get_levelname_str(level), module); va_start(args, fmt); std::vfprintf(fp, fmt, args); va_end(args); std::fprintf(fp, "\n"); std::fclose(fp); } } pvpgn-1.99.7.2.1+dfsg/src/common/eventlog.h000066400000000000000000000077031462640461400202620ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_EVENTLOG_TYPES #define INCLUDED_EVENTLOG_TYPES namespace pvpgn { typedef enum { eventlog_level_none = 0, eventlog_level_trace = 1, eventlog_level_debug = 2, eventlog_level_info = 4, eventlog_level_warn = 8, eventlog_level_error = 16, eventlog_level_fatal = 32 #ifdef WIN32_GUI , eventlog_level_gui = 64 #endif } t_eventlog_level; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_EVENTLOG_PROTOS #define INCLUDED_EVENTLOG_PROTOS #include #include namespace pvpgn { extern void eventlog_set_debugmode(int debugmode); extern void eventlog_set(std::FILE * fp); extern std::FILE * eventlog_get(void); extern int eventlog_open(char const * filename); extern int eventlog_close(void); extern void eventlog_clear_level(void); extern int eventlog_add_level(char const * levelname); extern int eventlog_del_level(char const * levelname); extern char const * eventlog_get_levelname_str(t_eventlog_level level); extern void eventlog_hexdump_data(void const * data, unsigned int len); void eventlog(t_eventlog_level level, const char* module, const char* format, fmt::ArgList args); FMT_VARIADIC(void, eventlog, t_eventlog_level, const char*, const char*) //template // extern void eventlog2(t_eventlog_level level, const char* module, const char* format, const Args& ... args); extern void eventlog_step(char const * filename, t_eventlog_level level, char const * module, char const * fmt, ...) PRINTF_ATTR(4, 5); #define ERROR0(fmt) eventlog(eventlog_level_error,__FUNCTION__,fmt) #define ERROR1(fmt,arg1) eventlog(eventlog_level_error,__FUNCTION__,fmt,arg1) #define ERROR2(fmt,arg1,arg2) eventlog(eventlog_level_error,__FUNCTION__,fmt,arg1,arg2) #define ERROR3(fmt,arg1,arg2,arg3) eventlog(eventlog_level_error,__FUNCTION__,fmt,arg1,arg2,arg3) #define WARN0(fmt) eventlog(eventlog_level_warn,__FUNCTION__,fmt) #define WARN1(fmt,arg1) eventlog(eventlog_level_warn,__FUNCTION__,fmt,arg1) #define WARN2(fmt,arg1,arg2) eventlog(eventlog_level_warn,__FUNCTION__,fmt,arg1,arg2) #define WARN3(fmt,arg1,arg2,arg3) eventlog(eventlog_level_warn,__FUNCTION__,fmt,arg1,arg2,arg3) #define INFO0(fmt) eventlog(eventlog_level_info,__FUNCTION__,fmt) #define INFO1(fmt,arg1) eventlog(eventlog_level_info,__FUNCTION__,fmt,arg1) #define INFO2(fmt,arg1,arg2) eventlog(eventlog_level_info,__FUNCTION__,fmt,arg1,arg2) #define INFO3(fmt,arg1,arg2,arg3) eventlog(eventlog_level_info,__FUNCTION__,fmt,arg1,arg2,arg3) #define DEBUG0(fmt) eventlog(eventlog_level_debug,__FUNCTION__,fmt) #define DEBUG1(fmt,arg1) eventlog(eventlog_level_debug,__FUNCTION__,fmt,arg1) #define DEBUG2(fmt,arg1,arg2) eventlog(eventlog_level_debug,__FUNCTION__,fmt,arg1,arg2) #define DEBUG3(fmt,arg1,arg2,arg3) eventlog(eventlog_level_debug,__FUNCTION__,fmt,arg1,arg2,arg3) #define TRACE0(fmt) eventlog(eventlog_level_trace,__FUNCTION__,fmt) #define TRACE1(fmt,arg1) eventlog(eventlog_level_trace,__FUNCTION__,fmt,arg1) #define TRACE2(fmt,arg1,arg2) eventlog(eventlog_level_trace,__FUNCTION__,fmt,arg1,arg2) #define TRACE3(fmt,arg1,arg2,arg3) eventlog(eventlog_level_trace,__FUNCTION__,fmt,arg1,arg2,arg3) } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch.cpp000066400000000000000000000107331462640461400204070ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "fdwatch.h" #include #include "common/eventlog.h" #include "common/rlimit.h" #include "fdwatch_select.h" #include "fdwatch_poll.h" #include "fdwatch_kqueue.h" #include "fdwatch_epoll.h" #include "fdwbackend.h" #include "common/setup_after.h" namespace pvpgn { unsigned fdw_maxcons; t_fdwatch_fd *fdw_fds = NULL; namespace { FDWBackend * fdw = NULL; typedef elist FDWList; FDWList freelist(&t_fdwatch_fd::freelist); FDWList uselist(&t_fdwatch_fd::uselist); } extern int fdwatch_init(int maxcons) { unsigned i; int maxsys; maxsys = get_socket_limit(); if (maxsys > 0) maxcons = (maxcons < maxsys) ? maxcons : maxsys; if (maxcons < 32) { eventlog(eventlog_level_fatal, __FUNCTION__, "too few sockets available ({})", maxcons); return -1; } fdw_maxcons = maxcons; fdw_fds = new t_fdwatch_fd[fdw_maxcons]; /* add all slots to the freelist */ for (i = 0; i < fdw_maxcons; i++) freelist.push_back(fdw_fds[i]); #ifdef HAVE_EPOLL try { fdw = new FDWEpollBackend(fdw_maxcons); return 0; } catch (const FDWBackend::InitError&) { } #endif #ifdef HAVE_KQUEUE try { fdw = new FDWKqueueBackend(fdw_maxcons); return 0; } catch (const FDWBackend::InitError&) { } #endif #ifdef HAVE_POLL try { fdw = new FDWPollBackend(fdw_maxcons); return 0; } catch (const FDWBackend::InitError&) { } #endif #ifdef HAVE_SELECT try { fdw = new FDWSelectBackend(fdw_maxcons); return 0; } catch (const FDWBackend::InitError&) { } #endif eventlog(eventlog_level_fatal, __FUNCTION__, "Found no working fdwatch layer"); fdw = NULL; fdwatch_close(); return -1; } extern int fdwatch_close(void) { if (fdw) { delete fdw; fdw = NULL; } if (fdw_fds) { delete[] fdw_fds; fdw_fds = NULL; } freelist.clear(); uselist.clear(); return 0; } extern int fdwatch_add_fd(int fd, unsigned rw, fdwatch_handler h, void *data) { /* max sockets reached */ if (freelist.empty()) return -1; t_fdwatch_fd *cfd = &freelist.front(); fdw_fd(cfd) = fd; if (fdw->add(fdw_idx(cfd), rw)) return -1; /* add it to used sockets list, remove it from free list */ uselist.push_back(*cfd); freelist.remove(*cfd); fdw_rw(cfd) = rw; fdw_data(cfd) = data; fdw_hnd(cfd) = h; return fdw_idx(cfd); } extern int fdwatch_update_fd(int idx, unsigned rw) { if (idx < 0 || idx >= fdw_maxcons) { ERROR2("out of bounds idx [{}] (max: {})", idx, fdw_maxcons); return -1; } /* do not allow completly reset the access because then backend codes * can get confused */ if (!rw) { ERROR0("tried to reset rw, not allowed"); return -1; } if (!fdw_rw(fdw_fds + idx)) { ERROR0("found reseted rw"); return -1; } if (fdw->add(idx, rw)) return -1; fdw_rw(&fdw_fds[idx]) = rw; return 0; } extern int fdwatch_del_fd(int idx) { if (idx < 0 || idx >= fdw_maxcons) { ERROR2("out of bounds idx [{}] (max: {})", idx, fdw_maxcons); return -1; } t_fdwatch_fd *cfd = fdw_fds + idx; if (!fdw_rw(cfd)) { ERROR0("found reseted rw"); return -1; } fdw->del(idx); /* remove it from uselist, add it to freelist */ uselist.remove(*cfd); freelist.push_back(*cfd); fdw_fd(cfd) = 0; fdw_rw(cfd) = 0; fdw_data(cfd) = NULL; fdw_hnd(cfd) = NULL; return 0; } extern int fdwatch(long timeout_msec) { return fdw->watch(timeout_msec); } extern void fdwatch_handle(void) { fdw->handle(); } extern void fdwatch_traverse(t_fdw_cb cb, void *data) { for (FDWList::iterator it(uselist.begin()); it != uselist.end(); ++it) { if (cb(&*it, data)) break; } } } pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch.h000066400000000000000000000040741462640461400200550ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __FDWATCH_INCLUDED__ #define __FDWATCH_INCLUDED__ #include "common/elist.h" namespace pvpgn { typedef enum { fdwatch_type_none = 0, fdwatch_type_read = 1, fdwatch_type_write = 2 } t_fdwatch_type; typedef int(*fdwatch_handler)(void *data, t_fdwatch_type); struct t_fdwatch_fd { int fd; int rw; fdwatch_handler hnd; void *data; elist_node uselist; elist_node freelist; t_fdwatch_fd() :fd(0), rw(0), hnd(0), data(0), uselist(), freelist() {} }; typedef int(*t_fdw_cb)(t_fdwatch_fd *cfd, void *data); extern unsigned fdw_maxcons; extern t_fdwatch_fd *fdw_fds; #define fdw_idx(ptr) ((ptr) - fdw_fds) #define fdw_fd(ptr) ((ptr)->fd) #define fdw_rw(ptr) ((ptr)->rw) #define fdw_data(ptr) ((ptr)->data) #define fdw_hnd(ptr) ((ptr)->hnd) extern int fdwatch_init(int maxcons); extern int fdwatch_close(void); extern int fdwatch_add_fd(int fd, unsigned rw, fdwatch_handler h, void *data); extern int fdwatch_update_fd(int idx, unsigned rw); extern int fdwatch_del_fd(int idx); extern int fdwatch(long timeout_msecs); extern void fdwatch_handle(void); extern void fdwatch_traverse(t_fdw_cb cb, void *data); } #endif /* __FDWATCH_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_epoll.cpp000066400000000000000000000067361462640461400216120ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * Linux epoll(4) based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifdef HAVE_EPOLL #include "fdwatch_epoll.h" #include #include "common/eventlog.h" #include "fdwatch.h" #include "common/setup_after.h" namespace pvpgn { FDWEpollBackend::FDWEpollBackend(int nfds_) :FDWBackend(nfds_), sr(0) { if ((epfd = epoll_create(nfds)) < 0) throw InitError("failed to open epoll device"); epevents.reset(new struct epoll_event[nfds]); std::memset(epevents.get(), 0, sizeof(struct epoll_event) * nfds); INFO1("fdwatch epoll() based layer initialized (max {} sockets)", nfds); } FDWEpollBackend::~FDWEpollBackend() throw() {} int FDWEpollBackend::add(int idx, unsigned rw) { // eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {} rw: {}", fd, rw); struct epoll_event tmpev; std::memset(&tmpev, 0, sizeof(tmpev)); tmpev.events = 0; if (rw & fdwatch_type_read) tmpev.events |= EPOLLIN; if (rw & fdwatch_type_write) tmpev.events |= EPOLLOUT; int op = fdw_rw(fdw_fds + idx) ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; tmpev.data.fd = idx; if (epoll_ctl(epfd, op, fdw_fd(fdw_fds + idx), &tmpev)) { ERROR0("got error from epoll_ctl()"); return -1; } return 0; } int FDWEpollBackend::del(int idx) { // eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {}", fd); if (sr > 0) ERROR0("BUG: called while still handling sockets"); if (fdw_rw(fdw_fds + idx)) { struct epoll_event tmpev; std::memset(&tmpev, 0, sizeof(tmpev)); tmpev.events = 0; tmpev.data.fd = idx; if (epoll_ctl(epfd, EPOLL_CTL_DEL, fdw_fd(fdw_fds + idx), &tmpev)) { ERROR0("got error from epoll_ctl()"); return -1; } } return 0; } int FDWEpollBackend::watch(long timeout_msec) { return (sr = epoll_wait(epfd, epevents.get(), fdw_maxcons, timeout_msec)); } void FDWEpollBackend::handle() { // eventlog(eventlog_level_trace, __FUNCTION__, "called"); for (struct epoll_event *ev = epevents.get(); sr; sr--, ev++) { // eventlog(eventlog_level_trace, __FUNCTION__, "checking {} ident: {} read: {} write: {}", i, kqevents[i].ident, kqevents[i].filter & EVFILT_READ, kqevents[i].filter & EVFILT_WRITE); t_fdwatch_fd *cfd = fdw_fds + ev->data.fd; if (fdw_rw(cfd) & fdwatch_type_read && ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP)) if (fdw_hnd(cfd) (fdw_data(cfd), fdwatch_type_read) == -2) continue; if (fdw_rw(cfd) & fdwatch_type_write && ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) fdw_hnd(cfd) (fdw_data(cfd), fdwatch_type_write); } sr = 0; } } #endif /* HAVE_EPOLL */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_epoll.h000066400000000000000000000030221462640461400212400ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * Linux epoll(4) based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __INCLUDED_FDWATCH_EPOLL__ #define __INCLUDED_FDWATCH_EPOLL__ #ifdef HAVE_EPOLL #ifdef HAVE_SYS_EPOLL_H # include #endif #include "scoped_array.h" #include "fdwbackend.h" namespace pvpgn { class FDWEpollBackend : public FDWBackend { public: explicit FDWEpollBackend(int nfds_); ~FDWEpollBackend() throw(); int add(int idx, unsigned rw); int del(int idx); int watch(long timeout_msecs); void handle(); private: int sr; int epfd; /* events to investigate */ scoped_array epevents; }; } #endif /* HAVE_EPOLL */ #endif /* __INCLUDED_FDWATCH_EPOLL__ */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_kqueue.cpp000066400000000000000000000174431462640461400217730ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * *BSD kqueue(2) based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifdef HAVE_KQUEUE #include "fdwatch_kqueue.h" #include #include #include "common/eventlog.h" #include "fdwatch.h" #include "common/setup_after.h" namespace pvpgn { FDWKqueueBackend::FDWKqueueBackend(int nfds_) :FDWBackend(nfds_), sr(0), nochanges(0) { if ((kq = kqueue()) == -1) throw InitError("error from kqueue()"); kqevents.reset(new struct kevent[nfds]); kqchanges.reset(new struct kevent[nfds * 2]); rridx.reset(new int[nfds]); wridx.reset(new int[nfds]); std::memset(kqchanges.get(), 0, sizeof(struct kevent) * nfds); for (int i = 0; i < nfds; i++) { rridx[i] = -1; wridx[i] = -1; } INFO1("fdwatch kqueue() based layer initialized (max {} sockets)", nfds); } FDWKqueueBackend::~FDWKqueueBackend() throw() {} int FDWKqueueBackend::add(int idx, unsigned rw) { int idxr; t_fdwatch_fd *cfd = fdw_fds + idx; /* eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {} rw: {}", fd, rw); */ /* adding read event filter */ if (!(fdw_rw(cfd) & fdwatch_type_read) && rw & fdwatch_type_read) { if (rridx[idx] >= 0 && rridx[idx] < nochanges && kqchanges[rridx[idx]].ident == fdw_fd(cfd)) { idxr = rridx[idx]; /* eventlog(eventlog_level_trace, __FUNCTION__, "updating change event (read) fd on {}", ridx); */ } else { idxr = nochanges++; rridx[idx] = idxr; /* eventlog(eventlog_level_trace, __FUNCTION__, "adding new change event (read) fd on {}", ridx); */ } EV_SET(kqchanges.get() + idxr, fdw_fd(cfd), EVFILT_READ, EV_ADD, 0, 0, (void*)idx); } else if (fdw_rw(cfd) & fdwatch_type_read && !(rw & fdwatch_type_read)) { if (rridx[idx] >= 0 && rridx[idx] < nochanges && kqchanges[rridx[idx]].ident == fdw_fd(cfd)) { idxr = rridx[idx]; /* eventlog(eventlog_level_trace, __FUNCTION__, "updating change event (read) fd on {}", ridx); */ } else { idxr = nochanges++; rridx[idx] = idxr; /* eventlog(eventlog_level_trace, __FUNCTION__, "adding new change event (read) fd on {}", ridx); */ } EV_SET(kqchanges.get() + idxr, fdw_fd(cfd), EVFILT_READ, EV_DELETE, 0, 0, (void*)idx); } /* adding write event filter */ if (!(fdw_rw(cfd) & fdwatch_type_write) && rw & fdwatch_type_write) { if (wridx[idx] >= 0 && wridx[idx] < nochanges && kqchanges[wridx[idx]].ident == fdw_fd(cfd)) { idxr = wridx[idx]; /* eventlog(eventlog_level_trace, __FUNCTION__, "updating change event (write) fd on {}", ridx); */ } else { idxr = nochanges++; wridx[idx] = idxr; /* eventlog(eventlog_level_trace, __FUNCTION__, "adding new change event (write) fd on {}", ridx); */ } EV_SET(kqchanges.get() + idxr, fdw_fd(cfd), EVFILT_WRITE, EV_ADD, 0, 0, (void*)idx); } else if (fdw_rw(cfd) & fdwatch_type_write && !(rw & fdwatch_type_write)) { if (wridx[idx] >= 0 && wridx[idx] < nochanges && kqchanges[wridx[idx]].ident == fdw_fd(cfd)) { idxr = wridx[idx]; /* eventlog(eventlog_level_trace, __FUNCTION__, "updating change event (write) fd on {}", ridx); */ } else { idxr = nochanges++; wridx[idx] = idxr; /* eventlog(eventlog_level_trace, __FUNCTION__, "adding new change event (write) fd on {}", ridx); */ } EV_SET(kqchanges.get() + idxr, fdw_fd(cfd), EVFILT_WRITE, EV_DELETE, 0, 0, (void*)idx); } return 0; } int FDWKqueueBackend::del(int idx) { /* eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {}", fd); */ if (sr > 0) eventlog(eventlog_level_error, __FUNCTION__, "BUG: called while still handling sockets"); t_fdwatch_fd *cfd = fdw_fds + idx; /* the last event changes about this fd has not yet been sent to kernel */ if (fdw_rw(cfd) & fdwatch_type_read && nochanges && rridx[idx] >= 0 && rridx[idx] < nochanges && kqchanges[rridx[idx]].ident == fdw_fd(cfd)) { nochanges--; if (rridx[idx] < nochanges) { intptr_t oidx; oidx = (intptr_t)(kqchanges[nochanges].udata); if (kqchanges[nochanges].filter == EVFILT_READ && rridx[oidx] == nochanges) { /* eventlog(eventlog_level_trace, __FUNCTION__, "not last, moving {}", kqchanges[rnfds].ident); */ rridx[oidx] = rridx[idx]; std::memcpy(kqchanges.get() + rridx[idx], kqchanges.get() + nochanges, sizeof(struct kevent)); } if (kqchanges[nochanges].filter == EVFILT_WRITE && wridx[oidx] == nochanges) { /* eventlog(eventlog_level_trace, __FUNCTION__, "not last, moving {}", kqchanges[rnfds].ident); */ wridx[oidx] = rridx[idx]; std::memcpy(kqchanges.get() + rridx[idx], kqchanges.get() + nochanges, sizeof(struct kevent)); } } rridx[idx] = -1; } if (fdw_rw(cfd) & fdwatch_type_write && nochanges && wridx[idx] >= 0 && wridx[idx] < nochanges && kqchanges[wridx[idx]].ident == fdw_fd(cfd)) { nochanges--; if (wridx[idx] < nochanges) { intptr_t oidx; oidx = (intptr_t)(kqchanges[nochanges].udata); if (kqchanges[nochanges].filter == EVFILT_READ && rridx[oidx] == nochanges) { /* eventlog(eventlog_level_trace, __FUNCTION__, "not last, moving {}", kqchanges[rnfds].ident); */ rridx[oidx] = wridx[idx]; std::memcpy(kqchanges.get() + wridx[idx], kqchanges.get() + nochanges, sizeof(struct kevent)); } if (kqchanges[nochanges].filter == EVFILT_WRITE && wridx[oidx] == nochanges) { /* eventlog(eventlog_level_trace, __FUNCTION__, "not last, moving {}, kqchanges[rnfds].ident); */ wridx[oidx] = wridx[idx]; std::memcpy(kqchanges.get() + wridx[idx], kqchanges.get() + nochanges, sizeof(struct kevent)); } } wridx[idx] = -1; } /* here we presume the calling code does close() on the socket and if so * it is automatically removed from any kernel kqueues */ return 0; } int FDWKqueueBackend::watch(long timeout_msec) { static struct timespec ts; ts.tv_sec = timeout_msec / 1000L; ts.tv_nsec = (timeout_msec % 1000L) * 1000000L; sr = kevent(kq, nochanges > 0 ? kqchanges.get() : NULL, nochanges, kqevents.get(), fdw_maxcons, &ts); nochanges = 0; return sr; } void FDWKqueueBackend::handle() { /* eventlog(eventlog_level_trace, __FUNCTION__, "called"); */ for (unsigned i = 0; i < sr; i++) { /* eventlog(eventlog_level_trace, __FUNCTION__, "checking {} ident: {} read: {} write: {}", i, kqevents[i].ident, kqevents[i].filter & EVFILT_READ, kqevents[i].filter & EVFILT_WRITE); */ t_fdwatch_fd *cfd = fdw_fds + (intptr_t)kqevents[i].udata; if (fdw_rw(cfd) & fdwatch_type_read && kqevents[i].filter == EVFILT_READ) if (fdw_hnd(cfd) (fdw_data(cfd), fdwatch_type_read) == -2) continue; if (fdw_rw(cfd) & fdwatch_type_write && kqevents[i].filter == EVFILT_WRITE) fdw_hnd(cfd) (fdw_data(cfd), fdwatch_type_write); } sr = 0; } } #endif /* HAVE_KQUEUE */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_kqueue.h000066400000000000000000000035201462640461400214270ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * *BSD kqueue() based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __INCLUDED_FDWATCH_KQUEUE__ #define __INCLUDED_FDWATCH_KQUEUE__ #ifdef HAVE_KQUEUE #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_EVENT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include "scoped_array.h" #include "fdwbackend.h" namespace pvpgn { class FDWKqueueBackend : public FDWBackend { public: explicit FDWKqueueBackend(int nfds_); ~FDWKqueueBackend() throw(); int add(int idx, unsigned rw); int del(int idx); int watch(long timeout_msecs); void handle(); private: int sr; int kq; /* changes to make to kqueue */ scoped_array kqchanges; /* events to investigate */ scoped_array kqevents; /* r/w indices from idx to the kqchanges index where the change is stored */ scoped_array rridx, wridx; unsigned nochanges; }; } #endif /* HAVE_KQUEUE */ #endif /* __INCLUDED_FDWATCH_KQUEUE__ */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_poll.cpp000066400000000000000000000074131462640461400214360ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * poll(2) based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "fdwatch_poll.h" #include #include "common/eventlog.h" #include "fdwatch.h" #include "common/setup_after.h" #ifdef HAVE_POLL namespace pvpgn { FDWPollBackend::FDWPollBackend(int nfds_) :FDWBackend(nfds_), sr(0), fds(new struct pollfd[nfds]), rridx(new int[nfds]), ridx(new int[nfds]), nofds(0) { std::memset(fds.get(), 0, sizeof(struct pollfd) * nfds); std::memset(rridx.get(), 0, sizeof(int)* nfds); /* I would use a memset with 255 but that is dirty and doesnt gain us anything */ for (int i = 0; i < nfds; i++) ridx[i] = -1; INFO1("fdwatch poll() based layer initialized (max {} sockets)", nfds); } FDWPollBackend::~FDWPollBackend() throw() {} int FDWPollBackend::add(int idx, unsigned rw) { int idxr; // eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {} rw: {}", fd, rw); if (ridx[idx] < 0) { idxr = nofds++; fds[idxr].fd = fdw_fd(fdw_fds + idx); ridx[idx] = idxr; rridx[idxr] = idx; // eventlog(eventlog_level_trace, __FUNCTION__, "adding new fd on {}", ridx); } else { if (fds[ridx[idx]].fd != fdw_fd(fdw_fds + idx)) { ERROR0("BUG: found existent poll_fd entry for same idx with different fd"); return -1; } idxr = ridx[idx]; // eventlog(eventlog_level_trace, __FUNCTION__, "updating fd on {}", ridx); } fds[idxr].events = 0; if (rw & fdwatch_type_read) fds[idxr].events |= POLLIN; if (rw & fdwatch_type_write) fds[idxr].events |= POLLOUT; return 0; } int FDWPollBackend::del(int idx) { // eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {}", fd); if (ridx[idx] < 0 || !nofds) return -1; if (sr > 0) ERROR0("BUG: called while still handling sockets"); /* move the last entry to the deleted one and decrement nofds count */ nofds--; if (ridx[idx] < nofds) { // eventlog(eventlog_level_trace, __FUNCTION__, "not last, moving {}", tfds[nofds].fd); ridx[rridx[nofds]] = ridx[idx]; rridx[ridx[idx]] = rridx[nofds]; std::memcpy(fds.get() + ridx[idx], fds.get() + nofds, sizeof(struct pollfd)); } ridx[idx] = -1; return 0; } int FDWPollBackend::watch(long timeout_msec) { return (sr = poll(fds.get(), nofds, timeout_msec)); } void FDWPollBackend::handle() { for (unsigned i = 0; i < nofds && sr; i++) { bool changed = false; t_fdwatch_fd *cfd = fdw_fds + rridx[i]; if (fdw_rw(cfd) & fdwatch_type_read && fds[i].revents & (POLLIN | POLLERR | POLLHUP | POLLNVAL)) { if (fdw_hnd(cfd)(fdw_data(cfd), fdwatch_type_read) == -2) { sr--; continue; } changed = true; } if (fdw_rw(cfd) & fdwatch_type_write && fds[i].revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) { fdw_hnd(cfd)(fdw_data(cfd), fdwatch_type_write); changed = true; } if (changed) sr--; } } } #endif /* HAVE_POLL */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_poll.h000066400000000000000000000031341462640461400210770ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * poll() based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __INCLUDED_FDWATCH_POLL__ #define __INCLUDED_FDWATCH_POLL__ #ifdef HAVE_POLL #ifdef HAVE_POLL_H # include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #include "scoped_array.h" #include "fdwbackend.h" namespace pvpgn { class FDWPollBackend : public FDWBackend { public: explicit FDWPollBackend(int nfds_); ~FDWPollBackend() throw(); int add(int idx, unsigned rw); int del(int idx); int watch(long timeout_msecs); void handle(); private: int sr; scoped_array fds; /* working set */ scoped_array rridx; scoped_array ridx; unsigned nofds; }; } #endif /* HAVE_POLL */ #endif /* __INCLUDED_FDWATCH_POLL__ */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_select.cpp000066400000000000000000000072501462640461400217460ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * select() based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define FDWATCH_BACKEND #include "common/setup_before.h" #include "fdwatch_select.h" #include #include "common/eventlog.h" #include "common/setup_after.h" #ifdef HAVE_SELECT namespace pvpgn { FDWSelectBackend::FDWSelectBackend(int nfds_) :FDWBackend(nfds_), sr(0), smaxfd(0) { /* this should not happen */ if (nfds > FD_SETSIZE) throw InitError("nfds over FD_SETSIZE"); rfds.reset(new t_psock_fd_set); wfds.reset(new t_psock_fd_set); trfds.reset(new t_psock_fd_set); twfds.reset(new t_psock_fd_set); PSOCK_FD_ZERO(trfds.get()); PSOCK_FD_ZERO(twfds.get()); INFO1("fdwatch select() based layer initialized (max {} sockets)", nfds); } FDWSelectBackend::~FDWSelectBackend() throw() {} int FDWSelectBackend::add(int idx, unsigned rw) { // eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {} rw: {}", fd, rw); int fd = fdw_fd(fdw_fds + idx); /* select() interface is limited by FD_SETSIZE max socket value */ if (fd >= FD_SETSIZE) return -1; if (rw & fdwatch_type_read) PSOCK_FD_SET(fd, trfds.get()); else PSOCK_FD_CLR(fd, trfds.get()); if (rw & fdwatch_type_write) PSOCK_FD_SET(fd, twfds.get()); else PSOCK_FD_CLR(fd, twfds.get()); if (smaxfd < fd) smaxfd = fd; return 0; } int FDWSelectBackend::del(int idx) { int fd = fdw_fd(fdw_fds + idx); // eventlog(eventlog_level_trace, __FUNCTION__, "called fd: {}", fd); if (sr > 0) ERROR0("BUG: called while still handling sockets"); PSOCK_FD_CLR(fd, trfds.get()); PSOCK_FD_CLR(fd, twfds.get()); return 0; } int FDWSelectBackend::watch(long timeout_msec) { static struct timeval tv; tv.tv_sec = timeout_msec / 1000; tv.tv_usec = timeout_msec % 1000; /* set the working sets based on the templates */ std::memcpy(rfds.get(), trfds.get(), sizeof(t_psock_fd_set)); std::memcpy(wfds.get(), twfds.get(), sizeof(t_psock_fd_set)); return (sr = psock_select(smaxfd + 1, rfds.get(), wfds.get(), NULL, &tv)); } namespace { int fdw_select_cb(t_fdwatch_fd *cfd, void *data) { FDWSelectBackend *obj = static_cast(data); return obj->cb(cfd); } } int FDWSelectBackend::cb(t_fdwatch_fd* cfd) { // eventlog(eventlog_level_trace, __FUNCTION__, "idx: {} fd: {}", idx, fdw_fd->fd); if (fdw_rw(cfd) & fdwatch_type_read && PSOCK_FD_ISSET(fdw_fd(cfd), rfds.get()) && fdw_hnd(cfd)(fdw_data(cfd), fdwatch_type_read) == -2) return 0; if (fdw_rw(cfd) & fdwatch_type_write && PSOCK_FD_ISSET(fdw_fd(cfd), wfds.get())) fdw_hnd(cfd)(fdw_data(cfd), fdwatch_type_write); return 0; } void FDWSelectBackend::handle() { // eventlog(eventlog_level_trace, __FUNCTION__, "called nofds: {}", fdw_nofds); fdwatch_traverse(fdw_select_cb, this); sr = 0; } } #endif /* HAVE_SELECT */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwatch_select.h000066400000000000000000000031331462640461400214070ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * select() based backend * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __INCLUDED_FDWATCH_SELECT__ #define __INCLUDED_FDWATCH_SELECT__ #ifdef HAVE_SELECT #include "compat/psock.h" #include "common/scoped_ptr.h" #include "fdwatch.h" #include "fdwbackend.h" namespace pvpgn { class FDWSelectBackend : public FDWBackend { public: explicit FDWSelectBackend(int nfds_); ~FDWSelectBackend() throw(); int add(int idx, unsigned rw); int del(int idx); int watch(long timeout_msecs); void handle(); int cb(t_fdwatch_fd* cfd); private: int sr, smaxfd; scoped_ptr rfds, wfds, /* working sets (updated often) */ trfds, twfds; /* templates (updated rare) */ }; } #endif /* HAVE_SELECT */ #endif /* __INCLUDED_FDWATCH_SELECT__ */ pvpgn-1.99.7.2.1+dfsg/src/common/fdwbackend.cpp000066400000000000000000000021131462640461400210500ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "fdwbackend.h" #include "common/setup_after.h" namespace pvpgn { FDWBackend::FDWBackend(int nfds_) :nfds(nfds_) {} FDWBackend::~FDWBackend() throw() {} } pvpgn-1.99.7.2.1+dfsg/src/common/fdwbackend.h000066400000000000000000000027641462640461400205310ustar00rootroot00000000000000/* * Abstraction API/layer for the various ways PvPGN can inspect sockets state * 2003 (C) * * Code is based on the ideas found in thttpd project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __PVPGN_FDWBACKEND_INCLUDED__ #define __PVPGN_FDWBACKEND_INCLUDED__ #include #include namespace pvpgn { class FDWBackend { public: class InitError :public std::runtime_error { public: explicit InitError(const std::string& str) :std::runtime_error(str) {} ~InitError() throw() {} }; explicit FDWBackend(int nfds_); virtual ~FDWBackend() throw(); virtual int add(int idx, unsigned rw) = 0; virtual int del(int idx) = 0; virtual int watch(long timeout_msecs) = 0; virtual void handle() = 0; protected: int nfds; }; } #endif /* __PVPGN_FDWBACKEND_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/common/field_sizes.h000066400000000000000000000066431462640461400207410ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_FIELD_SIZES_TYPES #define INCLUDED_FIELD_SIZES_TYPES const unsigned MAX_PACKET_SIZE = 3072; const unsigned MAX_WOL_GAMERES_PACKET_SIZE = 65535; const unsigned MAX_NORMAL_TYPE = 0xffff; const unsigned MAX_FILE_TYPE = 0xffff; const int MAX_AUTH_TYPE = 0xff; const int MAX_GAMES_LIST = 20; const unsigned MAX_MESSAGE_LEN = 255; const int MAX_COMMAND_LEN = 32; const unsigned MAX_USERNAME_LEN = 16; /* including terminating NUL char */ const unsigned MIN_USERNAME_LEN = 2; const unsigned MAX_USERPASS_LEN = 12; /* max password length as allowed by clients */ const int UNCHECKED_NAME_STR = 32; /* including terminating NUL char */ /* (longer than USER_NAME_MAX and CHAR_NAME_LEN so that * proper error packets can be returned) */ const int CLANSHORT_NAME_MAX = 4; const unsigned CLAN_NAME_MAX = 24; /* including terminating NUL char */ const unsigned CLAN_MOTD_MAX = 224; /* including terminating NUL char */ const int MAX_CHANNELNAME_LEN = 32; /* including terminating NUL char */ const unsigned MAX_CHARNAME_LEN = 16; const unsigned MIN_CHARNAME_LEN = 2; const int MAX_GAMENAME_LEN = 32; const int MAX_GAMEPASS_LEN = 32; const int MAX_GAMEINFO_LEN = 128; const int MAX_GAMEDESC_LEN = 32; const int MAP_NAME_LEN = 64; const int BNETHASH_LEN = 20; /* uint32*5, see bnethash.h */ const int MAX_EXEINFO_STR = 256; /* including terminating NUL char */ const int MAX_OWNER_STR = 128; /* including terminating NUL char */ const int MAX_CDKEY_STR = 128; /* including terminating NUL char */ const int MAX_EMAIL_STR = 128; /* including terminating NUL char */ const int MAX_WINHOST_STR = 128; /* including terminating NUL char */ const int MAX_WINUSER_STR = 128; /* including terminating NUL char */ const int MAX_LANG_STR = 64; /* including terminating NUL char */ const int MAX_COUNTRYNAME_STR = 128; /* including terminating NUL char */ const int MAX_FILENAME_STR = 2048; /* including terminating NUL char */ const int MAX_GAMEREP_HEAD_STR = 2048; /* including terminating NUL char */ const int MAX_GAMEREP_BODY_STR = 8192; /* including terminating NUL char */ const int MAX_PLAYERINFO_STR = 2048; /* including terminating NUL char */ const int MAX_COUNTRYCODE_STR = 32; /* including terminating NUL char */ const int MAX_COUNTRY_STR = 32; /* including terminating NUL char */ const int MAX_ATTRKEY_STR = 1024; /* including terminating NUL char */ const int MAX_ATTRVAL_STR = 4096; /* including terminating NUL char */ const unsigned MAX_IRC_MESSAGE_LEN = 512; /* including CRLF (according to RFC 2812) */ const unsigned MAX_TOPIC_LEN = 201; /* including terminating NUL char */ const int MAX_REALMNAME_LEN = 32; const int MAX_WOLV1_CHANNELNAME_LEN = 18; /* including '#' char */ #endif pvpgn-1.99.7.2.1+dfsg/src/common/file_protocol.h000066400000000000000000000113351462640461400212730ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_FILE_PROTOCOL_TYPES #define INCLUDED_FILE_PROTOCOL_TYPES #ifdef JUST_NEED_TYPES # include "common/bn_type.h" #else # define JUST_NEED_TYPES # include "common/bn_type.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { /******************************************************/ typedef struct { bn_short size; bn_short type; } PACKED_ATTR() t_file_header; /******************************************************/ /******************************************************/ typedef struct { t_file_header h; } PACKED_ATTR() t_file_generic; /******************************************************/ /******************************************************/ /* CLIENT FILE MPQ REQ 2D 00 00 01 36 38 58 49 -...68XI 50 58 45 53 00 00 00 00 00 00 00 00 00 00 00 00 PXES............ 00 2C 58 E1 09 28 BC 01 49 58 38 36 76 65 72 31 .,X..(..IX86ver1 2E 6D 70 71 00 .mpq. CLIENT FILE TOS REQ 28 00 00 01 36 38 58 49 (...68XI 50 58 45 53 00 00 00 00 00 00 00 00 00 00 00 00 PXES............ 30 C3 89 86 09 4F BD 01 74 6F 73 2E 74 78 74 00 0....O..tos.txt. CLIENT FILE AD REQ 2D 00 00 01 36 38 58 49 -...68XI 50 58 45 53 2B 51 02 00 2E 70 63 78 00 00 00 00 PXES+Q...pcx.... 00 00 00 00 58 01 B2 00 61 64 30 32 35 31 32 62 ....X...ad02512b 2E 70 63 78 00 .pcx. */ #define CLIENT_FILE_REQ 0x0100 typedef struct { t_file_header h; bn_int archtag; bn_int clienttag; bn_int adid; bn_int extensiontag; /* unlike other tags, this one is "forward" */ bn_int startoffset; /* is this actually used in the original clients? */ bn_long timestamp; /* filename */ } PACKED_ATTR() t_client_file_req; /******************************************************/ /* the next 2 file requests appeared on w3 first. first it sends filereq2 then server replies with some rnadom int then client sends filereq3 (which is a raw packet with no valuable header) then filename then server replies with normal file_reply and sends the contents 12: recv class=file[0x03] type=unknown[0x0200] length=20 0000: 14 00 00 02 36 38 58 49 33 52 41 57 00 00 00 00 ....68XI3RAW.... 0010: 00 00 00 00 .... */ #define CLIENT_FILE_REQ2 0x0200 typedef struct { t_file_header h; bn_int archtag; bn_int clienttag; bn_long unknown1; } PACKED_ATTR() t_client_file_req2; #define CLIENT_FILE_REQ3 0x0000 typedef struct { bn_int unknown1; bn_long timestamp; bn_long unknown2; bn_long unknown3; bn_long unknown4; bn_long unknown5; bn_long unknown6; /* filename */ } PACKED_ATTR() t_client_file_req3; /******************************************************/ /* SERVER FILE MPQ REPLY 25 00 00 00 33 1B 00 00 %...3... 00 00 00 00 00 00 00 00 00 2C 58 E1 09 28 BC 01 .........,X..(.. 49 58 38 36 76 65 72 31 2E 6D 70 71 00 IX86ver1.mpq. SERVER FILE TOS REPLY 20 00 00 00 E4 00 00 00 P."..... ....... 00 00 00 00 00 00 00 00 30 C3 89 86 09 4F BD 01 ........0....O.. 74 6F 73 2E 74 78 74 00 tos.txt. SERVER FILE AD REPLY 25 00 00 00 30 2E 00 00 %...0... 2B 51 02 00 2E 70 63 78 00 00 00 00 58 01 B2 00 +Q...pcx....X... 61 64 30 32 35 31 32 62 2E 70 63 78 00 ad02512b.pcx. */ #define SERVER_FILE_REPLY 0x0000 typedef struct { t_file_header h; bn_int filelen; bn_int adid; bn_int extensiontag; /* unlike other tags, this one is "forward" */ bn_long timestamp; /* filename */ } PACKED_ATTR() t_server_file_reply; /******************************************************/ #define SERVER_FILE_UNKNOWN1 0xdeadbeef typedef struct { bn_int unknown; } PACKED_ATTR() t_server_file_unknown1; } #endif pvpgn-1.99.7.2.1+dfsg/src/common/flags.h000066400000000000000000000017471462640461400175350ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_FLAGS_H #define INCLUDED_FLAGS_H #define FLAG_ZERO(var) *(var) = 0 #define FLAG_SET(var,flag) *(var) |= flag #define FLAG_CLEAR(var,flag) *(var) &= ~flag #define FLAG_ISSET(var,flag) (var & flag) #endif /* INCLUDED_FLAGS_H */ pvpgn-1.99.7.2.1+dfsg/src/common/format.cc000066400000000000000000000660741462640461400200730ustar00rootroot00000000000000/* Formatting library for C++ Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "format.h" #include #include #include #include #include #include #include // for std::ptrdiff_t #if defined(_WIN32) && defined(__MINGW32__) # include #endif #if FMT_USE_WINDOWS_H # if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) # include # else # define NOMINMAX # include # undef NOMINMAX # endif #endif using fmt::internal::Arg; #if FMT_EXCEPTIONS # define FMT_TRY try # define FMT_CATCH(x) catch (x) #else # define FMT_TRY if (true) # define FMT_CATCH(x) if (false) #endif #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4702) // unreachable code // Disable deprecation warning for strerror. The latter is not called but // MSVC fails to detect it. # pragma warning(disable: 4996) #endif // Dummy implementations of strerror_r and strerror_s called if corresponding // system functions are not available. static inline fmt::internal::Null<> strerror_r(int, char *, ...) { return fmt::internal::Null<>(); } static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { return fmt::internal::Null<>(); } namespace fmt { FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {} FMT_FUNC FormatError::~FormatError() throw() {} FMT_FUNC SystemError::~SystemError() throw() {} namespace { #ifndef _MSC_VER # define FMT_SNPRINTF snprintf #else // _MSC_VER inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { va_list args; va_start(args, format); int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); va_end(args); return result; } # define FMT_SNPRINTF fmt_snprintf #endif // _MSC_VER #if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) # define FMT_SWPRINTF snwprintf #else # define FMT_SWPRINTF swprintf #endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template struct IntChecker { template static bool fits_in_int(T value) { unsigned max = INT_MAX; return value <= max; } static bool fits_in_int(bool) { return true; } }; template <> struct IntChecker { template static bool fits_in_int(T value) { return value >= INT_MIN && value <= INT_MAX; } static bool fits_in_int(int) { return true; } }; const char RESET_COLOR[] = "\x1b[0m"; typedef void(*FormatFunc)(Writer &, int, StringRef); // Portable thread-safe version of strerror. // Sets buffer to point to a string describing the error code. // This can be either a pointer to a string stored in buffer, // or a pointer to some static immutable string. // Returns one of the following values: // 0 - success // ERANGE - buffer is not large enough to store the error message // other - failure // Buffer should be at least of size 1. int safe_strerror( int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); class StrError { private: int error_code_; char *&buffer_; std::size_t buffer_size_; // A noop assignment operator to avoid bogus warnings. void operator=(const StrError &) {} // Handle the result of XSI-compliant version of strerror_r. int handle(int result) { // glibc versions before 2.13 return result in errno. return result == -1 ? errno : result; } // Handle the result of GNU-specific version of strerror_r. int handle(char *message) { // If the buffer is full then the message is probably truncated. if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) return ERANGE; buffer_ = message; return 0; } // Handle the case when strerror_r is not available. int handle(internal::Null<>) { return fallback(strerror_s(buffer_, buffer_size_, error_code_)); } // Fallback to strerror_s when strerror_r is not available. int fallback(int result) { // If the buffer is full then the message is probably truncated. return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE : result; } // Fallback to strerror if strerror_r and strerror_s are not available. int fallback(internal::Null<>) { errno = 0; buffer_ = strerror(error_code_); return errno; } public: StrError(int err_code, char *&buf, std::size_t buf_size) : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} int run() { strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r. return handle(strerror_r(error_code_, buffer_, buffer_size_)); } }; return StrError(error_code, buffer, buffer_size).run(); } void format_error_code(Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { // Report error code making sure that the output fits into // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential // bad_alloc. out.clear(); static const char SEP[] = ": "; static const char ERROR_STR[] = "error "; // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; typedef internal::IntTraits::MainType MainType; MainType abs_value = static_cast(error_code); if (internal::is_negative(error_code)) { abs_value = 0 - abs_value; ++error_code_size; } error_code_size += internal::count_digits(abs_value); if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) out << message << SEP; out << ERROR_STR << error_code; assert(out.size() <= internal::INLINE_BUFFER_SIZE); } void report_error(FormatFunc func, int error_code, StringRef message) FMT_NOEXCEPT { MemoryWriter full_message; func(full_message, error_code, message); // Use Writer::data instead of Writer::c_str to avoid potential memory // allocation. std::fwrite(full_message.data(), full_message.size(), 1, stderr); std::fputc('\n', stderr); } // IsZeroInt::visit(arg) returns true iff arg is a zero integer. class IsZeroInt : public ArgVisitor { public: template bool visit_any_int(T value) { return value == 0; } }; // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. class WidthHandler : public ArgVisitor { private: FormatSpec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); public: explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} void report_unhandled_arg() { FMT_THROW(FormatError("width is not integer")); } template unsigned visit_any_int(T value) { typedef typename internal::IntTraits::MainType UnsignedType; UnsignedType width = static_cast(value); if (internal::is_negative(value)) { spec_.align_ = ALIGN_LEFT; width = 0 - width; } if (width > INT_MAX) FMT_THROW(FormatError("number is too big")); return static_cast(width); } }; class PrecisionHandler : public ArgVisitor { public: void report_unhandled_arg() { FMT_THROW(FormatError("precision is not integer")); } template int visit_any_int(T value) { if (!IntChecker::is_signed>::fits_in_int(value)) FMT_THROW(FormatError("number is too big")); return static_cast(value); } }; template struct is_same { enum { value = 0 }; }; template struct is_same { enum { value = 1 }; }; // An argument visitor that converts an integer argument to T for printf, // if T is an integral type. If T is void, the argument is converted to // corresponding signed or unsigned type depending on the type specifier: // 'd' and 'i' - signed, other - unsigned) template class ArgConverter : public ArgVisitor, void> { private: internal::Arg &arg_; wchar_t type_; FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); public: ArgConverter(internal::Arg &arg, wchar_t type) : arg_(arg), type_(type) {} void visit_bool(bool value) { if (type_ != 's') visit_any_int(value); } template void visit_any_int(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; using internal::Arg; typedef typename internal::Conditional< is_same::value, U, T>::type TargetType; if (sizeof(TargetType) <= sizeof(int)) { // Extra casts are used to silence warnings. if (is_signed) { arg_.type = Arg::INT; arg_.int_value = static_cast(static_cast(value)); } else { arg_.type = Arg::UINT; typedef typename internal::MakeUnsigned::Type Unsigned; arg_.uint_value = static_cast(static_cast(value)); } } else { if (is_signed) { arg_.type = Arg::LONG_LONG; // glibc's printf doesn't sign extend arguments of smaller types: // std::printf("%lld", -42); // prints "4294967254" // but we don't have to do the same because it's a UB. arg_.long_long_value = static_cast(value); } else { arg_.type = Arg::ULONG_LONG; arg_.ulong_long_value = static_cast::Type>(value); } } } }; // Converts an integer argument to char for printf. class CharConverter : public ArgVisitor { private: internal::Arg &arg_; FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); public: explicit CharConverter(internal::Arg &arg) : arg_(arg) {} template void visit_any_int(T value) { arg_.type = internal::Arg::CHAR; arg_.int_value = static_cast(value); } }; } // namespace namespace internal { template class PrintfArgFormatter : public ArgFormatterBase, Char> { void write_null_pointer() { this->spec().type_ = 0; this->write("(nil)"); } typedef ArgFormatterBase, Char> Base; public: PrintfArgFormatter(BasicWriter &w, FormatSpec &s) : ArgFormatterBase, Char>(w, s) {} void visit_bool(bool value) { FormatSpec &fmt_spec = this->spec(); if (fmt_spec.type_ != 's') return this->visit_any_int(value); fmt_spec.type_ = 0; this->write(value); } void visit_char(int value) { const FormatSpec &fmt_spec = this->spec(); BasicWriter &w = this->writer(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') w.write_int(value, fmt_spec); typedef typename BasicWriter::CharPtr CharPtr; CharPtr out = CharPtr(); if (fmt_spec.width_ > 1) { Char fill = ' '; out = w.grow_buffer(fmt_spec.width_); if (fmt_spec.align_ != ALIGN_LEFT) { std::fill_n(out, fmt_spec.width_ - 1, fill); out += fmt_spec.width_ - 1; } else { std::fill_n(out + 1, fmt_spec.width_ - 1, fill); } } else { out = w.grow_buffer(1); } *out = static_cast(value); } void visit_cstring(const char *value) { if (value) Base::visit_cstring(value); else if (this->spec().type_ == 'p') write_null_pointer(); else this->write("(null)"); } void visit_pointer(const void *value) { if (value) return Base::visit_pointer(value); this->spec().type_ = 0; write_null_pointer(); } void visit_custom(Arg::CustomValue c) { BasicFormatter formatter(ArgList(), this->writer()); const Char format_str[] = { '}', 0 }; const Char *format = format_str; c.format(&formatter, c.value, &format); } }; } // namespace internal } // namespace fmt FMT_FUNC void fmt::SystemError::init( int err_code, CStringRef format_str, ArgList args) { error_code_ = err_code; MemoryWriter w; internal::format_system_error(w, err_code, format(format_str, args)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } template int fmt::internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, T value) { if (width == 0) { return precision < 0 ? FMT_SNPRINTF(buffer, size, format, value) : FMT_SNPRINTF(buffer, size, format, precision, value); } return precision < 0 ? FMT_SNPRINTF(buffer, size, format, width, value) : FMT_SNPRINTF(buffer, size, format, width, precision, value); } template int fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, T value) { if (width == 0) { return precision < 0 ? FMT_SWPRINTF(buffer, size, format, value) : FMT_SWPRINTF(buffer, size, format, precision, value); } return precision < 0 ? FMT_SWPRINTF(buffer, size, format, width, value) : FMT_SWPRINTF(buffer, size, format, width, precision, value); } template const char fmt::internal::BasicData::DIGITS[] = "0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" "4041424344454647484950515253545556575859" "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; #define FMT_POWERS_OF_10(factor) \ factor * 10, \ factor * 100, \ factor * 1000, \ factor * 10000, \ factor * 100000, \ factor * 1000000, \ factor * 10000000, \ factor * 100000000, \ factor * 1000000000 template const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { 0, FMT_POWERS_OF_10(1) }; template const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), // Multiply several constants instead of using a single long long constant // to avoid warnings about C++98 not supporting long long. fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 }; FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { (void)type; if (std::isprint(static_cast(code))) { FMT_THROW(fmt::FormatError( fmt::format("unknown format code '{}' for {}", code, type))); } FMT_THROW(fmt::FormatError( fmt::format("unknown format code '\\x{:02x}' for {}", static_cast(code), type))); } #if FMT_USE_WINDOWS_H FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; if (s.size() > INT_MAX) FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); int s_size = static_cast(s.size()); int length = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0); if (length == 0) FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); buffer_.resize(length + 1); length = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); if (length == 0) FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); buffer_[length] = 0; } FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { if (int error_code = convert(s)) { FMT_THROW(WindowsError(error_code, "cannot convert string from UTF-16 to UTF-8")); } } FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; int s_size = static_cast(s.size()); int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0); if (length == 0) return GetLastError(); buffer_.resize(length + 1); length = WideCharToMultiByte( CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0); if (length == 0) return GetLastError(); buffer_[length] = 0; return 0; } FMT_FUNC void fmt::WindowsError::init( int err_code, CStringRef format_str, ArgList args) { error_code_ = err_code; MemoryWriter w; internal::format_windows_error(w, err_code, format(format_str, args)); std::runtime_error &base = *this; base = std::runtime_error(w.str()); } FMT_FUNC void fmt::internal::format_windows_error( fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT { FMT_TRY{ MemoryBuffer buffer; buffer.resize(INLINE_BUFFER_SIZE); for (;;) { wchar_t *system_message = &buffer[0]; int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, static_cast(buffer.size()), 0); if (result != 0) { UTF16ToUTF8 utf8_message; if (utf8_message.convert(system_message) == ERROR_SUCCESS) { out << message << ": " << utf8_message; return; } break; } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break; // Can't get error message, report error code instead. buffer.resize(buffer.size() * 2); } } FMT_CATCH(...) {} fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. } #endif // FMT_USE_WINDOWS_H FMT_FUNC void fmt::internal::format_system_error( fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT { FMT_TRY{ MemoryBuffer buffer; buffer.resize(INLINE_BUFFER_SIZE); for (;;) { char *system_message = &buffer[0]; int result = safe_strerror(error_code, system_message, buffer.size()); if (result == 0) { out << message << ": " << system_message; return; } if (result != ERANGE) break; // Can't get error message, report error code instead. buffer.resize(buffer.size() * 2); } } FMT_CATCH(...) {} fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. } template void fmt::internal::ArgMap::init(const ArgList &args) { if (!map_.empty()) return; typedef internal::NamedArg NamedArg; const NamedArg *named_arg = 0; bool use_values = args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; if (use_values) { for (unsigned i = 0;/*nothing*/; ++i) { internal::Arg::Type arg_type = args.type(i); switch (arg_type) { case internal::Arg::NONE: return; case internal::Arg::NAMED_ARG: named_arg = static_cast(args.values_[i].pointer); map_.push_back(Pair(named_arg->name, *named_arg)); break; default: /*nothing*/; } } return; } for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { internal::Arg::Type arg_type = args.type(i); if (arg_type == internal::Arg::NAMED_ARG) { named_arg = static_cast(args.args_[i].pointer); map_.push_back(Pair(named_arg->name, *named_arg)); } } for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { switch (args.args_[i].type) { case internal::Arg::NONE: return; case internal::Arg::NAMED_ARG: named_arg = static_cast(args.args_[i].pointer); map_.push_back(Pair(named_arg->name, *named_arg)); break; default: /*nothing*/; } } } template void fmt::internal::FixedBuffer::grow(std::size_t) { FMT_THROW(std::runtime_error("buffer overflow")); } FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( unsigned arg_index, const char *&error) { Arg arg = args_[arg_index]; switch (arg.type) { case Arg::NONE: error = "argument index out of range"; break; case Arg::NAMED_ARG: arg = *static_cast(arg.pointer); break; default: /*nothing*/; } return arg; } template void fmt::internal::PrintfFormatter::parse_flags( FormatSpec &spec, const Char *&s) { for (;;) { switch (*s++) { case '-': spec.align_ = ALIGN_LEFT; break; case '+': spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '0': spec.fill_ = '0'; break; case ' ': spec.flags_ |= SIGN_FLAG; break; case '#': spec.flags_ |= HASH_FLAG; break; default: --s; return; } } } template Arg fmt::internal::PrintfFormatter::get_arg( const Char *s, unsigned arg_index) { (void)s; const char *error = 0; Arg arg = arg_index == UINT_MAX ? next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); if (error) FMT_THROW(FormatError(!*s ? "invalid format string" : error)); return arg; } template unsigned fmt::internal::PrintfFormatter::parse_header( const Char *&s, FormatSpec &spec) { unsigned arg_index = UINT_MAX; Char c = *s; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). unsigned value = parse_nonnegative_int(s); if (*s == '$') { // value is an argument index ++s; arg_index = value; } else { if (c == '0') spec.fill_ = '0'; if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. spec.width_ = value; return arg_index; } } } parse_flags(spec, s); // Parse width. if (*s >= '0' && *s <= '9') { spec.width_ = parse_nonnegative_int(s); } else if (*s == '*') { ++s; spec.width_ = WidthHandler(spec).visit(get_arg(s)); } return arg_index; } template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, BasicCStringRef format_str) { const Char *start = format_str.c_str(); const Char *s = start; while (*s) { Char c = *s++; if (c != '%') continue; if (*s == c) { write(writer, start, s); start = ++s; continue; } write(writer, start, s - 1); FormatSpec spec; spec.align_ = ALIGN_RIGHT; // Parse argument index, flags and width. unsigned arg_index = parse_header(s, spec); // Parse precision. if (*s == '.') { ++s; if ('0' <= *s && *s <= '9') { spec.precision_ = static_cast(parse_nonnegative_int(s)); } else if (*s == '*') { ++s; spec.precision_ = PrecisionHandler().visit(get_arg(s)); } } Arg arg = get_arg(s, arg_index); if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) spec.flags_ &= ~to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { if (arg.type <= Arg::LAST_NUMERIC_TYPE) spec.align_ = ALIGN_NUMERIC; else spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. } // Parse length and convert the argument to the required type. switch (*s++) { case 'h': if (*s == 'h') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'l': if (*s == 'l') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'j': ArgConverter(arg, *s).visit(arg); break; case 'z': ArgConverter(arg, *s).visit(arg); break; case 't': ArgConverter(arg, *s).visit(arg); break; case 'L': // printf produces garbage when 'L' is omitted for long double, no // need to do the same. break; default: --s; ArgConverter(arg, *s).visit(arg); } // Parse type. if (!*s) FMT_THROW(FormatError("invalid format string")); spec.type_ = static_cast(*s++); if (arg.type <= Arg::LAST_INTEGER_TYPE) { // Normalize type. switch (spec.type_) { case 'i': case 'u': spec.type_ = 'd'; break; case 'c': // TODO: handle wchar_t CharConverter(arg).visit(arg); break; } } start = s; // Format argument. internal::PrintfArgFormatter(writer, spec).visit(arg); } write(writer, start, s); } FMT_FUNC void fmt::report_system_error( int error_code, fmt::StringRef message) FMT_NOEXCEPT { // 'fmt::' is for bcc32. fmt::report_error(internal::format_system_error, error_code, message); } #if FMT_USE_WINDOWS_H FMT_FUNC void fmt::report_windows_error( int error_code, fmt::StringRef message) FMT_NOEXCEPT { // 'fmt::' is for bcc32. fmt::report_error(internal::format_windows_error, error_code, message); } #endif FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) { MemoryWriter w; w.write(format_str, args); std::fwrite(w.data(), 1, w.size(), f); } FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) { print(stdout, format_str, args); } FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) { char escape[] = "\x1b[30m"; escape[3] = static_cast('0' + c); std::fputs(escape, stdout); print(format, args); std::fputs(RESET_COLOR, stdout); } FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); std::size_t size = w.size(); return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); } #ifndef FMT_HEADER_ONLY template struct fmt::internal::BasicData; // Explicit instantiations for char. template void fmt::internal::FixedBuffer::grow(std::size_t); template void fmt::internal::ArgMap::init(const fmt::ArgList &args); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, CStringRef format); template int fmt::internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, double value); template int fmt::internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, long double value); // Explicit instantiations for wchar_t. template void fmt::internal::FixedBuffer::grow(std::size_t); template void fmt::internal::ArgMap::init(const fmt::ArgList &args); template void fmt::internal::PrintfFormatter::format( BasicWriter &writer, WCStringRef format); template int fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, double value); template int fmt::internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, long double value); #endif // FMT_HEADER_ONLY #ifdef _MSC_VER # pragma warning(pop) #endif pvpgn-1.99.7.2.1+dfsg/src/common/format.h000066400000000000000000003437241462640461400177350ustar00rootroot00000000000000/* Formatting library for C++ Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ #include #include #include #include #include #include #include #include #include #include #include #ifdef _SECURE_SCL # define FMT_SECURE_SCL _SECURE_SCL #else # define FMT_SECURE_SCL 0 #endif #if FMT_SECURE_SCL # include #endif #ifdef _MSC_VER # define FMT_MSC_VER _MSC_VER #else # define FMT_MSC_VER 0 #endif #if FMT_MSC_VER && FMT_MSC_VER <= 1500 typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; typedef __int64 intmax_t; #else #include #endif #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) # ifdef FMT_EXPORT # define FMT_API __declspec(dllexport) # elif defined(FMT_SHARED) # define FMT_API __declspec(dllimport) # endif #endif #ifndef FMT_API # define FMT_API #endif #ifdef __GNUC__ # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # define FMT_GCC_EXTENSION __extension__ # if FMT_GCC_VERSION >= 406 # pragma GCC diagnostic push // Disable the warning about "long long" which is sometimes reported even // when using __extension__. # pragma GCC diagnostic ignored "-Wlong-long" // Disable the warning about declaration shadowing because it affects too // many valid cases. # pragma GCC diagnostic ignored "-Wshadow" // Disable the warning about implicit conversions that may change the sign of // an integer; silencing it otherwise would require many explicit casts. # pragma GCC diagnostic ignored "-Wsign-conversion" # endif # if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ # define FMT_HAS_GXX_CXX11 1 # endif #else # define FMT_GCC_EXTENSION #endif #if defined(__INTEL_COMPILER) # define FMT_ICC_VERSION __INTEL_COMPILER #elif defined(__ICL) # define FMT_ICC_VERSION __ICL #endif #if defined(__clang__) && !defined(FMT_ICC_VERSION) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdocumentation-unknown-command" # pragma clang diagnostic ignored "-Wpadded" #endif #ifdef __GNUC_LIBSTD__ # define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) #endif #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) #else # define FMT_HAS_FEATURE(x) 0 #endif #ifdef __has_builtin # define FMT_HAS_BUILTIN(x) __has_builtin(x) #else # define FMT_HAS_BUILTIN(x) 0 #endif #ifdef __has_cpp_attribute # define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) #else # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif #ifndef FMT_USE_VARIADIC_TEMPLATES // Variadic templates are available in GCC since version 4.4 // (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ // since version 2013. # define FMT_USE_VARIADIC_TEMPLATES \ (FMT_HAS_FEATURE(cxx_variadic_templates) || \ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800) #endif #ifndef FMT_USE_RVALUE_REFERENCES // Don't use rvalue references when compiling with clang and an old libstdc++ // as the latter doesn't provide std::move. # if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 # define FMT_USE_RVALUE_REFERENCES 0 # else # define FMT_USE_RVALUE_REFERENCES \ (FMT_HAS_FEATURE(cxx_rvalue_references) || \ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600) # endif #endif #if FMT_USE_RVALUE_REFERENCES # include // for std::move #endif // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 #endif #if FMT_MSC_VER && !_HAS_EXCEPTIONS # define FMT_EXCEPTIONS 0 #endif #ifndef FMT_EXCEPTIONS # define FMT_EXCEPTIONS 1 #endif #ifndef FMT_THROW # if FMT_EXCEPTIONS # define FMT_THROW(x) throw x # else # define FMT_THROW(x) assert(false) # endif #endif // Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). #ifndef FMT_USE_NOEXCEPT # define FMT_USE_NOEXCEPT 0 #endif #ifndef FMT_NOEXCEPT # if FMT_EXCEPTIONS # if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ FMT_MSC_VER >= 1900 # define FMT_NOEXCEPT noexcept # else # define FMT_NOEXCEPT throw() # endif # else # define FMT_NOEXCEPT # endif #endif #ifndef FMT_OVERRIDE # if FMT_USE_OVERRIDE || FMT_HAS_FEATURE(cxx_override) || \ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ FMT_MSC_VER >= 1900 # define FMT_OVERRIDE override # else # define FMT_OVERRIDE # endif #endif // A macro to disallow the copy constructor and operator= functions // This should be used in the private: declarations for a class #ifndef FMT_USE_DELETED_FUNCTIONS # define FMT_USE_DELETED_FUNCTIONS 0 #endif #if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 # define FMT_DELETED_OR_UNDEFINED = delete # define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&) = delete; \ TypeName& operator=(const TypeName&) = delete #else # define FMT_DELETED_OR_UNDEFINED # define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ TypeName& operator=(const TypeName&) #endif #ifndef FMT_USE_USER_DEFINED_LITERALS // All compilers which support UDLs also support variadic templates. This // makes the fmt::literals implementation easier. However, an explicit check // for variadic templates is added here just in case. // For Intel's compiler both it and the system gcc/msc must support UDLs. # define FMT_USE_USER_DEFINED_LITERALS \ FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ (FMT_HAS_FEATURE(cxx_user_literals) || \ (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) #endif #ifndef FMT_ASSERT # define FMT_ASSERT(condition, message) assert((condition) && message) #endif #if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) # define FMT_BUILTIN_CLZ(n) __builtin_clz(n) #endif #if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) # define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) #endif // Some compilers masquerade as both MSVC and GCC-likes or // otherwise support __builtin_clz and __builtin_clzll, so // only define FMT_BUILTIN_CLZ using the MSVC intrinsics // if the clz and clzll builtins are not available. #if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) # include // _BitScanReverse, _BitScanReverse64 namespace fmt { namespace internal { # pragma intrinsic(_BitScanReverse) inline uint32_t clz(uint32_t x) { unsigned long r = 0; _BitScanReverse(&r, x); assert(x != 0); // Static analysis complains about using uninitialized data // "r", but the only way that can happen is if "x" is 0, // which the callers guarantee to not happen. # pragma warning(suppress: 6102) return 31 - r; } # define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) # ifdef _WIN64 # pragma intrinsic(_BitScanReverse64) # endif inline uint32_t clzll(uint64_t x) { unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); # else // Scan the high 32 bits. if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 - (r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif assert(x != 0); // Static analysis complains about using uninitialized data // "r", but the only way that can happen is if "x" is 0, // which the callers guarantee to not happen. # pragma warning(suppress: 6102) return 63 - r; } # define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) } } #endif namespace fmt { namespace internal { struct DummyInt { int data[2]; operator int() const { return 0; } }; typedef std::numeric_limits FPUtil; // Dummy implementations of system functions such as signbit and ecvt called // if the latter are not available. inline DummyInt signbit(...) { return DummyInt(); } inline DummyInt _ecvt_s(...) { return DummyInt(); } inline DummyInt isinf(...) { return DummyInt(); } inline DummyInt _finite(...) { return DummyInt(); } inline DummyInt isnan(...) { return DummyInt(); } inline DummyInt _isnan(...) { return DummyInt(); } // A helper function to suppress bogus "conditional expression is constant" // warnings. template inline T const_check(T value) { return value; } } } // namespace fmt namespace std { // Standard permits specialization of std::numeric_limits. This specialization // is used to resolve ambiguity between isinf and std::isinf in glibc: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891 // and the same for isnan and signbit. template <> class numeric_limits : public std::numeric_limits { public: // Portable version of isinf. template static bool isinfinity(T x) { using namespace fmt::internal; // The resolution "priority" is: // isinf macro > std::isinf > ::isinf > fmt::internal::isinf if (const_check(sizeof(isinf(x)) == sizeof(bool) || sizeof(isinf(x)) == sizeof(int))) { return isinf(x) != 0; } return !_finite(static_cast(x)); } // Portable version of isnan. template static bool isnotanumber(T x) { using namespace fmt::internal; if (const_check(sizeof(isnan(x)) == sizeof(bool) || sizeof(isnan(x)) == sizeof(int))) { return isnan(x) != 0; } return _isnan(static_cast(x)) != 0; } // Portable version of signbit. static bool isnegative(double x) { using namespace fmt::internal; if (const_check(sizeof(signbit(x)) == sizeof(int))) return signbit(x) != 0; if (x < 0) return true; if (!isnotanumber(x)) return false; int dec = 0, sign = 0; char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign); return sign != 0; } }; } // namespace std namespace fmt { // Fix the warning about long long on older versions of GCC // that don't support the diagnostic pragma. FMT_GCC_EXTENSION typedef long long LongLong; FMT_GCC_EXTENSION typedef unsigned long long ULongLong; #if FMT_USE_RVALUE_REFERENCES using std::move; #endif template class BasicWriter; typedef BasicWriter Writer; typedef BasicWriter WWriter; template class ArgFormatter; template > class BasicFormatter; /** \rst A string reference. It can be constructed from a C string or ``std::string``. You can use one of the following typedefs for common character types: +------------+-------------------------+ | Type | Definition | +============+=========================+ | StringRef | BasicStringRef | +------------+-------------------------+ | WStringRef | BasicStringRef | +------------+-------------------------+ This class is most useful as a parameter type to allow passing different types of strings to a function, for example:: template std::string format(StringRef format_str, const Args & ... args); format("{}", 42); format(std::string("{}"), 42); \endrst */ template class BasicStringRef { private: const Char *data_; std::size_t size_; public: /** Constructs a string reference object from a C string and a size. */ BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} /** \rst Constructs a string reference object from a C string computing the size with ``std::char_traits::length``. \endrst */ BasicStringRef(const Char *s) : data_(s), size_(std::char_traits::length(s)) {} /** \rst Constructs a string reference from an ``std::string`` object. \endrst */ BasicStringRef(const std::basic_string &s) : data_(s.c_str()), size_(s.size()) {} /** \rst Converts a string reference to an ``std::string`` object. \endrst */ std::basic_string to_string() const { return std::basic_string(data_, size_); } /** Returns a pointer to the string data. */ const Char *data() const { return data_; } /** Returns the string size. */ std::size_t size() const { return size_; } // Lexicographically compare this string reference to other. int compare(BasicStringRef other) const { std::size_t size = size_ < other.size_ ? size_ : other.size_; int result = std::char_traits::compare(data_, other.data_, size); if (result == 0) result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); return result; } friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) == 0; } friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) != 0; } friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) < 0; } friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) <= 0; } friend bool operator>(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) > 0; } friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs) { return lhs.compare(rhs) >= 0; } }; typedef BasicStringRef StringRef; typedef BasicStringRef WStringRef; /** \rst A reference to a null terminated string. It can be constructed from a C string or ``std::string``. You can use one of the following typedefs for common character types: +-------------+--------------------------+ | Type | Definition | +=============+==========================+ | CStringRef | BasicCStringRef | +-------------+--------------------------+ | WCStringRef | BasicCStringRef | +-------------+--------------------------+ This class is most useful as a parameter type to allow passing different types of strings to a function, for example:: template std::string format(CStringRef format_str, const Args & ... args); format("{}", 42); format(std::string("{}"), 42); \endrst */ template class BasicCStringRef { private: const Char *data_; public: /** Constructs a string reference object from a C string. */ BasicCStringRef(const Char *s) : data_(s) {} /** \rst Constructs a string reference from an ``std::string`` object. \endrst */ BasicCStringRef(const std::basic_string &s) : data_(s.c_str()) {} /** Returns the pointer to a C string. */ const Char *c_str() const { return data_; } }; typedef BasicCStringRef CStringRef; typedef BasicCStringRef WCStringRef; /** A formatting error such as invalid format string. */ class FormatError : public std::runtime_error { public: explicit FormatError(CStringRef message) : std::runtime_error(message.c_str()) {} ~FormatError() throw(); }; namespace internal { // MakeUnsigned::Type gives an unsigned type corresponding to integer type T. template struct MakeUnsigned { typedef T Type; }; #define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ template <> \ struct MakeUnsigned { typedef U Type; } FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); // Casts nonnegative integer to unsigned. template inline typename MakeUnsigned::Type to_unsigned(Int value) { FMT_ASSERT(value >= 0, "negative value"); return static_cast::Type>(value); } // The number of characters to store in the MemoryBuffer object itself // to avoid dynamic memory allocation. enum { INLINE_BUFFER_SIZE = 500 }; #if FMT_SECURE_SCL // Use checked iterator to avoid warnings on MSVC. template inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { return stdext::checked_array_iterator(ptr, size); } #else template inline T *make_ptr(T *ptr, std::size_t) { return ptr; } #endif } // namespace internal /** \rst A buffer supporting a subset of ``std::vector``'s operations. \endrst */ template class Buffer { private: FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); protected: T *ptr_; std::size_t size_; std::size_t capacity_; Buffer(T *ptr = 0, std::size_t capacity = 0) : ptr_(ptr), size_(0), capacity_(capacity) {} /** \rst Increases the buffer capacity to hold at least *size* elements updating ``ptr_`` and ``capacity_``. \endrst */ virtual void grow(std::size_t size) = 0; public: virtual ~Buffer() {} /** Returns the size of this buffer. */ std::size_t size() const { return size_; } /** Returns the capacity of this buffer. */ std::size_t capacity() const { return capacity_; } /** Resizes the buffer. If T is a POD type new elements may not be initialized. */ void resize(std::size_t new_size) { if (new_size > capacity_) grow(new_size); size_ = new_size; } /** \rst Reserves space to store at least *capacity* elements. \endrst */ void reserve(std::size_t capacity) { if (capacity > capacity_) grow(capacity); } void clear() FMT_NOEXCEPT { size_ = 0; } void push_back(const T &value) { if (size_ == capacity_) grow(size_ + 1); ptr_[size_++] = value; } /** Appends data to the end of the buffer. */ template void append(const U *begin, const U *end); T &operator[](std::size_t index) { return ptr_[index]; } const T &operator[](std::size_t index) const { return ptr_[index]; } }; template template void Buffer::append(const U *begin, const U *end) { std::size_t new_size = size_ + internal::to_unsigned(end - begin); if (new_size > capacity_) grow(new_size); std::uninitialized_copy(begin, end, internal::make_ptr(ptr_, capacity_) + size_); size_ = new_size; } namespace internal { // A memory buffer for trivially copyable/constructible types with the first // SIZE elements stored in the object itself. template > class MemoryBuffer : private Allocator, public Buffer { private: T data_[SIZE]; // Deallocate memory allocated by the buffer. void deallocate() { if (this->ptr_ != data_) Allocator::deallocate(this->ptr_, this->capacity_); } protected: void grow(std::size_t size) FMT_OVERRIDE; public: explicit MemoryBuffer(const Allocator &alloc = Allocator()) : Allocator(alloc), Buffer(data_, SIZE) {} ~MemoryBuffer() { deallocate(); } #if FMT_USE_RVALUE_REFERENCES private: // Move data from other to this buffer. void move(MemoryBuffer &other) { Allocator &this_alloc = *this, &other_alloc = other; this_alloc = std::move(other_alloc); this->size_ = other.size_; this->capacity_ = other.capacity_; if (other.ptr_ == other.data_) { this->ptr_ = data_; std::uninitialized_copy(other.data_, other.data_ + this->size_, make_ptr(data_, this->capacity_)); } else { this->ptr_ = other.ptr_; // Set pointer to the inline array so that delete is not called // when deallocating. other.ptr_ = other.data_; } } public: MemoryBuffer(MemoryBuffer &&other) { move(other); } MemoryBuffer &operator=(MemoryBuffer &&other) { assert(this != &other); deallocate(); move(other); return *this; } #endif // Returns a copy of the allocator associated with this buffer. Allocator get_allocator() const { return *this; } }; template void MemoryBuffer::grow(std::size_t size) { std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; if (size > new_capacity) new_capacity = size; T *new_ptr = this->allocate(new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); std::size_t old_capacity = this->capacity_; T *old_ptr = this->ptr_; this->capacity_ = new_capacity; this->ptr_ = new_ptr; // deallocate may throw (at least in principle), but it doesn't matter since // the buffer already uses the new storage and will deallocate it in case // of exception. if (old_ptr != data_) Allocator::deallocate(old_ptr, old_capacity); } // A fixed-size buffer. template class FixedBuffer : public fmt::Buffer { public: FixedBuffer(Char *array, std::size_t size) : fmt::Buffer(array, size) {} protected: FMT_API void grow(std::size_t size); }; template class BasicCharTraits { public: #if FMT_SECURE_SCL typedef stdext::checked_array_iterator CharPtr; #else typedef Char *CharPtr; #endif static Char cast(int value) { return static_cast(value); } }; template class CharTraits; template <> class CharTraits : public BasicCharTraits { private: // Conversion from wchar_t to char is not allowed. static char convert(wchar_t); public: static char convert(char value) { return value; } // Formats a floating-point number. template FMT_API static int format_float(char *buffer, std::size_t size, const char *format, unsigned width, int precision, T value); }; template <> class CharTraits : public BasicCharTraits { public: static wchar_t convert(char value) { return value; } static wchar_t convert(wchar_t value) { return value; } template FMT_API static int format_float(wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, T value); }; // Checks if a number is negative - used to avoid warnings. template struct SignChecker { template static bool is_negative(T value) { return value < 0; } }; template <> struct SignChecker { template static bool is_negative(T) { return false; } }; // Returns true if value is negative, false otherwise. // Same as (value < 0) but doesn't produce warnings if T is an unsigned type. template inline bool is_negative(T value) { return SignChecker::is_signed>::is_negative(value); } // Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. template struct TypeSelector { typedef uint32_t Type; }; template <> struct TypeSelector { typedef uint64_t Type; }; template struct IntTraits { // Smallest of uint32_t and uint64_t that is large enough to represent // all values of T. typedef typename TypeSelector::digits <= 32>::Type MainType; }; FMT_API void report_unknown_type(char code, const char *type); // Static data is placed in this class template to allow header-only // configuration. template struct FMT_API BasicData { static const uint32_t POWERS_OF_10_32[]; static const uint64_t POWERS_OF_10_64[]; static const char DIGITS[]; }; #ifndef FMT_USE_EXTERN_TEMPLATES // Clang doesn't have a feature check for extern templates so we check // for variadic templates which were introduced in the same version. # define FMT_USE_EXTERN_TEMPLATES (__clang__ && FMT_USE_VARIADIC_TEMPLATES) #endif #if FMT_USE_EXTERN_TEMPLATES && !defined(FMT_HEADER_ONLY) extern template struct BasicData; #endif typedef BasicData<> Data; #ifdef FMT_BUILTIN_CLZLL // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. inline unsigned count_digits(uint64_t n) { // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1; } #else // Fallback version of count_digits used when __builtin_clz is not available. inline unsigned count_digits(uint64_t n) { unsigned count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. if (n < 10) return count; if (n < 100) return count + 1; if (n < 1000) return count + 2; if (n < 10000) return count + 3; n /= 10000u; count += 4; } } #endif #ifdef FMT_BUILTIN_CLZ // Optional version of count_digits for better performance on 32-bit platforms. inline unsigned count_digits(uint32_t n) { int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1; } #endif // A functor that doesn't add a thousands separator. struct NoThousandsSep { template void operator()(Char *) {} }; // A functor that adds a thousands separator. class ThousandsSep { private: fmt::StringRef sep_; // Index of a decimal digit with the least significant digit having index 0. unsigned digit_index_; public: explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {} template void operator()(Char *&buffer) { if (++digit_index_ % 3 != 0) return; buffer -= sep_.size(); std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), internal::make_ptr(buffer, sep_.size())); } }; // Formats a decimal unsigned integer value writing into buffer. // thousands_sep is a functor that is called after writing each char to // add a thousands separator if necessary. template inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, ThousandsSep thousands_sep) { buffer += num_digits; while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. unsigned index = static_cast((value % 100) * 2); value /= 100; *--buffer = Data::DIGITS[index + 1]; thousands_sep(buffer); *--buffer = Data::DIGITS[index]; thousands_sep(buffer); } if (value < 10) { *--buffer = static_cast('0' + value); return; } unsigned index = static_cast(value * 2); *--buffer = Data::DIGITS[index + 1]; thousands_sep(buffer); *--buffer = Data::DIGITS[index]; } template inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { return format_decimal(buffer, value, num_digits, NoThousandsSep()); } #ifndef _WIN32 # define FMT_USE_WINDOWS_H 0 #elif !defined(FMT_USE_WINDOWS_H) # define FMT_USE_WINDOWS_H 1 #endif // Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. // All the functionality that relies on it will be disabled too. #if FMT_USE_WINDOWS_H // A converter from UTF-8 to UTF-16. // It is only provided for Windows since other systems support UTF-8 natively. class UTF8ToUTF16 { private: MemoryBuffer buffer_; public: FMT_API explicit UTF8ToUTF16(StringRef s); operator WStringRef() const { return WStringRef(&buffer_[0], size()); } size_t size() const { return buffer_.size() - 1; } const wchar_t *c_str() const { return &buffer_[0]; } std::wstring str() const { return std::wstring(&buffer_[0], size()); } }; // A converter from UTF-16 to UTF-8. // It is only provided for Windows since other systems support UTF-8 natively. class UTF16ToUTF8 { private: MemoryBuffer buffer_; public: UTF16ToUTF8() {} FMT_API explicit UTF16ToUTF8(WStringRef s); operator StringRef() const { return StringRef(&buffer_[0], size()); } size_t size() const { return buffer_.size() - 1; } const char *c_str() const { return &buffer_[0]; } std::string str() const { return std::string(&buffer_[0], size()); } // Performs conversion returning a system error code instead of // throwing exception on conversion error. This method may still throw // in case of memory allocation error. FMT_API int convert(WStringRef s); }; FMT_API void format_windows_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; #endif FMT_API void format_system_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; // A formatting argument value. struct Value { template struct StringValue { const Char *value; std::size_t size; }; typedef void(*FormatFunc)( void *formatter, const void *arg, void *format_str_ptr); struct CustomValue { const void *value; FormatFunc format; }; union { int int_value; unsigned uint_value; LongLong long_long_value; ULongLong ulong_long_value; double double_value; long double long_double_value; const void *pointer; StringValue string; StringValue sstring; StringValue ustring; StringValue wstring; CustomValue custom; }; enum Type { NONE, NAMED_ARG, // Integer types should go first, INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, // followed by floating-point types. DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, CSTRING, STRING, WSTRING, POINTER, CUSTOM }; }; // A formatting argument. It is a trivially copyable/constructible type to // allow storage in internal::MemoryBuffer. struct Arg : Value { Type type; }; template struct NamedArg; template struct Null {}; // A helper class template to enable or disable overloads taking wide // characters and strings in MakeValue. template struct WCharHelper { typedef Null Supported; typedef T Unsupported; }; template struct WCharHelper { typedef T Supported; typedef Null Unsupported; }; typedef char Yes[1]; typedef char No[2]; template T &get(); // These are non-members to workaround an overload resolution bug in bcc32. Yes &convert(fmt::ULongLong); No &convert(...); template struct ConvertToIntImpl { enum { value = ENABLE_CONVERSION }; }; template struct ConvertToIntImpl2 { enum { value = false }; }; template struct ConvertToIntImpl2 { enum { // Don't convert numeric types. value = ConvertToIntImpl::is_specialized>::value }; }; template struct ConvertToInt { enum { enable_conversion = sizeof(convert(get())) == sizeof(Yes) }; enum { value = ConvertToIntImpl2::value }; }; #define FMT_DISABLE_CONVERSION_TO_INT(Type) \ template <> \ struct ConvertToInt { enum { value = 0 }; } // Silence warnings about convering float to int. FMT_DISABLE_CONVERSION_TO_INT(float); FMT_DISABLE_CONVERSION_TO_INT(double); FMT_DISABLE_CONVERSION_TO_INT(long double); template struct EnableIf {}; template struct EnableIf { typedef T type; }; template struct Conditional { typedef T type; }; template struct Conditional { typedef F type; }; // For bcc32 which doesn't understand ! in template arguments. template struct Not { enum { value = 0 }; }; template<> struct Not { enum { value = 1 }; }; template struct LConvCheck { LConvCheck(int) {} }; // Returns the thousands separator for the current locale. // We check if ``lconv`` contains ``thousands_sep`` because on Android // ``lconv`` is stubbed as an empty struct. template inline StringRef thousands_sep( LConv *lc, LConvCheck = 0) { return lc->thousands_sep; } inline fmt::StringRef thousands_sep(...) { return ""; } // Makes an Arg object from any type. template class MakeValue : public Arg { public: typedef typename Formatter::Char Char; private: // The following two methods are private to disallow formatting of // arbitrary pointers. If you want to output a pointer cast it to // "void *" or "const void *". In particular, this forbids formatting // of "[const] volatile char *" which is printed as bool by iostreams. // Do not implement! template MakeValue(const T *value); template MakeValue(T *value); // The following methods are private to disallow formatting of wide // characters and strings into narrow strings as in // fmt::format("{}", L"test"); // To fix this, use a wide format string: fmt::format(L"{}", L"test"). #if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) MakeValue(typename WCharHelper::Unsupported); #endif MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); void set_string(StringRef str) { string.value = str.data(); string.size = str.size(); } void set_string(WStringRef str) { wstring.value = str.data(); wstring.size = str.size(); } // Formats an argument of a custom type, such as a user-defined class. template static void format_custom_arg( void *formatter, const void *arg, void *format_str_ptr) { format(*static_cast(formatter), *static_cast(format_str_ptr), *static_cast(arg)); } public: MakeValue() {} #define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ MakeValue(Type value) { field = rhs; } \ static uint64_t type(Type) { return Arg::TYPE; } #define FMT_MAKE_VALUE(Type, field, TYPE) \ FMT_MAKE_VALUE_(Type, field, TYPE, value) FMT_MAKE_VALUE(bool, int_value, BOOL) FMT_MAKE_VALUE(short, int_value, INT) FMT_MAKE_VALUE(unsigned short, uint_value, UINT) FMT_MAKE_VALUE(int, int_value, INT) FMT_MAKE_VALUE(unsigned, uint_value, UINT) MakeValue(long value) { // To minimize the number of types we need to deal with, long is // translated either to int or to long long depending on its size. if (const_check(sizeof(long) == sizeof(int))) int_value = static_cast(value); else long_long_value = value; } static uint64_t type(long) { return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; } MakeValue(unsigned long value) { if (const_check(sizeof(unsigned long) == sizeof(unsigned))) uint_value = static_cast(value); else ulong_long_value = value; } static uint64_t type(unsigned long) { return sizeof(unsigned long) == sizeof(unsigned) ? Arg::UINT : Arg::ULONG_LONG; } FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) FMT_MAKE_VALUE(float, double_value, DOUBLE) FMT_MAKE_VALUE(double, double_value, DOUBLE) FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) FMT_MAKE_VALUE(signed char, int_value, INT) FMT_MAKE_VALUE(unsigned char, uint_value, UINT) FMT_MAKE_VALUE(char, int_value, CHAR) #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) MakeValue(typename WCharHelper::Supported value) { int_value = value; } static uint64_t type(wchar_t) { return Arg::CHAR; } #endif #define FMT_MAKE_STR_VALUE(Type, TYPE) \ MakeValue(Type value) { set_string(value); } \ static uint64_t type(Type) { return Arg::TYPE; } FMT_MAKE_VALUE(char *, string.value, CSTRING) FMT_MAKE_VALUE(const char *, string.value, CSTRING) FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) FMT_MAKE_STR_VALUE(const std::string &, STRING) FMT_MAKE_STR_VALUE(StringRef, STRING) FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) #define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ MakeValue(typename WCharHelper::Supported value) { \ set_string(value); \ } \ static uint64_t type(Type) { return Arg::TYPE; } FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) FMT_MAKE_VALUE(void *, pointer, POINTER) FMT_MAKE_VALUE(const void *, pointer, POINTER) template MakeValue(const T &value, typename EnableIf::value>::value, int>::type = 0) { custom.value = &value; custom.format = &format_custom_arg; } template MakeValue(const T &value, typename EnableIf::value, int>::type = 0) { int_value = value; } template static uint64_t type(const T &) { return ConvertToInt::value ? Arg::INT : Arg::CUSTOM; } // Additional template param `Char_` is needed here because make_type always // uses char. template MakeValue(const NamedArg &value) { pointer = &value; } template static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } }; template class MakeArg : public Arg { public: MakeArg() { type = Arg::NONE; } template MakeArg(const T &value) : Arg(MakeValue(value)) { type = static_cast(MakeValue::type(value)); } }; template struct NamedArg : Arg { BasicStringRef name; template NamedArg(BasicStringRef argname, const T &value) : Arg(MakeArg< BasicFormatter >(value)), name(argname) {} }; class RuntimeError : public std::runtime_error { protected: RuntimeError() : std::runtime_error("") {} ~RuntimeError() throw(); }; template class PrintfArgFormatter; template class ArgMap; } // namespace internal /** An argument list. */ class ArgList { private: // To reduce compiled code size per formatting function call, types of first // MAX_PACKED_ARGS arguments are passed in the types_ field. uint64_t types_; union { // If the number of arguments is less than MAX_PACKED_ARGS, the argument // values are stored in values_, otherwise they are stored in args_. // This is done to reduce compiled code size as storing larger objects // may require more code (at least on x86-64) even if the same amount of // data is actually copied to stack. It saves ~10% on the bloat test. const internal::Value *values_; const internal::Arg *args_; }; internal::Arg::Type type(unsigned index) const { unsigned shift = index * 4; uint64_t mask = 0xf; return static_cast( (types_ & (mask << shift)) >> shift); } template friend class internal::ArgMap; public: // Maximum number of arguments with packed types. enum { MAX_PACKED_ARGS = 16 }; ArgList() : types_(0) {} ArgList(ULongLong types, const internal::Value *values) : types_(types), values_(values) {} ArgList(ULongLong types, const internal::Arg *args) : types_(types), args_(args) {} /** Returns the argument at specified index. */ internal::Arg operator[](unsigned index) const { using internal::Arg; Arg arg; bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE; if (index < MAX_PACKED_ARGS) { Arg::Type arg_type = type(index); internal::Value &val = arg; if (arg_type != Arg::NONE) val = use_values ? values_[index] : args_[index]; arg.type = arg_type; return arg; } if (use_values) { // The index is greater than the number of arguments that can be stored // in values, so return a "none" argument. arg.type = Arg::NONE; return arg; } for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) { if (args_[i].type == Arg::NONE) return args_[i]; } return args_[index]; } }; #define FMT_DISPATCH(call) static_cast(this)->call /** \rst An argument visitor based on the `curiously recurring template pattern `_. To use `~fmt::ArgVisitor` define a subclass that implements some or all of the visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. Pass the subclass as the *Impl* template parameter. Then calling `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method specific to the argument type. For example, if the argument type is ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass will be called. If the subclass doesn't contain a method with this signature, then a corresponding method of `~fmt::ArgVisitor` will be called. **Example**:: class MyArgVisitor : public fmt::ArgVisitor { public: void visit_int(int value) { fmt::print("{}", value); } void visit_double(double value) { fmt::print("{}", value ); } }; \endrst */ template class ArgVisitor { private: typedef internal::Arg Arg; public: void report_unhandled_arg() {} Result visit_unhandled_arg() { FMT_DISPATCH(report_unhandled_arg()); return Result(); } /** Visits an ``int`` argument. **/ Result visit_int(int value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits a ``long long`` argument. **/ Result visit_long_long(LongLong value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits an ``unsigned`` argument. **/ Result visit_uint(unsigned value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits an ``unsigned long long`` argument. **/ Result visit_ulong_long(ULongLong value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits a ``bool`` argument. **/ Result visit_bool(bool value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits a ``char`` or ``wchar_t`` argument. **/ Result visit_char(int value) { return FMT_DISPATCH(visit_any_int(value)); } /** Visits an argument of any integral type. **/ template Result visit_any_int(T) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a ``double`` argument. **/ Result visit_double(double value) { return FMT_DISPATCH(visit_any_double(value)); } /** Visits a ``long double`` argument. **/ Result visit_long_double(long double value) { return FMT_DISPATCH(visit_any_double(value)); } /** Visits a ``double`` or ``long double`` argument. **/ template Result visit_any_double(T) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a null-terminated C string (``const char *``) argument. **/ Result visit_cstring(const char *) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a string argument. **/ Result visit_string(Arg::StringValue) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a wide string argument. **/ Result visit_wstring(Arg::StringValue) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits a pointer argument. **/ Result visit_pointer(const void *) { return FMT_DISPATCH(visit_unhandled_arg()); } /** Visits an argument of a custom (user-defined) type. **/ Result visit_custom(Arg::CustomValue) { return FMT_DISPATCH(visit_unhandled_arg()); } /** \rst Visits an argument dispatching to the appropriate visit method based on the argument type. For example, if the argument type is ``double`` then the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be called. \endrst */ Result visit(const Arg &arg) { switch (arg.type) { case Arg::NONE: case Arg::NAMED_ARG: FMT_ASSERT(false, "invalid argument type"); break; case Arg::INT: return FMT_DISPATCH(visit_int(arg.int_value)); case Arg::UINT: return FMT_DISPATCH(visit_uint(arg.uint_value)); case Arg::LONG_LONG: return FMT_DISPATCH(visit_long_long(arg.long_long_value)); case Arg::ULONG_LONG: return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); case Arg::BOOL: return FMT_DISPATCH(visit_bool(arg.int_value != 0)); case Arg::CHAR: return FMT_DISPATCH(visit_char(arg.int_value)); case Arg::DOUBLE: return FMT_DISPATCH(visit_double(arg.double_value)); case Arg::LONG_DOUBLE: return FMT_DISPATCH(visit_long_double(arg.long_double_value)); case Arg::CSTRING: return FMT_DISPATCH(visit_cstring(arg.string.value)); case Arg::STRING: return FMT_DISPATCH(visit_string(arg.string)); case Arg::WSTRING: return FMT_DISPATCH(visit_wstring(arg.wstring)); case Arg::POINTER: return FMT_DISPATCH(visit_pointer(arg.pointer)); case Arg::CUSTOM: return FMT_DISPATCH(visit_custom(arg.custom)); } return Result(); } }; enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC }; // Flags. enum { SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. }; // An empty format specifier. struct EmptySpec {}; // A type specifier. template struct TypeSpec : EmptySpec { Alignment align() const { return ALIGN_DEFAULT; } unsigned width() const { return 0; } int precision() const { return -1; } bool flag(unsigned) const { return false; } char type() const { return TYPE; } char fill() const { return ' '; } }; // A width specifier. struct WidthSpec { unsigned width_; // Fill is always wchar_t and cast to char if necessary to avoid having // two specialization of WidthSpec and its subclasses. wchar_t fill_; WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} unsigned width() const { return width_; } wchar_t fill() const { return fill_; } }; // An alignment specifier. struct AlignSpec : WidthSpec { Alignment align_; AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) : WidthSpec(width, fill), align_(align) {} Alignment align() const { return align_; } int precision() const { return -1; } }; // An alignment and type specifier. template struct AlignTypeSpec : AlignSpec { AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} bool flag(unsigned) const { return false; } char type() const { return TYPE; } }; // A full format specifier. struct FormatSpec : AlignSpec { unsigned flags_; int precision_; char type_; FormatSpec( unsigned width = 0, char type = 0, wchar_t fill = ' ') : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} bool flag(unsigned f) const { return (flags_ & f) != 0; } int precision() const { return precision_; } char type() const { return type_; } }; // An integer format specifier. template , typename Char = char> class IntFormatSpec : public SpecT { private: T value_; public: IntFormatSpec(T val, const SpecT &spec = SpecT()) : SpecT(spec), value_(val) {} T value() const { return value_; } }; // A string format specifier. template class StrFormatSpec : public AlignSpec { private: const Char *str_; public: template StrFormatSpec(const Char *str, unsigned width, FillChar fill) : AlignSpec(width, fill), str_(str) { internal::CharTraits::convert(FillChar()); } const Char *str() const { return str_; } }; /** Returns an integer format specifier to format the value in base 2. */ IntFormatSpec > bin(int value); /** Returns an integer format specifier to format the value in base 8. */ IntFormatSpec > oct(int value); /** Returns an integer format specifier to format the value in base 16 using lower-case letters for the digits above 9. */ IntFormatSpec > hex(int value); /** Returns an integer formatter format specifier to format in base 16 using upper-case letters for the digits above 9. */ IntFormatSpec > hexu(int value); /** \rst Returns an integer format specifier to pad the formatted argument with the fill character to the specified width using the default (right) numeric alignment. **Example**:: MemoryWriter out; out << pad(hex(0xcafe), 8, '0'); // out.str() == "0000cafe" \endrst */ template IntFormatSpec, Char> pad( int value, unsigned width, Char fill = ' '); #define FMT_DEFINE_INT_FORMATTERS(TYPE) \ inline IntFormatSpec > bin(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'b'>()); \ } \ \ inline IntFormatSpec > oct(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'o'>()); \ } \ \ inline IntFormatSpec > hex(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'x'>()); \ } \ \ inline IntFormatSpec > hexu(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'X'>()); \ } \ \ template \ inline IntFormatSpec > pad( \ IntFormatSpec > f, unsigned width) { \ return IntFormatSpec >( \ f.value(), AlignTypeSpec(width, ' ')); \ } \ \ /* For compatibility with older compilers we provide two overloads for pad, */ \ /* one that takes a fill character and one that doesn't. In the future this */ \ /* can be replaced with one overload making the template argument Char */ \ /* default to char (C++11). */ \ template \ inline IntFormatSpec, Char> pad( \ IntFormatSpec, Char> f, \ unsigned width, Char fill) { \ return IntFormatSpec, Char>( \ f.value(), AlignTypeSpec(width, fill)); \ } \ \ inline IntFormatSpec > pad( \ TYPE value, unsigned width) { \ return IntFormatSpec >( \ value, AlignTypeSpec<0>(width, ' ')); \ } \ \ template \ inline IntFormatSpec, Char> pad( \ TYPE value, unsigned width, Char fill) { \ return IntFormatSpec, Char>( \ value, AlignTypeSpec<0>(width, fill)); \ } FMT_DEFINE_INT_FORMATTERS(int) FMT_DEFINE_INT_FORMATTERS(long) FMT_DEFINE_INT_FORMATTERS(unsigned) FMT_DEFINE_INT_FORMATTERS(unsigned long) FMT_DEFINE_INT_FORMATTERS(LongLong) FMT_DEFINE_INT_FORMATTERS(ULongLong) /** \rst Returns a string formatter that pads the formatted argument with the fill character to the specified width using the default (left) string alignment. **Example**:: std::string s = str(MemoryWriter() << pad("abc", 8)); // s == "abc " \endrst */ template inline StrFormatSpec pad( const Char *str, unsigned width, Char fill = ' ') { return StrFormatSpec(str, width, fill); } inline StrFormatSpec pad( const wchar_t *str, unsigned width, char fill = ' ') { return StrFormatSpec(str, width, fill); } namespace internal { template class ArgMap { private: typedef std::vector< std::pair, internal::Arg> > MapType; typedef typename MapType::value_type Pair; MapType map_; public: FMT_API void init(const ArgList &args); const internal::Arg* find(const fmt::BasicStringRef &name) const { // The list is unsorted, so just return the first matching name. for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); it != end; ++it) { if (it->first == name) return &it->second; } return 0; } }; template class ArgFormatterBase : public ArgVisitor { private: BasicWriter &writer_; FormatSpec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); void write_pointer(const void *p) { spec_.flags_ = HASH_FLAG; spec_.type_ = 'x'; writer_.write_int(reinterpret_cast(p), spec_); } protected: BasicWriter &writer() { return writer_; } FormatSpec &spec() { return spec_; } void write(bool value) { const char *str_value = value ? "true" : "false"; Arg::StringValue str = { str_value, std::strlen(str_value) }; writer_.write_str(str, spec_); } void write(const char *value) { Arg::StringValue str = { value, value != 0 ? std::strlen(value) : 0 }; writer_.write_str(str, spec_); } public: ArgFormatterBase(BasicWriter &w, FormatSpec &s) : writer_(w), spec_(s) {} template void visit_any_int(T value) { writer_.write_int(value, spec_); } template void visit_any_double(T value) { writer_.write_double(value, spec_); } void visit_bool(bool value) { if (spec_.type_) return visit_any_int(value); write(value); } void visit_char(int value) { if (spec_.type_ && spec_.type_ != 'c') { spec_.flags_ |= CHAR_FLAG; writer_.write_int(value, spec_); return; } if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) FMT_THROW(FormatError("invalid format specifier for char")); typedef typename BasicWriter::CharPtr CharPtr; Char fill = internal::CharTraits::cast(spec_.fill()); CharPtr out = CharPtr(); const unsigned CHAR_SIZE = 1; if (spec_.width_ > CHAR_SIZE) { out = writer_.grow_buffer(spec_.width_); if (spec_.align_ == ALIGN_RIGHT) { std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill); out += spec_.width_ - CHAR_SIZE; } else if (spec_.align_ == ALIGN_CENTER) { out = writer_.fill_padding(out, spec_.width_, internal::const_check(CHAR_SIZE), fill); } else { std::uninitialized_fill_n(out + CHAR_SIZE, spec_.width_ - CHAR_SIZE, fill); } } else { out = writer_.grow_buffer(CHAR_SIZE); } *out = internal::CharTraits::cast(value); } void visit_cstring(const char *value) { if (spec_.type_ == 'p') return write_pointer(value); write(value); } void visit_string(Arg::StringValue value) { writer_.write_str(value, spec_); } using ArgVisitor::visit_wstring; void visit_wstring(Arg::StringValue value) { writer_.write_str(value, spec_); } void visit_pointer(const void *value) { if (spec_.type_ && spec_.type_ != 'p') report_unknown_type(spec_.type_, "pointer"); write_pointer(value); } }; class FormatterBase { private: ArgList args_; int next_arg_index_; // Returns the argument with specified index. FMT_API Arg do_get_arg(unsigned arg_index, const char *&error); protected: const ArgList &args() const { return args_; } explicit FormatterBase(const ArgList &args) { args_ = args; next_arg_index_ = 0; } // Returns the next argument. Arg next_arg(const char *&error) { if (next_arg_index_ >= 0) return do_get_arg(internal::to_unsigned(next_arg_index_++), error); error = "cannot switch from manual to automatic argument indexing"; return Arg(); } // Checks if manual indexing is used and returns the argument with // specified index. Arg get_arg(unsigned arg_index, const char *&error) { return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); } bool check_no_auto_index(const char *&error) { if (next_arg_index_ > 0) { error = "cannot switch from automatic to manual argument indexing"; return false; } next_arg_index_ = -1; return true; } template void write(BasicWriter &w, const Char *start, const Char *end) { if (start != end) w << BasicStringRef(start, internal::to_unsigned(end - start)); } }; // A printf formatter. template class PrintfFormatter : private FormatterBase { private: void parse_flags(FormatSpec &spec, const Char *&s); // Returns the argument with specified index or, if arg_index is equal // to the maximum unsigned value, the next argument. Arg get_arg(const Char *s, unsigned arg_index = (std::numeric_limits::max)()); // Parses argument index, flags and width and returns the argument index. unsigned parse_header(const Char *&s, FormatSpec &spec); public: explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {} FMT_API void format(BasicWriter &writer, BasicCStringRef format_str); }; } // namespace internal /** \rst An argument formatter based on the `curiously recurring template pattern `_. To use `~fmt::BasicArgFormatter` define a subclass that implements some or all of the visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. Pass the subclass as the *Impl* template parameter. When a formatting function processes an argument, it will dispatch to a visit method specific to the argument type. For example, if the argument type is ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass will be called. If the subclass doesn't contain a method with this signature, then a corresponding method of `~fmt::BasicArgFormatter` or its superclass will be called. \endrst */ template class BasicArgFormatter : public internal::ArgFormatterBase { private: BasicFormatter &formatter_; const Char *format_; public: /** \rst Constructs an argument formatter object. *formatter* is a reference to the main formatter object, *spec* contains format specifier information for standard argument types, and *fmt* points to the part of the format string being parsed for custom argument types. \endrst */ BasicArgFormatter(BasicFormatter &formatter, FormatSpec &spec, const Char *fmt) : internal::ArgFormatterBase(formatter.writer(), spec), formatter_(formatter), format_(fmt) {} /** Formats argument of a custom (user-defined) type. */ void visit_custom(internal::Arg::CustomValue c) { c.format(&formatter_, c.value, &format_); } }; /** The default argument formatter. */ template class ArgFormatter : public BasicArgFormatter, Char> { public: /** Constructs an argument formatter object. */ ArgFormatter(BasicFormatter &formatter, FormatSpec &spec, const Char *fmt) : BasicArgFormatter, Char>(formatter, spec, fmt) {} }; /** This template formats data and writes the output to a writer. */ template class BasicFormatter : private internal::FormatterBase { public: /** The character type for the output. */ typedef CharType Char; private: BasicWriter &writer_; internal::ArgMap map_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); using internal::FormatterBase::get_arg; // Checks if manual indexing is used and returns the argument with // specified name. internal::Arg get_arg(BasicStringRef arg_name, const char *&error); // Parses argument index and returns corresponding argument. internal::Arg parse_arg_index(const Char *&s); // Parses argument name and returns corresponding argument. internal::Arg parse_arg_name(const Char *&s); public: /** \rst Constructs a ``BasicFormatter`` object. References to the arguments and the writer are stored in the formatter object so make sure they have appropriate lifetimes. \endrst */ BasicFormatter(const ArgList &args, BasicWriter &w) : internal::FormatterBase(args), writer_(w) {} /** Returns a reference to the writer associated with this formatter. */ BasicWriter &writer() { return writer_; } /** Formats stored arguments and writes the output to the writer. */ void format(BasicCStringRef format_str); // Formats a single argument and advances format_str, a format string pointer. const Char *format(const Char *&format_str, const internal::Arg &arg); }; // Generates a comma-separated list with results of applying f to // numbers 0..n-1. # define FMT_GEN(n, f) FMT_GEN##n(f) # define FMT_GEN1(f) f(0) # define FMT_GEN2(f) FMT_GEN1(f), f(1) # define FMT_GEN3(f) FMT_GEN2(f), f(2) # define FMT_GEN4(f) FMT_GEN3(f), f(3) # define FMT_GEN5(f) FMT_GEN4(f), f(4) # define FMT_GEN6(f) FMT_GEN5(f), f(5) # define FMT_GEN7(f) FMT_GEN6(f), f(6) # define FMT_GEN8(f) FMT_GEN7(f), f(7) # define FMT_GEN9(f) FMT_GEN8(f), f(8) # define FMT_GEN10(f) FMT_GEN9(f), f(9) # define FMT_GEN11(f) FMT_GEN10(f), f(10) # define FMT_GEN12(f) FMT_GEN11(f), f(11) # define FMT_GEN13(f) FMT_GEN12(f), f(12) # define FMT_GEN14(f) FMT_GEN13(f), f(13) # define FMT_GEN15(f) FMT_GEN14(f), f(14) namespace internal { inline uint64_t make_type() { return 0; } template inline uint64_t make_type(const T &arg) { return MakeValue< BasicFormatter >::type(arg); } template struct ArgArray; template struct ArgArray { typedef Value Type[N > 0 ? N : 1]; template static Value make(const T &value) { #ifdef __clang__ Value result = MakeValue(value); // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: // https://github.com/fmtlib/fmt/issues/276 (void)result.custom.format; return result; #else return MakeValue(value); #endif } }; template struct ArgArray { typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE template static Arg make(const T &value) { return MakeArg(value); } }; #if FMT_USE_VARIADIC_TEMPLATES template inline uint64_t make_type(const Arg &first, const Args & ... tail) { return make_type(first) | (make_type(tail...) << 4); } #else struct ArgType { uint64_t type; ArgType() : type(0) {} template ArgType(const T &arg) : type(make_type(arg)) {} }; # define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | (t12.type << 48) | (t13.type << 52) | (t14.type << 56); } #endif } // namespace internal # define FMT_MAKE_TEMPLATE_ARG(n) typename T##n # define FMT_MAKE_ARG_TYPE(n) T##n # define FMT_MAKE_ARG(n) const T##n &v##n # define FMT_ASSIGN_char(n) \ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) # define FMT_ASSIGN_wchar_t(n) \ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) #if FMT_USE_VARIADIC_TEMPLATES // Defines a variadic function returning void. # define FMT_VARIADIC_VOID(func, arg_type) \ template \ void func(arg_type arg0, const Args & ... args) { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \ } // Defines a variadic constructor. # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ template \ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \ } #else # define FMT_MAKE_REF(n) \ fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) # define FMT_MAKE_REF2(n) v##n // Defines a wrapper for a function taking one argument of type arg_type // and n additional arguments of arbitrary types. # define FMT_WRAP1(func, arg_type, n) \ template \ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ func(arg1, fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ } // Emulates a variadic function returning void on a pre-C++11 compiler. # define FMT_VARIADIC_VOID(func, arg_type) \ inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) # define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ template \ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ func(arg0, arg1, fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ } // Emulates a variadic constructor on a pre-C++11 compiler. # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) #endif // Generates a comma-separated list with results of applying f to pairs // (argument, index). #define FMT_FOR_EACH1(f, x0) f(x0, 0) #define FMT_FOR_EACH2(f, x0, x1) \ FMT_FOR_EACH1(f, x0), f(x1, 1) #define FMT_FOR_EACH3(f, x0, x1, x2) \ FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) #define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) #define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) #define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) #define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) #define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) #define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) #define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) /** An error returned by an operating system or a language runtime, for example a file opening error. */ class SystemError : public internal::RuntimeError { private: void init(int err_code, CStringRef format_str, ArgList args); protected: int error_code_; typedef char Char; // For FMT_VARIADIC_CTOR. SystemError() {} public: /** \rst Constructs a :class:`fmt::SystemError` object with the description of the form .. parsed-literal:: **: ** where ** is the formatted message and ** is the system message corresponding to the error code. *error_code* is a system error code as given by ``errno``. If *error_code* is not a valid error code such as -1, the system message may look like "Unknown error -1" and is platform-dependent. **Example**:: // This throws a SystemError with the description // cannot open file 'madeup': No such file or directory // or similar (system message may vary). const char *filename = "madeup"; std::FILE *file = std::fopen(filename, "r"); if (!file) throw fmt::SystemError(errno, "cannot open file '{}'", filename); \endrst */ SystemError(int error_code, CStringRef message) { init(error_code, message, ArgList()); } FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) ~SystemError() throw(); int error_code() const { return error_code_; } }; /** \rst This template provides operations for formatting and writing data into a character stream. The output is stored in a buffer provided by a subclass such as :class:`fmt::BasicMemoryWriter`. You can use one of the following typedefs for common character types: +---------+----------------------+ | Type | Definition | +=========+======================+ | Writer | BasicWriter | +---------+----------------------+ | WWriter | BasicWriter | +---------+----------------------+ \endrst */ template class BasicWriter { private: // Output buffer. Buffer &buffer_; FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); typedef typename internal::CharTraits::CharPtr CharPtr; #if FMT_SECURE_SCL // Returns pointer value. static Char *get(CharPtr p) { return p.base(); } #else static Char *get(Char *p) { return p; } #endif // Fills the padding around the content and returns the pointer to the // content area. static CharPtr fill_padding(CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill); // Grows the buffer by n characters and returns a pointer to the newly // allocated area. CharPtr grow_buffer(std::size_t n) { std::size_t size = buffer_.size(); buffer_.resize(size + n); return internal::make_ptr(&buffer_[size], n); } // Writes an unsigned decimal integer. template Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) { unsigned num_digits = internal::count_digits(value); Char *ptr = get(grow_buffer(prefix_size + num_digits)); internal::format_decimal(ptr + prefix_size, value, num_digits); return ptr; } // Writes a decimal integer. template void write_decimal(Int value) { typedef typename internal::IntTraits::MainType MainType; MainType abs_value = static_cast(value); if (internal::is_negative(value)) { abs_value = 0 - abs_value; *write_unsigned_decimal(abs_value, 1) = '-'; } else { write_unsigned_decimal(abs_value, 0); } } // Prepare a buffer for integer formatting. CharPtr prepare_int_buffer(unsigned num_digits, const EmptySpec &, const char *prefix, unsigned prefix_size) { unsigned size = prefix_size + num_digits; CharPtr p = grow_buffer(size); std::uninitialized_copy(prefix, prefix + prefix_size, p); return p + size - 1; } template CharPtr prepare_int_buffer(unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size); // Formats an integer. template void write_int(T value, Spec spec); // Formats a floating-point number (double or long double). template void write_double(T value, const FormatSpec &spec); // Writes a formatted string. template CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec); template void write_str(const internal::Arg::StringValue &str, const FormatSpec &spec); // This following methods are private to disallow writing wide characters // and strings to a char stream. If you want to print a wide string as a // pointer as std::ostream does, cast it to const void*. // Do not implement! void operator<<(typename internal::WCharHelper::Unsupported); void operator<<( typename internal::WCharHelper::Unsupported); // Appends floating-point length specifier to the format string. // The second argument is only used for overload resolution. void append_float_length(Char *&format_ptr, long double) { *format_ptr++ = 'L'; } template void append_float_length(Char *&, T) {} template friend class internal::ArgFormatterBase; friend class internal::PrintfArgFormatter; protected: /** Constructs a ``BasicWriter`` object. */ explicit BasicWriter(Buffer &b) : buffer_(b) {} public: /** \rst Destroys a ``BasicWriter`` object. \endrst */ virtual ~BasicWriter() {} /** Returns the total number of characters written. */ std::size_t size() const { return buffer_.size(); } /** Returns a pointer to the output buffer content. No terminating null character is appended. */ const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } /** Returns a pointer to the output buffer content with terminating null character appended. */ const Char *c_str() const { std::size_t size = buffer_.size(); buffer_.reserve(size + 1); buffer_[size] = '\0'; return &buffer_[0]; } /** \rst Returns the content of the output buffer as an `std::string`. \endrst */ std::basic_string str() const { return std::basic_string(&buffer_[0], buffer_.size()); } /** \rst Writes formatted data. *args* is an argument list representing arbitrary arguments. **Example**:: MemoryWriter out; out.write("Current point:\n"); out.write("({:+f}, {:+f})", -3.14, 3.14); This will write the following output to the ``out`` object: .. code-block:: none Current point: (-3.140000, +3.140000) The output can be accessed using :func:`data()`, :func:`c_str` or :func:`str` methods. See also :ref:`syntax`. \endrst */ void write(BasicCStringRef format, ArgList args) { BasicFormatter(args, *this).format(format); } FMT_VARIADIC_VOID(write, BasicCStringRef) BasicWriter &operator<<(int value) { write_decimal(value); return *this; } BasicWriter &operator<<(unsigned value) { return *this << IntFormatSpec(value); } BasicWriter &operator<<(long value) { write_decimal(value); return *this; } BasicWriter &operator<<(unsigned long value) { return *this << IntFormatSpec(value); } BasicWriter &operator<<(LongLong value) { write_decimal(value); return *this; } /** \rst Formats *value* and writes it to the stream. \endrst */ BasicWriter &operator<<(ULongLong value) { return *this << IntFormatSpec(value); } BasicWriter &operator<<(double value) { write_double(value, FormatSpec()); return *this; } /** \rst Formats *value* using the general format for floating-point numbers (``'g'``) and writes it to the stream. \endrst */ BasicWriter &operator<<(long double value) { write_double(value, FormatSpec()); return *this; } /** Writes a character to the stream. */ BasicWriter &operator<<(char value) { buffer_.push_back(value); return *this; } BasicWriter &operator<<( typename internal::WCharHelper::Supported value) { buffer_.push_back(value); return *this; } /** \rst Writes *value* to the stream. \endrst */ BasicWriter &operator<<(fmt::BasicStringRef value) { const Char *str = value.data(); buffer_.append(str, str + value.size()); return *this; } BasicWriter &operator<<( typename internal::WCharHelper::Supported value) { const char *str = value.data(); buffer_.append(str, str + value.size()); return *this; } template BasicWriter &operator<<(IntFormatSpec spec) { internal::CharTraits::convert(FillChar()); write_int(spec.value(), spec); return *this; } template BasicWriter &operator<<(const StrFormatSpec &spec) { const StrChar *s = spec.str(); write_str(s, std::char_traits::length(s), spec); return *this; } void clear() FMT_NOEXCEPT { buffer_.clear(); } Buffer &buffer() FMT_NOEXCEPT { return buffer_; } }; template template typename BasicWriter::CharPtr BasicWriter::write_str( const StrChar *s, std::size_t size, const AlignSpec &spec) { CharPtr out = CharPtr(); if (spec.width() > size) { out = grow_buffer(spec.width()); Char fill = internal::CharTraits::cast(spec.fill()); if (spec.align() == ALIGN_RIGHT) { std::uninitialized_fill_n(out, spec.width() - size, fill); out += spec.width() - size; } else if (spec.align() == ALIGN_CENTER) { out = fill_padding(out, spec.width(), size, fill); } else { std::uninitialized_fill_n(out + size, spec.width() - size, fill); } } else { out = grow_buffer(size); } std::uninitialized_copy(s, s + size, out); return out; } template template void BasicWriter::write_str( const internal::Arg::StringValue &s, const FormatSpec &spec) { // Check if StrChar is convertible to Char. internal::CharTraits::convert(StrChar()); if (spec.type_ && spec.type_ != 's') internal::report_unknown_type(spec.type_, "string"); const StrChar *str_value = s.value; std::size_t str_size = s.size; if (str_size == 0) { if (!str_value) { FMT_THROW(FormatError("string pointer is null")); } } std::size_t precision = static_cast(spec.precision_); if (spec.precision_ >= 0 && precision < str_size) str_size = precision; write_str(str_value, str_size, spec); } template typename BasicWriter::CharPtr BasicWriter::fill_padding( CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill) { std::size_t padding = total_size - content_size; std::size_t left_padding = padding / 2; Char fill_char = internal::CharTraits::cast(fill); std::uninitialized_fill_n(buffer, left_padding, fill_char); buffer += left_padding; CharPtr content = buffer; std::uninitialized_fill_n(buffer + content_size, padding - left_padding, fill_char); return content; } template template typename BasicWriter::CharPtr BasicWriter::prepare_int_buffer( unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size) { unsigned width = spec.width(); Alignment align = spec.align(); Char fill = internal::CharTraits::cast(spec.fill()); if (spec.precision() > static_cast(num_digits)) { // Octal prefix '0' is counted as a digit, so ignore it if precision // is specified. if (prefix_size > 0 && prefix[prefix_size - 1] == '0') --prefix_size; unsigned number_size = prefix_size + internal::to_unsigned(spec.precision()); AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); if (number_size >= width) return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); buffer_.reserve(width); unsigned fill_size = width - number_size; if (align != ALIGN_LEFT) { CharPtr p = grow_buffer(fill_size); std::uninitialized_fill(p, p + fill_size, fill); } CharPtr result = prepare_int_buffer( num_digits, subspec, prefix, prefix_size); if (align == ALIGN_LEFT) { CharPtr p = grow_buffer(fill_size); std::uninitialized_fill(p, p + fill_size, fill); } return result; } unsigned size = prefix_size + num_digits; if (width <= size) { CharPtr p = grow_buffer(size); std::uninitialized_copy(prefix, prefix + prefix_size, p); return p + size - 1; } CharPtr p = grow_buffer(width); CharPtr end = p + width; if (align == ALIGN_LEFT) { std::uninitialized_copy(prefix, prefix + prefix_size, p); p += size; std::uninitialized_fill(p, end, fill); } else if (align == ALIGN_CENTER) { p = fill_padding(p, width, size, fill); std::uninitialized_copy(prefix, prefix + prefix_size, p); p += size; } else { if (align == ALIGN_NUMERIC) { if (prefix_size != 0) { p = std::uninitialized_copy(prefix, prefix + prefix_size, p); size -= prefix_size; } } else { std::uninitialized_copy(prefix, prefix + prefix_size, end - size); } std::uninitialized_fill(p, end - size, fill); p = end; } return p - 1; } template template void BasicWriter::write_int(T value, Spec spec) { unsigned prefix_size = 0; typedef typename internal::IntTraits::MainType UnsignedType; UnsignedType abs_value = static_cast(value); char prefix[4] = ""; if (internal::is_negative(value)) { prefix[0] = '-'; ++prefix_size; abs_value = 0 - abs_value; } else if (spec.flag(SIGN_FLAG)) { prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; ++prefix_size; } switch (spec.type()) { case 0: case 'd': { unsigned num_digits = internal::count_digits(abs_value); CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1; internal::format_decimal(get(p), abs_value, 0); break; } case 'x': case 'X': { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = spec.type(); } unsigned num_digits = 0; do { ++num_digits; } while ((n >>= 4) != 0); Char *p = get(prepare_int_buffer( num_digits, spec, prefix, prefix_size)); n = abs_value; const char *digits = spec.type() == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; do { *p-- = digits[n & 0xf]; } while ((n >>= 4) != 0); break; } case 'b': case 'B': { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) { prefix[prefix_size++] = '0'; prefix[prefix_size++] = spec.type(); } unsigned num_digits = 0; do { ++num_digits; } while ((n >>= 1) != 0); Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); n = abs_value; do { *p-- = static_cast('0' + (n & 1)); } while ((n >>= 1) != 0); break; } case 'o': { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) prefix[prefix_size++] = '0'; unsigned num_digits = 0; do { ++num_digits; } while ((n >>= 3) != 0); Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); n = abs_value; do { *p-- = static_cast('0' + (n & 7)); } while ((n >>= 3) != 0); break; } case 'n': { unsigned num_digits = internal::count_digits(abs_value); fmt::StringRef sep = ""; #ifndef ANDROID sep = internal::thousands_sep(std::localeconv()); #endif unsigned size = static_cast( num_digits + sep.size() * ((num_digits - 1) / 3)); CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1; internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep)); break; } default: internal::report_unknown_type( spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); break; } } template template void BasicWriter::write_double(T value, const FormatSpec &spec) { // Check type. char type = spec.type(); bool upper = false; switch (type) { case 0: type = 'g'; break; case 'e': case 'f': case 'g': case 'a': break; case 'F': #if FMT_MSC_VER // MSVC's printf doesn't support 'F'. type = 'f'; #endif // Fall through. case 'E': case 'G': case 'A': upper = true; break; default: internal::report_unknown_type(type, "double"); break; } char sign = 0; // Use isnegative instead of value < 0 because the latter is always // false for NaN. if (internal::FPUtil::isnegative(static_cast(value))) { sign = '-'; value = -value; } else if (spec.flag(SIGN_FLAG)) { sign = spec.flag(PLUS_FLAG) ? '+' : ' '; } if (internal::FPUtil::isnotanumber(value)) { // Format NaN ourselves because sprintf's output is not consistent // across platforms. std::size_t nan_size = 4; const char *nan = upper ? " NAN" : " nan"; if (!sign) { --nan_size; ++nan; } CharPtr out = write_str(nan, nan_size, spec); if (sign) *out = sign; return; } if (internal::FPUtil::isinfinity(value)) { // Format infinity ourselves because sprintf's output is not consistent // across platforms. std::size_t inf_size = 4; const char *inf = upper ? " INF" : " inf"; if (!sign) { --inf_size; ++inf; } CharPtr out = write_str(inf, inf_size, spec); if (sign) *out = sign; return; } std::size_t offset = buffer_.size(); unsigned width = spec.width(); if (sign) { buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u)); if (width > 0) --width; ++offset; } // Build format string. enum { MAX_FORMAT_SIZE = 10 }; // longest format: %#-*.*Lg Char format[MAX_FORMAT_SIZE]; Char *format_ptr = format; *format_ptr++ = '%'; unsigned width_for_sprintf = width; if (spec.flag(HASH_FLAG)) *format_ptr++ = '#'; if (spec.align() == ALIGN_CENTER) { width_for_sprintf = 0; } else { if (spec.align() == ALIGN_LEFT) *format_ptr++ = '-'; if (width != 0) *format_ptr++ = '*'; } if (spec.precision() >= 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; } append_float_length(format_ptr, value); *format_ptr++ = type; *format_ptr = '\0'; // Format using snprintf. Char fill = internal::CharTraits::cast(spec.fill()); unsigned n = 0; Char *start = 0; for (;;) { std::size_t buffer_size = buffer_.capacity() - offset; #if FMT_MSC_VER // MSVC's vsnprintf_s doesn't work with zero size, so reserve // space for at least one extra character to make the size non-zero. // Note that the buffer's capacity will increase by more than 1. if (buffer_size == 0) { buffer_.reserve(offset + 1); buffer_size = buffer_.capacity() - offset; } #endif start = &buffer_[offset]; int result = internal::CharTraits::format_float( start, buffer_size, format, width_for_sprintf, spec.precision(), value); if (result >= 0) { n = internal::to_unsigned(result); if (offset + n < buffer_.capacity()) break; // The buffer is large enough - continue with formatting. buffer_.reserve(offset + n + 1); } else { // If result is negative we ask to increase the capacity by at least 1, // but as std::vector, the buffer grows exponentially. buffer_.reserve(buffer_.capacity() + 1); } } if (sign) { if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || *start != ' ') { *(start - 1) = sign; sign = 0; } else { *(start - 1) = fill; } ++n; } if (spec.align() == ALIGN_CENTER && spec.width() > n) { width = spec.width(); CharPtr p = grow_buffer(width); std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); fill_padding(p, spec.width(), n, fill); return; } if (spec.fill() != ' ' || sign) { while (*start == ' ') *start++ = fill; if (sign) *(start - 1) = sign; } grow_buffer(n); } /** \rst This class template provides operations for formatting and writing data into a character stream. The output is stored in a memory buffer that grows dynamically. You can use one of the following typedefs for common character types and the standard allocator: +---------------+-----------------------------------------------------+ | Type | Definition | +===============+=====================================================+ | MemoryWriter | BasicMemoryWriter> | +---------------+-----------------------------------------------------+ | WMemoryWriter | BasicMemoryWriter> | +---------------+-----------------------------------------------------+ **Example**:: MemoryWriter out; out << "The answer is " << 42 << "\n"; out.write("({:+f}, {:+f})", -3.14, 3.14); This will write the following output to the ``out`` object: .. code-block:: none The answer is 42 (-3.140000, +3.140000) The output can be converted to an ``std::string`` with ``out.str()`` or accessed as a C string with ``out.c_str()``. \endrst */ template > class BasicMemoryWriter : public BasicWriter { private: internal::MemoryBuffer buffer_; public: explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) : BasicWriter(buffer_), buffer_(alloc) {} #if FMT_USE_RVALUE_REFERENCES /** \rst Constructs a :class:`fmt::BasicMemoryWriter` object moving the content of the other object to it. \endrst */ BasicMemoryWriter(BasicMemoryWriter &&other) : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { } /** \rst Moves the content of the other ``BasicMemoryWriter`` object to this one. \endrst */ BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { buffer_ = std::move(other.buffer_); return *this; } #endif }; typedef BasicMemoryWriter MemoryWriter; typedef BasicMemoryWriter WMemoryWriter; /** \rst This class template provides operations for formatting and writing data into a fixed-size array. For writing into a dynamically growing buffer use :class:`fmt::BasicMemoryWriter`. Any write method will throw ``std::runtime_error`` if the output doesn't fit into the array. You can use one of the following typedefs for common character types: +--------------+---------------------------+ | Type | Definition | +==============+===========================+ | ArrayWriter | BasicArrayWriter | +--------------+---------------------------+ | WArrayWriter | BasicArrayWriter | +--------------+---------------------------+ \endrst */ template class BasicArrayWriter : public BasicWriter { private: internal::FixedBuffer buffer_; public: /** \rst Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the given size. \endrst */ BasicArrayWriter(Char *array, std::size_t size) : BasicWriter(buffer_), buffer_(array, size) {} /** \rst Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the size known at compile time. \endrst */ template explicit BasicArrayWriter(Char(&array)[SIZE]) : BasicWriter(buffer_), buffer_(array, SIZE) {} }; typedef BasicArrayWriter ArrayWriter; typedef BasicArrayWriter WArrayWriter; // Reports a system error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; #if FMT_USE_WINDOWS_H /** A Windows error. */ class WindowsError : public SystemError { private: FMT_API void init(int error_code, CStringRef format_str, ArgList args); public: /** \rst Constructs a :class:`fmt::WindowsError` object with the description of the form .. parsed-literal:: **: ** where ** is the formatted message and ** is the system message corresponding to the error code. *error_code* is a Windows error code as given by ``GetLastError``. If *error_code* is not a valid error code such as -1, the system message will look like "error -1". **Example**:: // This throws a WindowsError with the description // cannot open file 'madeup': The system cannot find the file specified. // or similar (system message may vary). const char *filename = "madeup"; LPOFSTRUCT of = LPOFSTRUCT(); HFILE file = OpenFile(filename, &of, OF_READ); if (file == HFILE_ERROR) { throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); } \endrst */ WindowsError(int error_code, CStringRef message) { init(error_code, message, ArgList()); } FMT_VARIADIC_CTOR(WindowsError, init, int, CStringRef) }; // Reports a Windows error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; #endif enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; /** Formats a string and prints it to stdout using ANSI escape sequences to specify color (experimental). Example: print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); */ FMT_API void print_colored(Color c, CStringRef format, ArgList args); /** \rst Formats arguments and returns the result as a string. **Example**:: std::string message = format("The answer is {}", 42); \endrst */ inline std::string format(CStringRef format_str, ArgList args) { MemoryWriter w; w.write(format_str, args); return w.str(); } inline std::wstring format(WCStringRef format_str, ArgList args) { WMemoryWriter w; w.write(format_str, args); return w.str(); } /** \rst Prints formatted data to the file *f*. **Example**:: print(stderr, "Don't {}!", "panic"); \endrst */ FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args); /** \rst Prints formatted data to ``stdout``. **Example**:: print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ FMT_API void print(CStringRef format_str, ArgList args); template void printf(BasicWriter &w, BasicCStringRef format, ArgList args) { internal::PrintfFormatter(args).format(w, format); } /** \rst Formats arguments and returns the result as a string. **Example**:: std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ inline std::string sprintf(CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); return w.str(); } inline std::wstring sprintf(WCStringRef format, ArgList args) { WMemoryWriter w; printf(w, format, args); return w.str(); } /** \rst Prints formatted data to the file *f*. **Example**:: fmt::fprintf(stderr, "Don't %s!", "panic"); \endrst */ FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); /** \rst Prints formatted data to ``stdout``. **Example**:: fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ inline int printf(CStringRef format, ArgList args) { return fprintf(stdout, format, args); } /** Fast integer formatter. */ class FormatInt { private: // Buffer should be large enough to hold all digits (digits10 + 1), // a sign and a null character. enum { BUFFER_SIZE = std::numeric_limits::digits10 + 3 }; mutable char buffer_[BUFFER_SIZE]; char *str_; // Formats value in reverse and returns the number of digits. char *format_decimal(ULongLong value) { char *buffer_end = buffer_ + BUFFER_SIZE - 1; while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. unsigned index = static_cast((value % 100) * 2); value /= 100; *--buffer_end = internal::Data::DIGITS[index + 1]; *--buffer_end = internal::Data::DIGITS[index]; } if (value < 10) { *--buffer_end = static_cast('0' + value); return buffer_end; } unsigned index = static_cast(value * 2); *--buffer_end = internal::Data::DIGITS[index + 1]; *--buffer_end = internal::Data::DIGITS[index]; return buffer_end; } void FormatSigned(LongLong value) { ULongLong abs_value = static_cast(value); bool negative = value < 0; if (negative) abs_value = 0 - abs_value; str_ = format_decimal(abs_value); if (negative) *--str_ = '-'; } public: explicit FormatInt(int value) { FormatSigned(value); } explicit FormatInt(long value) { FormatSigned(value); } explicit FormatInt(LongLong value) { FormatSigned(value); } explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} /** Returns the number of characters written to the output buffer. */ std::size_t size() const { return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1); } /** Returns a pointer to the output buffer content. No terminating null character is appended. */ const char *data() const { return str_; } /** Returns a pointer to the output buffer content with terminating null character appended. */ const char *c_str() const { buffer_[BUFFER_SIZE - 1] = '\0'; return str_; } /** \rst Returns the content of the output buffer as an ``std::string``. \endrst */ std::string str() const { return std::string(str_, size()); } }; // Formats a decimal integer value writing into buffer and returns // a pointer to the end of the formatted string. This function doesn't // write a terminating null character. template inline void format_decimal(char *&buffer, T value) { typedef typename internal::IntTraits::MainType MainType; MainType abs_value = static_cast(value); if (internal::is_negative(value)) { *buffer++ = '-'; abs_value = 0 - abs_value; } if (abs_value < 100) { if (abs_value < 10) { *buffer++ = static_cast('0' + abs_value); return; } unsigned index = static_cast(abs_value * 2); *buffer++ = internal::Data::DIGITS[index]; *buffer++ = internal::Data::DIGITS[index + 1]; return; } unsigned num_digits = internal::count_digits(abs_value); internal::format_decimal(buffer, abs_value, num_digits); buffer += num_digits; } /** \rst Returns a named argument for formatting functions. **Example**:: print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); \endrst */ template inline internal::NamedArg arg(StringRef name, const T &arg) { return internal::NamedArg(name, arg); } template inline internal::NamedArg arg(WStringRef name, const T &arg) { return internal::NamedArg(name, arg); } // The following two functions are deleted intentionally to disable // nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``. template void arg(StringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; template void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; } #if FMT_GCC_VERSION // Use the system_header pragma to suppress warnings about variadic macros // because suppressing -Wvariadic-macros with the diagnostic pragma doesn't // work. It is used at the end because we want to suppress as little warnings // as possible. # pragma GCC system_header #endif // This is used to work around VC++ bugs in handling variadic macros. #define FMT_EXPAND(args) args // Returns the number of arguments. // Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. #define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) #define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) #define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #define FMT_CONCAT(a, b) a##b #define FMT_FOR_EACH_(N, f, ...) \ FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) #define FMT_FOR_EACH(f, ...) \ FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) #define FMT_ADD_ARG_NAME(type, index) type arg##index #define FMT_GET_ARG_NAME(type, index) arg##index #if FMT_USE_VARIADIC_TEMPLATES # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ template \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ const Args & ... args) { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ fmt::ArgList(fmt::internal::make_type(args...), array)); \ } #else // Defines a wrapper for a function taking __VA_ARGS__ arguments // and n additional arguments of arbitrary types. # define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ template \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ FMT_GEN(n, FMT_MAKE_ARG)) { \ fmt::internal::ArgArray::Type arr; \ FMT_GEN(n, FMT_ASSIGN_##Char); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ } # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ } \ FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) #endif // FMT_USE_VARIADIC_TEMPLATES /** \rst Defines a variadic function with the specified return type, function name and argument types passed as variable arguments to this macro. **Example**:: void print_error(const char *file, int line, const char *format, fmt::ArgList args) { fmt::print("{}: {}: ", file, line); fmt::print(format, args); } FMT_VARIADIC(void, print_error, const char *, int, const char *) ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that don't implement variadic templates. You don't have to use this macro if you don't need legacy compiler support and can use variadic templates directly:: template void print_error(const char *file, int line, const char *format, const Args & ... args) { fmt::print("{}: {}: ", file, line); fmt::print(format, args...); } \endrst */ #define FMT_VARIADIC(ReturnType, func, ...) \ FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) #define FMT_VARIADIC_W(ReturnType, func, ...) \ FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) #define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) #define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id) /** \rst Convenient macro to capture the arguments' names and values into several ``fmt::arg(name, value)``. **Example**:: int x = 1, y = 2; print("point: ({x}, {y})", FMT_CAPTURE(x, y)); // same as: // print("point: ({x}, {y})", arg("x", x), arg("y", y)); \endrst */ #define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) #define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__) namespace fmt { FMT_VARIADIC(std::string, format, CStringRef) FMT_VARIADIC_W(std::wstring, format, WCStringRef) FMT_VARIADIC(void, print, CStringRef) FMT_VARIADIC(void, print, std::FILE *, CStringRef) FMT_VARIADIC(void, print_colored, Color, CStringRef) FMT_VARIADIC(std::string, sprintf, CStringRef) FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) namespace internal { template inline bool is_name_start(Char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } // Parses an unsigned integer advancing s to the end of the parsed input. // This function assumes that the first character of s is a digit. template unsigned parse_nonnegative_int(const Char *&s) { assert('0' <= *s && *s <= '9'); unsigned value = 0; do { unsigned new_value = value * 10 + (*s++ - '0'); // Check if value wrapped around. if (new_value < value) { value = (std::numeric_limits::max)(); break; } value = new_value; } while ('0' <= *s && *s <= '9'); // Convert to unsigned to prevent a warning. unsigned max_int = (std::numeric_limits::max)(); if (value > max_int) FMT_THROW(FormatError("number is too big")); return value; } inline void require_numeric_argument(const Arg &arg, char spec) { if (arg.type > Arg::LAST_NUMERIC_TYPE) { std::string message = fmt::format("format specifier '{}' requires numeric argument", spec); FMT_THROW(fmt::FormatError(message)); } } template void check_sign(const Char *&s, const Arg &arg) { char sign = static_cast(*s); require_numeric_argument(arg, sign); if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { FMT_THROW(FormatError(fmt::format( "format specifier '{}' requires signed argument", sign))); } ++s; } } // namespace internal template inline internal::Arg BasicFormatter::get_arg( BasicStringRef arg_name, const char *&error) { if (check_no_auto_index(error)) { map_.init(args()); const internal::Arg *arg = map_.find(arg_name); if (arg) return *arg; error = "argument not found"; } return internal::Arg(); } template inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) { const char *error = 0; internal::Arg arg = *s < '0' || *s > '9' ? next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error); if (error) { FMT_THROW(FormatError( *s != '}' && *s != ':' ? "invalid format string" : error)); } return arg; } template inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) { assert(internal::is_name_start(*s)); const Char *start = s; Char c; do { c = *++s; } while (internal::is_name_start(c) || ('0' <= c && c <= '9')); const char *error = 0; internal::Arg arg = get_arg(BasicStringRef(start, s - start), error); if (error) FMT_THROW(FormatError(error)); return arg; } template const Char *BasicFormatter::format( const Char *&format_str, const internal::Arg &arg) { using internal::Arg; const Char *s = format_str; FormatSpec spec; if (*s == ':') { if (arg.type == Arg::CUSTOM) { arg.custom.format(this, arg.custom.value, &s); return s; } ++s; // Parse fill and alignment. if (Char c = *s) { const Char *p = s + 1; spec.align_ = ALIGN_DEFAULT; do { switch (*p) { case '<': spec.align_ = ALIGN_LEFT; break; case '>': spec.align_ = ALIGN_RIGHT; break; case '=': spec.align_ = ALIGN_NUMERIC; break; case '^': spec.align_ = ALIGN_CENTER; break; } if (spec.align_ != ALIGN_DEFAULT) { if (p != s) { if (c == '}') break; if (c == '{') FMT_THROW(FormatError("invalid fill character '{'")); s += 2; spec.fill_ = c; } else ++s; if (spec.align_ == ALIGN_NUMERIC) require_numeric_argument(arg, '='); break; } } while (--p >= s); } // Parse sign. switch (*s) { case '+': check_sign(s, arg); spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '-': check_sign(s, arg); spec.flags_ |= MINUS_FLAG; break; case ' ': check_sign(s, arg); spec.flags_ |= SIGN_FLAG; break; } if (*s == '#') { require_numeric_argument(arg, '#'); spec.flags_ |= HASH_FLAG; ++s; } // Parse zero flag. if (*s == '0') { require_numeric_argument(arg, '0'); spec.align_ = ALIGN_NUMERIC; spec.fill_ = '0'; ++s; } // Parse width. if ('0' <= *s && *s <= '9') { spec.width_ = internal::parse_nonnegative_int(s); } else if (*s == '{') { ++s; Arg width_arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; switch (width_arg.type) { case Arg::INT: if (width_arg.int_value < 0) FMT_THROW(FormatError("negative width")); value = width_arg.int_value; break; case Arg::UINT: value = width_arg.uint_value; break; case Arg::LONG_LONG: if (width_arg.long_long_value < 0) FMT_THROW(FormatError("negative width")); value = width_arg.long_long_value; break; case Arg::ULONG_LONG: value = width_arg.ulong_long_value; break; default: FMT_THROW(FormatError("width is not integer")); } if (value >(std::numeric_limits::max)()) FMT_THROW(FormatError("number is too big")); spec.width_ = static_cast(value); } // Parse precision. if (*s == '.') { ++s; spec.precision_ = 0; if ('0' <= *s && *s <= '9') { spec.precision_ = internal::parse_nonnegative_int(s); } else if (*s == '{') { ++s; Arg precision_arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); if (*s++ != '}') FMT_THROW(FormatError("invalid format string")); ULongLong value = 0; switch (precision_arg.type) { case Arg::INT: if (precision_arg.int_value < 0) FMT_THROW(FormatError("negative precision")); value = precision_arg.int_value; break; case Arg::UINT: value = precision_arg.uint_value; break; case Arg::LONG_LONG: if (precision_arg.long_long_value < 0) FMT_THROW(FormatError("negative precision")); value = precision_arg.long_long_value; break; case Arg::ULONG_LONG: value = precision_arg.ulong_long_value; break; default: FMT_THROW(FormatError("precision is not integer")); } if (value >(std::numeric_limits::max)()) FMT_THROW(FormatError("number is too big")); spec.precision_ = static_cast(value); } else { FMT_THROW(FormatError("missing precision specifier")); } if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { FMT_THROW(FormatError( fmt::format("precision not allowed in {} format specifier", arg.type == Arg::POINTER ? "pointer" : "integer"))); } } // Parse type. if (*s != '}' && *s) spec.type_ = static_cast(*s++); } if (*s++ != '}') FMT_THROW(FormatError("missing '}' in format string")); // Format argument. ArgFormatter(*this, spec, s - 1).visit(arg); return s; } template void BasicFormatter::format(BasicCStringRef format_str) { const Char *s = format_str.c_str(); const Char *start = s; while (*s) { Char c = *s++; if (c != '{' && c != '}') continue; if (*s == c) { write(writer_, start, s); start = ++s; continue; } if (c == '}') FMT_THROW(FormatError("unmatched '}' in format string")); write(writer_, start, s - 1); internal::Arg arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); start = s = format(s, arg); } write(writer_, start, s); } } // namespace fmt #if FMT_USE_USER_DEFINED_LITERALS namespace fmt { namespace internal { template struct UdlFormat { const Char *str; template auto operator()(Args && ... args) const -> decltype(format(str, std::forward(args)...)) { return format(str, std::forward(args)...); } }; template struct UdlArg { const Char *str; template NamedArg operator=(T &&value) const { return{ str, std::forward(value) }; } }; } // namespace internal inline namespace literals { /** \rst C++11 literal equivalent of :func:`fmt::format`. **Example**:: using namespace fmt::literals; std::string message = "The answer is {}"_format(42); \endrst */ inline internal::UdlFormat operator"" _format(const char *s, std::size_t) { return{ s }; } inline internal::UdlFormat operator"" _format(const wchar_t *s, std::size_t) { return{ s }; } /** \rst C++11 literal equivalent of :func:`fmt::arg`. **Example**:: using namespace fmt::literals; print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); \endrst */ inline internal::UdlArg operator"" _a(const char *s, std::size_t) { return{ s }; } inline internal::UdlArg operator"" _a(const wchar_t *s, std::size_t) { return{ s }; } } // inline namespace literals } // namespace fmt #endif // FMT_USE_USER_DEFINED_LITERALS // Restore warnings. #if FMT_GCC_VERSION >= 406 # pragma GCC diagnostic pop #endif #if defined(__clang__) && !defined(FMT_ICC_VERSION) # pragma clang diagnostic pop #endif #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format.cc" #else # define FMT_FUNC #endif #endif // FMT_FORMAT_H_ pvpgn-1.99.7.2.1+dfsg/src/common/give_up_root_privileges.cpp000066400000000000000000000077071462640461400237300ustar00rootroot00000000000000/* * Copyright (C) 2001 Hakan Tandogan (hakan@gurkensalat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/give_up_root_privileges.h" #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_PWD_H # include #endif #ifdef HAVE_GRP_H # include #endif #include "common/eventlog.h" #include "common/setup_after.h" #define ILLEGAL_ID -1 namespace pvpgn { static int gurp_gname2id(const char *name); static int gurp_uname2id(const char *name); extern int give_up_root_privileges(char const * user_name, char const * group_name) { int user_id = 0; int group_id = 0; eventlog(eventlog_level_debug, __FUNCTION__, "about to give up root privileges"); if (user_name) { if ((user_id = gurp_uname2id(user_name)) == ILLEGAL_ID) { return -1; } else { eventlog(eventlog_level_debug, __FUNCTION__, "should change to user = '{}' ({})", user_name, user_id); } } if (group_name) { if ((group_id = gurp_gname2id(group_name)) == ILLEGAL_ID) { return -1; } else { eventlog(eventlog_level_debug, __FUNCTION__, "should change to group = '{}' ({})", group_name, group_id); } } /* Change first the group ID, later we might not be able to anymore * We can use setgid safely because we don't want to return to root * privileges anymore */ #ifdef HAVE_SETGID if (group_name) { if (-1 == setgid(group_id)) { eventlog(eventlog_level_fatal, __FUNCTION__, "could not set gid to {} (setgid: {})", group_id, std::strerror(errno)); return -1; } # ifdef HAVE_GETUID eventlog(eventlog_level_info, __FUNCTION__, "Changed privileges to gid = {}", getgid()); # endif } #endif #ifdef HAVE_SETUID if (user_name) { if (-1 == setuid(user_id)) { eventlog(eventlog_level_fatal, __FUNCTION__, "could not set uid to {} (setuid: {})", user_id, std::strerror(errno)); return -1; } # ifdef HAVE_GETGID eventlog(eventlog_level_info, __FUNCTION__, "Changed privileges to uid = {}", getuid()); # endif } #endif return 0; } static int gurp_uname2id(const char *name) { int id = ILLEGAL_ID; if (name != NULL) { if (name[0] == '#') { id = std::atoi(&name[1]); } else { #ifdef HAVE_GETPWNAME struct passwd * ent; eventlog(eventlog_level_debug, __FUNCTION__, "about to getpwnam({})", name); if (!(ent = getpwnam(name))) { eventlog(eventlog_level_fatal, __FUNCTION__, "cannot get password file entry for '{}' (getpwnam: {})", name, std::strerror(errno)); return id; } id = ent->pw_uid; #else return id; #endif } } return id; } static int gurp_gname2id(const char *name) { int id = ILLEGAL_ID; if (name != NULL) { if (name[0] == '#') { id = std::atoi(&name[1]); } else { #ifdef HAVE_GETGRNAM struct group * ent; eventlog(eventlog_level_debug, __FUNCTION__, "about to getgrnam({})", name); if (!(ent = getgrnam(name))) { eventlog(eventlog_level_fatal, __FUNCTION__, "cannot get group file entry for '{}' (getgrnam: {})", name, std::strerror(errno)); return id; } id = ent->gr_gid; #else return id; #endif } } return id; } } pvpgn-1.99.7.2.1+dfsg/src/common/give_up_root_privileges.h000066400000000000000000000017321462640461400233650ustar00rootroot00000000000000/* * Copyright (C) 2001 Hakan Tandogan (hakan@gurkensalat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_GIVE_UP_ROOT_PRIVILEGES #define INCLUDED_GIVE_UP_ROOT_PRIVILEGES namespace pvpgn { extern int give_up_root_privileges(char const * user_name, char const * group_name); } #endif pvpgn-1.99.7.2.1+dfsg/src/common/gui_printf.cpp000066400000000000000000000054071462640461400211370ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32_GUI #include "common/setup_before.h" #include "gui_printf.h" #include #include #include #include #include "common/eventlog.h" #include #include "common/setup_after.h" namespace pvpgn { HWND ghwndConsole; static void guiAddText(const char *str, COLORREF clr) { int text_length = SendMessageW(ghwndConsole, WM_GETTEXTLENGTH, 0, 0); if (text_length > 30000) { CHARRANGE ds = {}; ds.cpMin = 0; ds.cpMax = text_length - 30000; SendMessageW(ghwndConsole, EM_EXSETSEL, 0, (LPARAM)&ds); SendMessageW(ghwndConsole, EM_REPLACESEL, FALSE, 0); } CHARRANGE cr = {}; cr.cpMin = text_length; cr.cpMax = text_length; SendMessageW(ghwndConsole, EM_EXSETSEL, 0, (LPARAM)&cr); CHARFORMATW fmt = {}; fmt.cbSize = sizeof(CHARFORMATW); fmt.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_BOLD | CFM_ITALIC | CFM_STRIKEOUT | CFM_UNDERLINE; fmt.yHeight = 160; fmt.dwEffects = 0; fmt.crTextColor = clr; std::swprintf(fmt.szFaceName, sizeof fmt.szFaceName / sizeof *fmt.szFaceName, L"%ls", L"Courier New"); SendMessageW(ghwndConsole, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt); SendMessageA(ghwndConsole, EM_REPLACESEL, FALSE, (LPARAM)str); } //template //void gui_lvprintf(t_eventlog_level l, const char* format, const Args& ... args) void gui_lvprintf(t_eventlog_level l, const char* format, fmt::ArgList args) { COLORREF clr; switch (l) { case eventlog_level_none: clr = RGB(0, 0, 0); break; case eventlog_level_trace: clr = RGB(255, 0, 255); break; case eventlog_level_debug: clr = RGB(0, 0, 255); break; case eventlog_level_info: clr = RGB(0, 0, 0); break; case eventlog_level_warn: clr = RGB(255, 128, 64); break; case eventlog_level_error: clr = RGB(255, 0, 0); break; case eventlog_level_fatal: clr = RGB(255, 0, 0); break; default: clr = RGB(0, 0, 0); } guiAddText(fmt::format(format, args).c_str(), clr); } } #endif pvpgn-1.99.7.2.1+dfsg/src/common/gui_printf.h000066400000000000000000000022761462640461400206050ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_GUI_PRINTF_H #define INCLUDED_GUI_PRINTF_H #ifdef WIN32_GUI #include "common/eventlog.h" #include namespace pvpgn { //template //void gui_lvprintf(t_eventlog_level l, const char* format, const Args& ... args); void gui_lvprintf(t_eventlog_level l, const char* format, fmt::ArgList args); FMT_VARIADIC(void, gui_lvprintf, t_eventlog_level, const char*); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/hash_tuple.hpp000066400000000000000000000026141462640461400211270ustar00rootroot00000000000000#ifndef INCLUDED_PVPGN_HASH_TUPLE_H #define INCLUDED_PVPGN_HASH_TUPLE_H #include #include #include // https://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set namespace pvpgn { namespace hash_tuple { template struct hash { std::size_t operator()(TT const& tt) const { return std::hash()(tt); } }; namespace { template inline void hash_combine(std::size_t& seed, T const& v) { seed ^= hash_tuple::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } } namespace { // Recursive template code derived from Matthieu M. template ::value - 1> struct HashValueImpl { static void apply(std::size_t& seed, Tuple const& tuple) { HashValueImpl::apply(seed, tuple); hash_combine(seed, std::get(tuple)); } }; template struct HashValueImpl { static void apply(std::size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } }; } template struct hash> { std::size_t operator()(std::tuple const& tt) const { std::size_t seed = 0; HashValueImpl >::apply(seed, tt); return seed; } }; } } #endifpvpgn-1.99.7.2.1+dfsg/src/common/hashtable.cpp000066400000000000000000000244521462640461400207250ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define HASHTABLE_INTERNAL_ACCESS #include "common/setup_before.h" #include "common/hashtable.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { static int nodata; /* if data points to this, then the entry was actually deleted */ static t_entry * hashtable_entry_export(t_internentry * entry, t_hashtable const * hashtable, unsigned int row); static t_entry * hashtable_entry_export(t_internentry * entry, t_hashtable const * hashtable, unsigned int row) { t_entry * temp; if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL entry"); return NULL; } if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return NULL; } if (row >= hashtable->num_rows) { eventlog(eventlog_level_error, __FUNCTION__, "got bad row {} (max {})", row, hashtable->num_rows - 1); return NULL; } temp = (t_entry*)xmalloc(sizeof(t_entry)); temp->row = row; temp->real = entry; temp->hashtable = hashtable; return temp; } extern t_hashtable * hashtable_create(unsigned int num_rows) { t_hashtable * newh; unsigned int i; if (num_rows < 1) { eventlog(eventlog_level_error, __FUNCTION__, "num_rows must be at least 1"); return NULL; } newh = (t_hashtable*)xmalloc(sizeof(t_hashtable)); newh->rows = (t_internentry**)xmalloc(sizeof(t_internentry *)*num_rows); newh->num_rows = num_rows; newh->len = 0; for (i = 0; i < num_rows; i++) newh->rows[i] = NULL; return newh; } extern int hashtable_destroy(t_hashtable * hashtable) { unsigned int i; if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return -1; } hashtable_purge(hashtable); for (i = 0; i < hashtable->num_rows; i++) if (hashtable->rows[i]) eventlog(eventlog_level_error, __FUNCTION__, "got non-empty hashtable"); xfree(hashtable->rows); xfree(hashtable); return 0; } extern int hashtable_purge(t_hashtable * hashtable) { unsigned int row; t_internentry * curr; t_internentry * head; t_internentry * next; t_internentry * * change; if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return -1; } for (row = 0; row < hashtable->num_rows; row++) { head = NULL; change = NULL; for (curr = hashtable->rows[row]; curr; curr = next) { next = curr->next; if (curr->data == &nodata) { if (change) *change = next; xfree(curr); } else { if (!head) head = curr; change = &curr->next; } } hashtable->rows[row] = head; } return 0; } extern unsigned int hashtable_get_length(t_hashtable const * hashtable) { if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return 0; } return hashtable->len; } extern int hashtable_insert_data(t_hashtable * hashtable, void * data, unsigned int hash) { unsigned int row; t_internentry * entry; if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return -1; } entry = (t_internentry*)xmalloc(sizeof(t_internentry)); entry->data = data; row = hash%hashtable->num_rows; entry->next = hashtable->rows[row]; hashtable->rows[row] = entry; hashtable->len++; return 0; } extern t_entry * hashtable_get_entry_by_data(t_hashtable const * hashtable, void const * data, unsigned int hash) { unsigned int row; t_internentry * curr; if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return NULL; } row = hash%hashtable->num_rows; for (curr = hashtable->rows[row]; curr; curr = curr->next) if (curr->data == data) return hashtable_entry_export(curr, hashtable, row); return NULL; } extern t_entry const * hashtable_get_entry_by_data_const(t_hashtable const * hashtable, void const * data, unsigned int hash) { unsigned int row; t_internentry * curr; if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return NULL; } row = hash%hashtable->num_rows; for (curr = hashtable->rows[row]; curr; curr = curr->next) if (curr->data == data) return hashtable_entry_export(curr, hashtable, row); return NULL; } extern int hashtable_remove_entry(t_hashtable * hashtable, t_entry * entry) { if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return -1; } if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL entry"); return -1; } if (!entry->real) { eventlog(eventlog_level_error, __FUNCTION__, "entry has NULL real pointer"); return -1; } if (entry->real->data == &nodata) { eventlog(eventlog_level_error, __FUNCTION__, "got deleted entry"); return -1; } entry->real->data = &nodata; hashtable->len--; return 0; } extern int hashtable_remove_data(t_hashtable * hashtable, void const * data, unsigned int hash) { t_entry * entry; int retval; if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return -1; } if (!(entry = hashtable_get_entry_by_data(hashtable, data, hash))) return -1; retval = hashtable_remove_entry(hashtable, entry); hashtable_entry_release(entry); return retval; } extern void * entry_get_data(t_entry const * entry) { if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL entry"); return NULL; } if (!entry->real) { eventlog(eventlog_level_error, __FUNCTION__, "entry has NULL real pointer"); return NULL; } if (entry->real->data == &nodata) { eventlog(eventlog_level_error, __FUNCTION__, "got deleted entry"); return NULL; } return entry->real->data; } extern void * hashtable_get_data_by_pos(t_hashtable const * hashtable, unsigned int pos) { t_internentry const * curr; unsigned int row; unsigned int len; if (!hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); return NULL; } len = 0; row = 0; curr = NULL; for (;;) { if (!curr) { if (row >= hashtable->num_rows) break; curr = hashtable->rows[row++]; continue; } if (curr->data != &nodata && len++ == pos) return curr->data; curr = curr->next; } eventlog(eventlog_level_error, __FUNCTION__, "requested position {} but len={}", pos, len); return NULL; } #ifdef HASHTABLE_DEBUG extern t_entry * hashtable_get_first_real(t_hashtable const * hashtable, char const * fn, unsigned int ln) #else extern t_entry * hashtable_get_first(t_hashtable const * hashtable) #endif { unsigned int row; t_internentry * curr; if (!hashtable) { #ifdef HASHTABLE_DEBUG eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable from {}:{}", fn, ln); #else eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); #endif return NULL; } for (row = 0; row < hashtable->num_rows; row++) for (curr = hashtable->rows[row]; curr; curr = curr->next) if (curr->data != &nodata) return hashtable_entry_export(curr, hashtable, row); return NULL; } extern t_entry * entry_get_next(t_entry * entry) { t_hashtable const * hashtable; unsigned int row; t_internentry * curr; if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL entry"); return NULL; } hashtable = entry->hashtable; for (curr = entry->real->next; curr; curr = curr->next) if (curr->data != &nodata) { entry->real = curr; return entry; } for (row = entry->row + 1; row < hashtable->num_rows; row++) for (curr = hashtable->rows[row]; curr; curr = curr->next) if (curr->data != &nodata) { entry->real = curr; entry->row = row; return entry; } hashtable_entry_release(entry); return NULL; } #ifdef HASHTABLE_DEBUG extern t_entry * hashtable_get_first_matching_real(t_hashtable const * hashtable, unsigned int hash, char const * fn, unsigned int ln) #else extern t_entry * hashtable_get_first_matching(t_hashtable const * hashtable, unsigned int hash) #endif { unsigned int row; t_internentry * curr; if (!hashtable) { #ifdef HASHTABLE_DEBUG eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable from {}:{}", fn, ln); #else eventlog(eventlog_level_error, __FUNCTION__, "got NULL hashtable"); #endif return NULL; } row = hash%hashtable->num_rows; for (curr = hashtable->rows[row]; curr; curr = curr->next) if (curr->data != &nodata) return hashtable_entry_export(curr, hashtable, row); return NULL; } extern t_entry * entry_get_next_matching(t_entry * entry) { t_internentry * curr; if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL entry"); return NULL; } for (curr = entry->real->next; curr; curr = curr->next) if (curr->data != &nodata) { entry->real = curr; return entry; } hashtable_entry_release(entry); return NULL; } extern int hashtable_entry_release(t_entry * entry) { if (!entry) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL entry"); return -1; } if (!entry->hashtable) { eventlog(eventlog_level_error, __FUNCTION__, "got entry with NULL hashtable"); return -1; } if (!entry->real) { eventlog(eventlog_level_error, __FUNCTION__, "got entry with NULL real pointer"); return -1; } if (entry->row >= entry->hashtable->num_rows) { eventlog(eventlog_level_error, __FUNCTION__, "entry has bad row {} (max {})", entry->row, entry->hashtable->num_rows - 1); return -1; } xfree(entry); return 0; } } pvpgn-1.99.7.2.1+dfsg/src/common/hashtable.h000066400000000000000000000102061462640461400203620ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HASHTABLE_TYPES #define INCLUDED_HASHTABLE_TYPES namespace pvpgn { #ifdef HASHTABLE_INTERNAL_ACCESS struct hashtable; /* forward reference for t_entry */ #endif #ifdef HASHTABLE_INTERNAL_ACCESS typedef struct internentry { void * data; struct internentry * next; } t_internentry; #endif typedef struct entry #ifdef HASHTABLE_INTERNAL_ACCESS { unsigned int row; t_internentry * real; struct hashtable const * hashtable; } #endif t_entry; typedef struct hashtable #ifdef HASHTABLE_INTERNAL_ACCESS { unsigned int num_rows; unsigned int len; unsigned int zombies; t_internentry * * rows; } #endif t_hashtable; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HASHTABLE_PROTOS #define INCLUDED_HASHTABLE_PROTOS namespace pvpgn { extern t_hashtable * hashtable_create(unsigned int num_rows); extern int hashtable_destroy(t_hashtable * hashtable); extern int hashtable_purge(t_hashtable * hashtable); extern int hashtable_check(t_hashtable const * hashtable); extern unsigned int hashtable_get_length(t_hashtable const * hashtable); extern int hashtable_insert_data(t_hashtable * hashtable, void * data, unsigned int hash); extern t_entry * hashtable_get_entry_by_data(t_hashtable const * hashtable, void const * data, unsigned int hash); extern t_entry const * hashtable_get_entry_by_data_const(t_hashtable const * hashtable, void const * data, unsigned int hash); extern int hashtable_remove_data(t_hashtable * hashtable, void const * data, unsigned int hash); /* delete matching item */ extern int hashtable_remove_entry(t_hashtable * hashtable, t_entry * entry); extern void * hashtable_get_data_by_pos(t_hashtable const * hashtable, unsigned int pos); extern void * entry_get_data(t_entry const * entry); #ifdef HASHTABLE_DEBUG extern t_entry * hashtable_match_get_first_real(t_hashtable const * hashtable, unsigned int hash, char const * fn, unsigned int ln); # define hashtable_match_get_first(L,H) hashtable_match_get_first_real(L,H,__FILE__,__LINE__) #else extern t_entry * hashtable_match_get_first(t_hashtable const * hashtable, unsigned int hash); #endif extern t_entry * entry_match_get_next(t_entry const * entry, unsigned int hash); #ifdef HASHTABLE_DEBUG extern t_entry * hashtable_get_first_real(t_hashtable const * hashtable, char const * fn, unsigned int ln); # define hashtable_get_first(L) hashtable_get_first_real(L,__FILE__,__LINE__) #else extern t_entry * hashtable_get_first(t_hashtable const * hashtable); #endif extern t_entry * entry_get_next(t_entry * entry); #ifdef HASHTABLE_DEBUG extern t_entry * hashtable_get_first_matching_real(t_hashtable const * hashtable, unsigned int hash, char const * fn, unsigned int ln); # define hashtable_get_first_matching(L,H) hashtable_get_first_matching_real(L,H,__FILE__,__LINE__) #else extern t_entry * hashtable_get_first_matching(t_hashtable const * hashtable, unsigned int hash); #endif extern t_entry * entry_get_next_matching(t_entry * entry); extern int hashtable_entry_release(t_entry * entry); extern int hashtable_stats(t_hashtable * hashtable); #define HASHTABLE_TRAVERSE(hashtable,curr) for (curr=hashtable_get_first(hashtable); curr; curr=entry_get_next(curr)) #define HASHTABLE_TRAVERSE_MATCHING(hashtable,curr,hash) for (curr=hashtable_get_first_matching(hashtable,hash); curr; curr=entry_get_next_matching(curr)) } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/hexdump.cpp000066400000000000000000000051761462640461400204460ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2004 Donny Redmond (dredmond@linuxmail.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/hexdump.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { extern void hexdump(std::FILE * stream, void const * data, unsigned int len) { unsigned int i; char dst[100]; unsigned char * datac; if (!data) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL data"); return; } if (!stream) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL stream"); return; } for (i = 0, datac = (unsigned char*)data; i < len; i += 16, datac += 16) { hexdump_string(datac, (len - i < 16) ? (len - i) : 16, dst, i); std::fprintf(stream, "%s\n", dst); std::fflush(stream); } } extern void hexdump_string(unsigned char * data, unsigned int datalen, char * dst, unsigned int counter) { unsigned int c; int tlen = 0; unsigned char *datatmp; datatmp = data; tlen += std::sprintf((dst + tlen), "%04X: ", counter); for (c = 0; c < 8; c++) /* left half of hex dump */ if (c < datalen) tlen += std::sprintf((dst + tlen), "%02X ", *(datatmp++)); else tlen += std::sprintf((dst + tlen), " "); /* pad if short line */ tlen += std::sprintf((dst + tlen), " "); for (c = 8; c < 16; c++) /* right half of hex dump */ if (c < datalen) tlen += std::sprintf((dst + tlen), "%02X ", *(datatmp++)); else tlen += std::sprintf((dst + tlen), " "); /* pad if short line */ tlen += std::sprintf((dst + tlen), " "); for (c = 0, datatmp = data; c < 16; c++, datatmp++) /* ASCII dump */ if (c < datalen) { if (*datatmp >= 32 && *datatmp < 127) tlen += std::sprintf((dst + tlen), "%c", *datatmp); else tlen += std::sprintf((dst + tlen), "."); /* put this for non-printables */ } } } pvpgn-1.99.7.2.1+dfsg/src/common/hexdump.h000066400000000000000000000023271462640461400201060ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2004 Donny Redmond (dredmond@linuxmail.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_HEXDUMP_PROTOS #define INCLUDED_HEXDUMP_PROTOS #include namespace pvpgn { extern void hexdump(std::FILE * stream, void const * data, unsigned int len); extern void hexdump_string(unsigned char * data, unsigned int datalen, char * dst, unsigned int counter); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/init_protocol.h000066400000000000000000000041201462640461400213110ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_INIT_PROTOCOL_TYPES #define INCLUDED_INIT_PROTOCOL_TYPES #ifdef JUST_NEED_TYPES # include "common/bn_type.h" #else # define JUST_NEED_TYPES # include "common/bn_type.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { /* There is no header for this packet class and there is only * one packet type. */ /* * There is a single byte sent upon initial connections that * reports the connection type. */ /******************************************************/ #define CLIENT_INITCONN 0x1 typedef struct { bn_byte cclass; } PACKED_ATTR() t_client_initconn; /******************************************************/ } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_INIT_PROTOCOL_PROTOS #define INCLUDED_INIT_PROTOCOL_PROTOS #define CLIENT_INITCONN_CLASS_BNET 0x01 /* standard bnet protocol */ #define CLIENT_INITCONN_CLASS_FILE 0x02 /* BNFTP */ #define CLIENT_INITCONN_CLASS_BOT 0x03 #define CLIENT_INITCONN_CLASS_ENC 0x04 /* encrypted connection */ #define CLIENT_INITCONN_CLASS_TELNET 0x0d /* Hack alert: look for user to hit \r when they connect */ #define CLIENT_INITCONN_CLASS_D2CS 0x01 #define CLIENT_INITCONN_CLASS_D2GS 0x64 #define CLIENT_INITCONN_CLASS_D2CS_BNETD 0x65 #define CLIENT_INITCONN_CLASS_LOCALMACHINE 0x98 /* local computer connecting via 127.0.0.1 */ #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/introtate.h000066400000000000000000000046361462640461400204520ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_INTROTATE_PROTOS #define INCLUDED_INTROTATE_PROTOS /* * ROTL(x,n,w) rotates "w" bit wide value "x" by "n" bits to the left * * The expression passed in as x must have a type at least as wide as w. * The type should probably be unsigned for this to be guaranteed to work * properly. If n or w is signed and larger than x remember that the * promotion rules would promote x to be of the same (signed) type. * * Unfortunately C doesn't have rotate operations and they can be difficult * to implement when handling rotates by zero, negative numbers, or numbers * greater or equal to the bit width of the number. This is because ANSI/ISO * C makes weak guarantees about the left and right shift operators. We * would like to not depend on word size, endianness, or how negative * integers are represented. Unfortunately only some of those goals may be * achieved. As for optimization, a really good compiler might be able to * recognize what we are doing and turn it into a single machine instruction. */ /* valid for 00 */ /*#define ROTL(x,n,w) (((x)<<(n)) | ((x)>>((w)-(n))))*/ /* valid for 0<=n0 */ /*#define ROTL(x,n,w) (((x)<<(n)) | ((x)>>(((-(n))&(w-1)))))*/ /* valid for 0<=n and w>0 , depends on 2's complement */ #define ROTL(x,n,w) (((x)<<((n)&(w-1))) | ((x)>>(((-(n))&(w-1))))) /* valid for 0<=n and w>0 , uses three mods and an ugly conditional */ /* FIXME: and also a bug because it doesn't work on PPC */ /*#define ROTL(x,n,w) (((n)%(w)) ? (((x)<<((n)%(w))) | ((x)>>((w)-((n)%(w))))) : (x))*/ #define ROTL32(x,n) ROTL(x,n,32) #define ROTL16(x,n) ROTL(x,n,16) #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/irc_protocol.h000066400000000000000000000506231462640461400211340ustar00rootroot00000000000000/* * Copyright (C) 2001 Marco Ziech (mmz@gmx.net) * Copyright (C) 2005 Bryan Biedenkapp (gatekeep@gmail.com) * Copyright (C) 2006,2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_IRC_PROTOCOL_TYPES #define INCLUDED_IRC_PROTOCOL_TYPES #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_INIT_PROTOCOL_PROTOS #define INCLUDED_INIT_PROTOCOL_PROTOS /* IRC Defines */ #define WOL_NICKNAME_LEN 9 #define CHANNEL_PREFIX "(ohv)@%+" #define CHANNEL_TYPE "#" /* Reply Defines */ /* The following defines were taken from include/numeric.h * of the undernet-ircd distribution. * See http://www.undernet.org/ */ /** reply's */ #define RPL_WELCOME 1 #define RPL_YOURHOST 2 #define RPL_CREATED 3 #define RPL_MYINFO 4 #define RPL_ISUPPORT 5 /* Undernet/Dalnet extension */ /* RPL_BOUNCE IRCnet extension */ /* RPL_MAP 6 unreal */ /* RPL_MAPEND 7 unreal */ #define RPL_SNOMASK 8 /* Undernet extension */ #define RPL_STATMEMTOT 9 /* Undernet extension */ #define RPL_STATMEM 10 /* Undernet extension */ /* RPL_YOURCOOKIE 14 IRCnet extension */ #define RPL_MAP 15 /* Undernet extension */ #define RPL_MAPMORE 16 /* Undernet extension */ #define RPL_MAPEND 17 /* Undernet extension */ /* RPL_YOURID 42 IRCnet extension */ /* RPL_ATTEMPTINGJUNC 50 aircd extension */ /* RPL_ATTEMPTINGREROUTE 51 aircd extension */ /* * Numberic replies from server commands. * These are currently in the range 200-399. */ #define RPL_TRACELINK 200 #define RPL_TRACECONNECTING 201 #define RPL_TRACEHANDSHAKE 202 #define RPL_TRACEUNKNOWN 203 #define RPL_TRACEOPERATOR 204 #define RPL_TRACEUSER 205 #define RPL_TRACESERVER 206 #define RPL_TRACENEWTYPE 208 #define RPL_TRACECLASS 209 /* RPL_STATS 210 aircd extension, used instead of having multiple stats numerics */ /* RPL_TRACERECONNECT 210 IRCnet extension */ #define RPL_STATSLINKINFO 211 #define RPL_STATSCOMMANDS 212 #define RPL_STATSCLINE 213 /* RPL_STATSOLDNLINE 214 unreal */ #define RPL_STATSNLINE 214 /* unused */ #define RPL_STATSILINE 215 #define RPL_STATSKLINE 216 #define RPL_STATSPLINE 217 /* Undernet extenstion */ /* RPL_STATSQLINE 217 Various */ #define RPL_STATSYLINE 218 #define RPL_ENDOFSTATS 219 /* See also RPL_STATSDLINE */ /* RPL_STATSPLINE 220 Hybrid - Because 217 was for old Q: lines. */ /* RPL_STATSBLINE 220 Numerics List: Dalnet,unreal */ #define RPL_UMODEIS 221 /* RPL_SQLINE_NICK 222 Numerics List: Dalnet */ /* RPL_STATSELINE 223 dalnet */ /* RPL_STATSGLINE 223 unreal */ /* RPL_STATSFLINE 224 Hybrid extension,Dalnet */ /* RPL_STATSTLINE 224 unreal */ /* RPL_STATSDLINE 225 Hybrid extension */ /* RPL_STATSZLINE 225 Dalnet RPL_STATSELINE 225 unreal RPL_STATSCOUNT 226 Dalnet RPL_STATSNLINE 226 unreal RPL_STATSGLINE 227 Dalnet RPL_STATSVLINE 227 unreal */ #define RPL_SERVICEINFO 231 /* unused */ #define RPL_ENDOFSERVICES 232 /* unused */ /* RPL_RULES 232 unreal */ #define RPL_SERVICE 233 /* unused */ #define RPL_SERVLIST 234 /* unused */ #define RPL_SERVLISTEND 235 /* unused */ #define RPL_STATSENGINE 237 /* Undernet engine name */ #define RPL_STATSFLINE 238 /* Undernet feature lines */ /* RPL_STATSIAUTH 239 IRCnet extension */ /* RPL_STATSVLINE 240 IRCnet extension */ /* RPL_STATSXLINE 240 austnet */ #define RPL_STATSLLINE 241 #define RPL_STATSUPTIME 242 #define RPL_STATSOLINE 243 #define RPL_STATSHLINE 244 /* RPL_STATSSLINE 245 Reserved / Dalnet / IRCnet / EFnet */ /* RPL_STATSSPING 246 Numerics List: IRCnet */ #define RPL_STATSTLINE 246 /* Undernet extension */ /* RPL_STATSULINE 246 Dalnet */ #define RPL_STATSGLINE 247 /* Undernet extension */ /* RPL_STATSXLINE 247 hybrid extension,PTlink,unreal */ /* RPL_STATSBLINE 247 Numerics List: IRCnet */ #define RPL_STATSULINE 248 /* Undernet extension */ /* RPL_STATSDEFINE 248 Numerics List: IRCnet */ #define RPL_STATSDEBUG 249 /* Extension to RFC1459 */ #define RPL_STATSCONN 250 /* Undernet extension */ /* RPL_STATSDLINE 250 Numerics List: IRCnet */ #define RPL_LUSERCLIENT 251 #define RPL_LUSEROP 252 #define RPL_LUSERUNKNOWN 253 #define RPL_LUSERCHANNELS 254 #define RPL_LUSERME 255 #define RPL_ADMINME 256 #define RPL_ADMINLOC1 257 #define RPL_ADMINLOC2 258 #define RPL_ADMINEMAIL 259 #define RPL_TRACELOG 261 /* unused */ #define RPL_TRACEPING 262 /* Extension to RFC1459, unused */ /* RPL_TRACEEND 262 efnet(?) Numerics List: IRCnet */ /* RPL_LOAD_THROTTLED 263 efnet/hybrid */ /* RPL_TRYAGAIN 263 Numerics List: IRCnet */ /* RPL_LOAD2HI 263 Dalnet */ /* RPL_CURRENT_LOCAL 265 aircd/efnet/hybrid/dalnet*/ /* RPL_CURRENT_GLOBAL 266 aircd/efnet/hybrid/dalnet */ /* RPL_START_NETSTAT 267 aircd */ /* RPL_NETSTAT 268 aircd */ /* RPL_END_NETSTAT 269 aircd */ #define RPL_PRIVS 270 /* Undernet extension - privs */ #define RPL_SILELIST 271 /* Undernet extension */ #define RPL_ENDOFSILELIST 272 /* Undernet extension */ /* RPL_NOTIFY 273 aircd */ /* RPL_END_NOTIFY 274 aircd */ /* RPL_STATSDELTA 274 IRCnet extension */ #define RPL_STATSDLINE 275 /* Undernet extension */ #define RPL_GLIST 280 /* Undernet extension */ #define RPL_ENDOFGLIST 281 /* Undernet extension */ #define RPL_JUPELIST 282 /* Undernet extension - jupe -Kev */ #define RPL_ENDOFJUPELIST 283 /* Undernet extension - jupe -Kev */ #define RPL_FEATURE 284 /* Undernet extension - features */ /* RPL_CHANINFO_HANDLE 285 aircd */ /* RPL_CHANINFO_USERS 286 aircd */ /* RPL_CHANINFO_CHOPS 287 aircd */ /* RPL_CHANINFO_VOICES 288 aircd */ /* RPL_CHANINFO_AWAY 289 aircd */ /* RPL_CHANINFO_OPERS 290 aircd */ /* RPL_HELPHDR 290 Numeric List: Dalnet */ /* RPL_CHANINFO_BANNED 291 aircd */ /* RPL_HELPOP 291 Numeric List: Dalnet */ /* RPL_CHANINFO_BANS 292 aircd */ /* RPL_HELPTLR 292 Numeric List: Dalnet */ /* RPL_CHANINFO_INVITE 293 aircd */ /* RPL_HELPHLP 293 Numeric List: Dalnet */ /* RPL_CHANINFO_INVITES 294 aircd */ /* RPL_HELPFWD 294 Numeric List: Dalnet */ /* RPL_CHANINFO_KICK 295 aircd */ /* RPL_HELPIGN 295 Numeric List: Dalnet */ /* RPL_CHANINFO_KICKS 296 aircd */ /* RPL_END_CHANINFO 299 aircd */ #define RPL_NONE 300 /* unused */ #define RPL_AWAY 301 #define RPL_USERHOST 302 #define RPL_ISON 303 #define RPL_TEXT 304 /* unused */ #define RPL_UNAWAY 305 #define RPL_NOWAWAY 306 #define RPL_USERIP 307 /* Undernet extension */ /* NotAway, aircd */ /* RPL_WHOISREGNICK 307 Numeric List: Dalnet */ /* RPL_SUSERHOST 307 austnet */ /* RPL_NOTIFYACTION 308 aircd */ /* RPL_WHOISADMIN 308 Numeric List: Dalnet */ /* RPL_RULESSTART 308 unreal */ /* RPL_NICKTRACE 309 aircd */ /* RPL_WHOISSADMIN 309 Numeric List: Dalnet */ /* RPL_ENDOFRULES 309 unreal */ /* RPL_WHOISHELPER 309 austnet */ /* RPL_WHOISSVCMSG 310 Dalnet */ /* RPL_WHOISHELPOP 310 unreal */ /* RPL_WHOISSERVICE 310 austnet */ #define RPL_WHOISUSER 311 /* See also RPL_ENDOFWHOIS */ #define RPL_WHOISSERVER 312 #define RPL_WHOISOPERATOR 313 #define RPL_WHOWASUSER 314 /* See also RPL_ENDOFWHOWAS */ #define RPL_ENDOFWHO 315 /* See RPL_WHOREPLY/RPL_WHOSPCRPL */ /* RPL_WHOISCHANOP 316 removed from RFC1459 */ #define RPL_WHOISIDLE 317 #define RPL_ENDOFWHOIS 318 /* See RPL_WHOISUSER/RPL_WHOISSERVER/ RPL_WHOISOPERATOR/RPL_WHOISIDLE */ #define RPL_WHOISCHANNELS 319 /* RPL_WHOIS_HIDDEN 320 Anothernet +h, ick! */ /* RPL_WHOISSPECIAL 320 unreal */ #define RPL_LISTSTART 321 #define RPL_LIST 322 #define RPL_LISTEND 323 #define RPL_CHANNELMODEIS 324 /* RPL_CHANNELPASSIS 325 IRCnet extension */ /* RPL_UNIQOPIS 325 IRCnet extension */ /* RPL_NOCHANPASS 326 IRCnet extension */ /* RPL_CHPASSUNKNOWN 327 IRCnet extension */ /* RPL_CHANNEL_URL 328 dalnet, anothernet */ #define RPL_CREATIONTIME 329 /* RPL_WHOWAS_TIME 330 ? */ #define RPL_NOTOPIC 331 #define RPL_TOPIC 332 #define RPL_TOPICWHOTIME 333 /* Undernet extension */ #define RPL_LISTUSAGE 334 /* Undernet extension */ /* RPL_COMMANDSYNTAX 334 Dalnet */ /* RPL_LISTSYNTAX 334 unreal */ /* RPL_CHANPASSOK 338 IRCnet extension (?)*/ /* RPL_WHOISACTUALLY 338 dalnet */ /* RPL_BADCHANPASS 339 IRCnet extension (?)*/ #define RPL_INVITING 341 /* RPL_SUMMONING 342 removed from RFC1459 */ #define RPL_INVITELIST 346 /* IRCnet, Undernet extension */ #define RPL_ENDOFINVITELIST 347 /* IRCnet, Undernet extension */ /* RPL_EXCEPTLIST 348 IRCnet extension */ /* RPL_ENDOFEXCEPTLIST 349 IRCnet extension */ #define RPL_VERSION 351 #define RPL_WHOREPLY 352 /* See also RPL_ENDOFWHO */ #define RPL_NAMREPLY 353 /* See also RPL_ENDOFNAMES */ #define RPL_WHOSPCRPL 354 /* Undernet extension, See also RPL_ENDOFWHO */ #define RPL_KILLDONE 361 /* not used */ #define RPL_CLOSING 362 #define RPL_CLOSEEND 363 #define RPL_LINKS 364 #define RPL_ENDOFLINKS 365 #define RPL_ENDOFNAMES 366 /* See RPL_NAMREPLY */ #define RPL_BANLIST 367 #define RPL_ENDOFBANLIST 368 #define RPL_ENDOFWHOWAS 369 #define RPL_INFO 371 #define RPL_MOTD 372 #define RPL_INFOSTART 373 /* not used */ #define RPL_ENDOFINFO 374 #define RPL_MOTDSTART 375 #define RPL_ENDOFMOTD 376 /* RPL_KICKEXPIRED 377 aircd */ /* RPL_SPAM 377 austnet */ /* RPL_BANEXPIRED 378 aircd */ /* RPL_KICKLINKED 379 aircd */ /* RPL_BANLINKED 380 aircd */ #define RPL_YOUREOPER 381 #define RPL_REHASHING 382 /* RPL_YOURSERVICE 383 Numeric List: various */ #define RPL_MYPORTIS 384 /* not used */ #define RPL_NOTOPERANYMORE 385 /* Extension to RFC1459, not used */ /* RPL_QLIST 386 unreal */ /* RPL_ENDOFQLIST 387 unreal */ /* RPL_ALIST 388 unreal */ /* RPL_ENDOFALIST 389 unreal */ #define RPL_TIME 391 /* RPL_START_USERS 392 Dalnet/EFnet/IRCnet */ /* RPL_USERS 393 Dalnet/EFnet/IRCnet */ /* RPL_END_USERS 394 Dalnet/EFnet/IRCnet */ /* RPL_NOUSERS 395 Dalnet/EFnet/IRCnet */ /* * Numberic replies from server commands. * These are currently in the range 600-699. */ /* RPL_LOGON 600 dalnet,unreal RPL_LOGOFF 601 dalnet,unreal RPL_WATCHOFF 602 dalnet,unreal RPL_WATCHSTAT 603 dalnet,unreal RPL_NOWON 604 dalnet,unreal RPL_NOWOFF 605 dalnet,unreal RPL_WATCHLIST 606 dalnet,unreal RPL_ENDOFWATCHLIST 607 dalnet,unreal RPL_MAPMORE 610 unreal RPL_MAPMORE 615 PTlink RPL_DCCSTATUS 617 dalnet RPL_DCCLIST 618 dalnet RPL_ENDOFDCCLIST 619 dalnet RPL_DCCINFO 620 dalnet RPL_DUMPING 640 unreal RPL_DUMPRPL 641 unreal RPL_EODUMP 642 unreal */ /** * Westwood Online Extensions */ #define RPL_GET_LOCALE 309 #define RPL_SET_LOCALE 310 #define RPL_GAME_CHANNEL 326 #define RPL_CHANNEL 327 #define RPL_GET_CODEPAGE 328 #define RPL_SET_CODEPAGE 329 #define RPL_GET_BUDDY 333 #define RPL_ADD_BUDDY 334 #define RPL_DEL_BUDDY 335 #define RPL_BATTLECLAN 358 #define RPL_ANNOUNCE 377 /* only in WOLv2 */ #define RPL_BAD_LOGIN 378 #define RPL_VERCHK_NONREQ 379 /* for WOL */ #define RPL_FIND_USER 388 #define RPL_PAGE 389 #define RPL_FIND_USER_EX 398 #define RPL_GET_INSIDER 399 /* Westwood Online servserv replies are in range 600 - 615 */ #define RPL_UPDATE_NONEX 602 /* for servserv */ #define RPL_UPDATE_EXIST 603 /* for servserv */ #define RPL_WOLSERV 605 #define RPL_UPDATE_FTP 606 /* for servserv FTP message */ #define RPL_QUIT 607 #define RPL_GAMERESSERV 608 #define RPL_LADDERSERV 609 #define RPL_LOBCOUNT 610 #define RPL_WDTSERV 611 #define RPL_MANGLERSERV 612 #define RPL_TICKETSERV 613 #define RPL_PINGSERVER 615 #define ERR_IDNOEXIST 439 //#define ERR_CHANNELISFULL 471 (used for games too) //#define ERR_BANNEDFROMCHAN 474 (used for games too) //#define ERR_BADCHANNELKEY 475 (used for game password) #define ERR_GAMEHASCLOSED 478 /** error's */ /* * Errors are in the range from 400-599 currently and are grouped by what * commands they come from. */ #define ERR_FIRSTERROR 400 /* unused */ #define ERR_NOSUCHNICK 401 #define ERR_NOSUCHSERVER 402 #define ERR_NOSUCHCHANNEL 403 #define ERR_CANNOTSENDTOCHAN 404 #define ERR_TOOMANYCHANNELS 405 #define ERR_WASNOSUCHNICK 406 #define ERR_TOOMANYTARGETS 407 /* ERR_NOSUCHSERVICE 408 IRCnet */ /* ERR_NOCOLORSONCHAN 408 Dalnet */ #define ERR_NOORIGIN 409 #define ERR_NORECIPIENT 411 #define ERR_NOTEXTTOSEND 412 #define ERR_NOTOPLEVEL 413 #define ERR_WILDTOPLEVEL 414 /* ERR_BADMASK 415 IRCnet extension */ #define ERR_QUERYTOOLONG 416 /* Undernet extension */ /* ERR_TOOMANYMATCHES 416 IRCnet extension */ /* ERR_LENGTHTRUNCATED 419 aircd */ #define ERR_UNKNOWNCOMMAND 421 #define ERR_NOMOTD 422 #define ERR_NOADMININFO 423 /* ERR_FILEERROR 424 removed from RFC1459 */ /* ERR_TOOMANYAWAY 429 Dalnet */ #define ERR_NONICKNAMEGIVEN 431 #define ERR_ERRONEUSNICKNAME 432 #define ERR_NICKNAMEINUSE 433 /* ERR_SERVICENAMEINUSE 434 ? */ /* ERR_NORULES 434 unreal */ /* ERR_SERVICECONFUSED 435 ? */ /* ERR_BANONCHAN 435 dalnet */ #define ERR_NICKCOLLISION 436 #define ERR_BANNICKCHANGE 437 /* Undernet extension */ /* ERR_UNAVAILRESOURCE 437 IRCnet extension */ #define ERR_NICKTOOFAST 438 /* Undernet extension */ /* ERR_DEAD 438 IRCnet reserved for later use */ #define ERR_TARGETTOOFAST 439 /* Undernet extension */ /* ERR_SERVICESDOWN 440 Dalnet,unreal */ #define ERR_USERNOTINCHANNEL 441 #define ERR_NOTONCHANNEL 442 #define ERR_USERONCHANNEL 443 /* ERR_NOLOGIN 444 removed from RFC1459 */ /* ERR_SUMMONDISABLED 445 removed from RFC1459 */ /* ERR_USERSDISABLED 446 removed from RFC1459 */ /* ERR_NONICKCHANGE 447 unreal */ #define ERR_NOTREGISTERED 451 /* ERR_IDCOLLISION 452 IRCnet extension ? */ /* ERR_NICKLOST 453 IRCnet extension ? */ /* ERR_HOSTILENAME 455 unreal */ /* ERR_NOHIDING 459 unreal */ /* ERR_NOTFORHALFOPS 460 unreal */ #define ERR_NEEDMOREPARAMS 461 #define ERR_ALREADYREGISTRED 462 #define ERR_NOPERMFORHOST 463 #define ERR_PASSWDMISMATCH 464 #define ERR_YOUREBANNEDCREEP 465 #define ERR_YOUWILLBEBANNED 466 #define ERR_KEYSET 467 /* Undernet extension */ #define ERR_INVALIDUSERNAME 468 /* Undernet extension */ /* ERR_ONLYSERVERSCANCHANGE 468 Dalnet,unreal */ /* ERR_LINKSET 469 unreal */ /* ERR_LINKCHANNEL 470 unreal */ /* ERR_KICKEDFROMCHAN 470 aircd */ #define ERR_CHANNELISFULL 471 #define ERR_UNKNOWNMODE 472 #define ERR_INVITEONLYCHAN 473 #define ERR_BANNEDFROMCHAN 474 #define ERR_BADCHANNELKEY 475 #define ERR_BADCHANMASK 476 /* Undernet extension */ /* ERR_NEEDREGGEDNICK 477 DalNet Extention */ #define ERR_BANLISTFULL 478 /* Undernet extension */ /* ERR_LINKFAIL 479 unreal */ #define ERR_BADCHANNAME 479 /* EFNet extension */ /* 479 Undernet extension badchan */ /* ERR_CANNOTKNOCK 480 unreal */ /* ERR_NOULINE 480 austnet */ #define ERR_NOPRIVILEGES 481 #define ERR_CHANOPRIVSNEEDED 482 #define ERR_CANTKILLSERVER 483 #define ERR_ISCHANSERVICE 484 /* Undernet extension */ /* ERR_DESYNC 484 Dalnet,PTlink */ /* ERR_ATTACKDENY 484 unreal */ #define ERR_RESTRICTED 484 /* IRCnet extension */ /* ERR_UNIQOPRIVSNEEDED 485 IRCnet extension */ /* ERR_KILLDENY 485 unreal */ /* ERR_CANTKICKADMIN 485 PTlink */ /* ERR_HTMDISABLED 486 unreal */ /* ERR_CHANTOORECENT 487 IRCnet extension (?) */ /* ERR_TSLESSCHAN 488 IRCnet extension (?) */ #define ERR_VOICENEEDED 489 /* Undernet extension */ #define ERR_NOOPERHOST 491 /* ERR_NOSERVICEHOST 492 IRCnet extension */ #define ERR_NOFEATURE 493 /* Undernet extension - features */ #define ERR_BADFEATVALUE 494 /* Undernet extension - features */ #define ERR_BADLOGTYPE 495 /* Undernet extension - features */ #define ERR_BADLOGSYS 496 /* Undernet extension - features */ #define ERR_BADLOGVALUE 497 /* Undernet extension - features */ #define ERR_ISOPERLCHAN 498 /* Undernet extension */ #define ERR_UMODEUNKNOWNFLAG 501 #define ERR_USERSDONTMATCH 502 /* ERR_GHOSTEDCLIENT 503 efnet */ /* ERR_VWORLDWARN 503 austnet */ #define ERR_SILELISTFULL 511 /* Undernet extension */ /* ERR_NOTIFYFULL 512 aircd */ /* ERR_TOOMANYWATCH 512 Numeric List: Dalnet */ #define ERR_NOSUCHGLINE 512 /* Undernet extension */ #define ERR_BADPING 513 /* Undernet extension */ /* ERR_NEEDPONG 512 Numeric List: Dalnet */ #define ERR_NOSUCHJUPE 514 /* Undernet extension - jupe -Kev */ /* ERR_TOOMANYDCC 514 dalnet */ #define ERR_BADEXPIRE 515 /* Undernet extension - jupe -Kev */ #define ERR_DONTCHEAT 516 /* Undernet extension */ #define ERR_DISABLED 517 /* Undernet extension -Kev */ /* ERR_NOINVITE 518 unreal */ #define ERR_LONGMASK 518 /* Undernet extension -Kev */ /* ERR_ADMONLY 519 unreal */ #define ERR_TOOMANYUSERS 519 /* Undernet extension -Kev */ /* ERR_OPERONLY 520 unreal */ #define ERR_MASKTOOWIDE 520 /* Undernet extension -Kev */ /* ERR_WHOTRUNC 520 austnet */ #define ERR_LASTERROR 521 /* ERR_LISTSYNTAX 521 dalnet ERR_WHOSYNTAX 522 dalnet ERR_WHOLIMEXCEED 523 dalnet */ #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/list.cpp000066400000000000000000000141261462640461400177420ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define LIST_INTERNAL_ACCESS #include "common/list.h" #include #include "common/eventlog.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { static t_elem listhead; extern t_list * list_create(void) { t_list * newl; newl = (t_list*)xmalloc(sizeof(t_list)); newl->head = NULL; newl->tail = NULL; newl->len = 0; return newl; } extern int list_destroy(t_list * list) { if (!list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); return -1; } if (list->head) eventlog(eventlog_level_error, __FUNCTION__, "got non-empty list"); xfree(list); return 0; } extern unsigned int list_get_length(t_list const * list) { if (!list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); return 0; } return list->len; } extern int list_prepend_data(t_list * list, void * data) { t_elem * elem; assert(list != NULL); elem = (t_elem*)xmalloc(sizeof(t_elem)); elem->data = data; if (list->head) list->head->prev = elem; elem->next = list->head; elem->prev = NULL; list->head = elem; if (!list->tail) list->tail = elem; list->len++; return 0; } extern int list_append_data(t_list * list, void * data) { t_elem * elem; assert(list != NULL); elem = (t_elem*)xmalloc(sizeof(t_elem)); elem->data = data; elem->next = NULL; if (!list->head) { list->head = elem; elem->prev = NULL; } if (list->tail) { elem->prev = list->tail; list->tail->next = elem; } list->tail = elem; list->len++; return 0; } extern t_elem * list_get_elem_by_data(t_list const * list, void const * data) { t_elem * curr; if (!list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); return NULL; } LIST_TRAVERSE(list, curr) if (curr->data == data) return curr; return NULL; } extern t_elem const * list_get_elem_by_data_const(t_list const * list, void const * data) { t_elem const * curr; if (!list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); return NULL; } LIST_TRAVERSE_CONST(list, curr) if (curr->data == data) return curr; return NULL; } extern int list_remove_elem(t_list * list, t_elem ** elem) { t_elem * target; if (!list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); return -1; } if (!elem) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL *elem"); return -1; } target = *elem; if (target->prev) { target->prev->next = target->next; } if (target->next) { target->next->prev = target->prev; } if (target == list->tail) { list->tail = target->prev; } if (target == list->head) { list->head = target->next; *elem = &listhead; } else *elem = target->prev; target->next = NULL; target->prev = NULL; xfree(target); list->len--; return 0; } extern int list_remove_data(t_list * list, void const * data, t_elem ** elem) { if (!list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); return -1; } if (!(*elem = list_get_elem_by_data(list, data))) return -1; return list_remove_elem(list, elem); } extern int elem_set_data(t_elem * elem, void * data) { if (!elem) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem"); return -1; } elem->data = data; return 0; } extern void * elem_get_data(t_elem const * elem) { if (!elem) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem"); return NULL; } return elem->data; } extern void * list_get_data_by_pos(t_list const * list, unsigned int pos) { t_elem const * curr; unsigned int len; len = 0; LIST_TRAVERSE_CONST(list, curr) if (len++ == pos) return curr->data; eventlog(eventlog_level_error, __FUNCTION__, "requested position {} but len={}", pos, len); return NULL; } #ifdef LIST_DEBUG extern t_elem * list_get_first_real(t_list const * list, char const * fn, unsigned int ln) #else extern t_elem * list_get_first(t_list const * list) #endif { if (!list) { #ifdef LIST_DEBUG eventlog(eventlog_level_error, __FUNCTION__, "got NULL list from {}:{}", fn, ln); #else eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); #endif return NULL; } return list->head; } #ifdef LIST_DEBUG extern t_elem const * list_get_first_const_real(t_list const * list, char const * fn, unsigned int ln) #else extern t_elem const * list_get_first_const(t_list const * list) #endif { if (!list) { #ifdef LIST_DEBUG eventlog(eventlog_level_error, __FUNCTION__, "got NULL list from {}:{}", fn, ln); #else eventlog(eventlog_level_error, __FUNCTION__, "got NULL list"); #endif return NULL; } return list->head; } extern t_elem * elem_get_next_real(t_list const * list, t_elem const * elem, char const * fn, unsigned int ln) { if (!elem) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem from {}:{}", fn, ln); return NULL; } if (elem == &listhead) return list->head; else return elem->next; } extern t_elem const * elem_get_next_const(t_list const * list, t_elem const * elem) { if (!elem) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL elem"); return NULL; } if (elem == &listhead) return list->head; else return elem->next; } } pvpgn-1.99.7.2.1+dfsg/src/common/list.h000066400000000000000000000067601462640461400174140ustar00rootroot00000000000000/* * Copyright (C) 1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_LIST_TYPES #define INCLUDED_LIST_TYPES namespace pvpgn { typedef struct elem #ifdef LIST_INTERNAL_ACCESS { void * data; struct elem * prev; struct elem * next; } #endif t_elem; typedef struct list #ifdef LIST_INTERNAL_ACCESS { unsigned int len; t_elem * head; t_elem * tail; } #endif t_list; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_LIST_PROTOS #define INCLUDED_LIST_PROTOS namespace pvpgn { extern t_list * list_create(void); extern int list_destroy(t_list * list); extern unsigned int list_get_length(t_list const * list); extern int list_prepend_data(t_list * list, void * data); extern int list_append_data(t_list * list, void * data); extern t_elem * list_get_elem_by_data(t_list const * list, void const * data); extern t_elem const * list_get_elem_by_data_const(t_list const * list, void const * data); /* note changed API for those commands: due to direct removal of elements from list, you need to take special care during list traversal. a pointer to the traversal variable needs to be passed to the list_remove functions, so they can properly modify it to point to the "previous" element (the one before the element to be deleted) so the next elem_get_next call will address the "next" element (the one after the element to be deleted) */ extern int list_remove_data(t_list * list, void const * data, t_elem ** elem); /* delete matching item */ extern int list_remove_elem(t_list * list, t_elem ** elem); extern void * list_get_data_by_pos(t_list const * list, unsigned int pos); #ifdef LIST_DEBUG extern t_elem * list_get_first_real(t_list const * list, char const * fn, unsigned int ln); # define list_get_first(L) list_get_first_real(L,__FILE__,__LINE__) #else extern t_elem * list_get_first(t_list const * list); #endif #ifdef LIST_DEBUG extern t_elem const * list_get_first_const_real(t_list const * list, char const * fn, unsigned int ln); # define list_get_first_const(L) list_get_first_const_real(L,__FILE__,__LINE__) #else extern t_elem const * list_get_first_const(t_list const * list); #endif extern void * elem_get_data(t_elem const * elem); extern int elem_set_data(t_elem * elem, void * data); #define elem_get_next(list,elem) elem_get_next_real(list,elem,__FILE__,__LINE__) extern t_elem * elem_get_next_real(t_list const * list, t_elem const * elem, char const * fn, unsigned int ln); extern t_elem const * elem_get_next_const(t_list const * list, t_elem const * elem); #define LIST_TRAVERSE(list,curr) for (curr=(list)?list_get_first(list):(NULL); curr; curr=elem_get_next(list,curr)) #define LIST_TRAVERSE_CONST(list,curr) for (curr=(list)?list_get_first_const(list):(NULL); curr; curr=elem_get_next_const(list,curr)) } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/lstr.h000066400000000000000000000026441462640461400174220ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * lstr is a structure to be used for cached string lengths for speed * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_LSTR_TYPES #define INCLUDED_LSTR_TYPES namespace pvpgn { typedef struct lstr { char *str; unsigned len; } t_lstr; } #endif /* INCLUDED_LSTR_TYPES */ #ifndef INCLUDED_LSTR_PROTOS #define INCLUDED_LSTR_PROTOS namespace pvpgn { static inline void lstr_set_str(t_lstr *lstr, char *str) { lstr->str = str; } static inline char *lstr_get_str(t_lstr *lstr) { return lstr->str; } static inline void lstr_set_len(t_lstr *lstr, unsigned len) { lstr->len = len; } static inline unsigned lstr_get_len(t_lstr *lstr) { return lstr->len; } } #endif /* INCLUDED_LSTR_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/common/make_unique.hpp000066400000000000000000000033471462640461400213020ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAKE_UNIQUE_H #define MAKE_UNIQUE_H #include "common/setup_before.h" #ifndef HAVE_MAKE_UNIQUE #include #include #include namespace pvpgn { //https://isocpp.org/files/papers/N3656.txt template struct _Unique_if { typedef std::unique_ptr _Single_object; }; template struct _Unique_if { typedef std::unique_ptr _Unknown_bound; }; template struct _Unique_if { typedef void _Known_bound; }; template typename _Unique_if::_Single_object make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } template typename _Unique_if::_Unknown_bound make_unique(size_t n) { typedef typename remove_extent::type U; return std::unique_ptr(new U[n]()); } template typename _Unique_if::_Known_bound make_unique(Args&&...) = delete; } #else #define make_unique std::make_unique #endif //HAVE_MAKE_UNIQUE #endif //MAKE_UNIQUE_Hpvpgn-1.99.7.2.1+dfsg/src/common/network.cpp000066400000000000000000000130331462640461400204540ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/network.h" #include #include #include "compat/socket.h" #include "compat/recv.h" #include "compat/send.h" #include "compat/netinet_in.h" #include "compat/psock.h" #include "common/packet.h" #include "common/eventlog.h" #include "common/field_sizes.h" #include "common/setup_after.h" namespace pvpgn { extern int net_recv(int sock, void *buff, int len) { int res; res = psock_recv(sock, buff, len, 0); if (res > 0) return res; if (!res) return -1; /* connection closed */ if ( #ifdef PSOCK_EINTR psock_errno() == PSOCK_EINTR || #endif #ifdef PSOCK_EAGAIN psock_errno() == PSOCK_EAGAIN || #endif #ifdef PSOCK_EWOULDBLOCK psock_errno() == PSOCK_EWOULDBLOCK || #endif #ifdef PSOCK_ENOMEM psock_errno() == PSOCK_ENOMEM || #endif 0) /* try later */ return 0; if ( #ifdef PSOCK_ENOTCONN psock_errno() == PSOCK_ENOTCONN || #endif #ifdef PSOCK_ECONNRESET psock_errno() == PSOCK_ECONNRESET || #endif 0) { /* eventlog(eventlog_level_debug,__FUNCTION__,"[{}] remote host closed connection (psock_recv: {})",sock,std::strerror(psock_errno())); */ return -1; /* common error: close connection, but no message */ } eventlog(eventlog_level_debug, __FUNCTION__, "[{}] receive error (closing connection) (psock_recv: {})", sock, std::strerror(psock_errno())); return -1; } extern int net_recv_packet(int sock, t_packet * packet, unsigned int * currsize) { int addlen; unsigned int header_size; void * temp; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet (closing connection)", sock); return -1; } if (!currsize) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL currsize (closing connection)", sock); return -1; } if ((header_size = packet_get_header_size(packet)) >= MAX_PACKET_SIZE) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not determine header size (closing connection)", sock); return -1; } if (!(temp = packet_get_raw_data_build(packet, *currsize))) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not obtain raw data pointer at offset {} (closing connection)", sock, *currsize); return -1; } if (*currsize < header_size) addlen = net_recv(sock, temp, header_size - *currsize); else { unsigned int total_size = packet_get_size(packet); if (total_size < header_size) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] corrupted packet received (total_size={} currsize={}) (closing connection)", sock, total_size, *currsize); return -1; } if (*currsize >= total_size) { eventlog(eventlog_level_warn, __FUNCTION__, "[{}] more data requested for already complete packet (total_size={} currsize={}) (closing connection)", sock, total_size, *currsize); return -1; } addlen = net_recv(sock, temp, total_size - *currsize); } if (addlen <= 0) return addlen; *currsize += addlen; if (*currsize >= header_size && *currsize == packet_get_size(packet)) return 1; return 0; } extern int net_send(int sock, const void *buff, int len) { int res; res = psock_send(sock, buff, len, 0); if (res > 0) return res; if (!res) return -1; if ( #ifdef PSOCK_EINTR psock_errno() == PSOCK_EINTR || #endif #ifdef PSOCK_EAGAIN psock_errno() == PSOCK_EAGAIN || #endif #ifdef PSOCK_EWOULDBLOCK psock_errno() == PSOCK_EWOULDBLOCK || #endif #ifdef PSOCK_ENOBUFS psock_errno() == PSOCK_ENOBUFS || #endif #ifdef PSOCK_ENOMEM psock_errno() == PSOCK_ENOMEM || #endif 0) return 0; /* try again later */ if ( #ifdef PSOCK_EPIPE psock_errno() != PSOCK_EPIPE && #endif #ifdef PSOCK_ECONNRESET psock_errno() != PSOCK_ECONNRESET && #endif 1) eventlog(eventlog_level_debug, __FUNCTION__, "[{}] could not send data (closing connection) (psock_send: {})", sock, std::strerror(psock_errno())); return -1; } extern int net_send_packet(int sock, t_packet const * packet, unsigned int * currsize) { unsigned int size; int addlen; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet (closing connection)", sock); return -1; } if (!currsize) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL currsize (closing connection)", sock); return -1; } if ((size = packet_get_size(packet)) < 1) { eventlog(eventlog_level_error, __FUNCTION__, "[{}] packet to send is empty (skipping it)", sock); *currsize = 0; return 1; } addlen = net_send(sock, packet_get_raw_data_const(packet, *currsize), size - *currsize); if (addlen <= 0) return addlen; *currsize += addlen; /* sent all data in this packet? */ if (size == *currsize) { *currsize = 0; return 1; } return 0; } } pvpgn-1.99.7.2.1+dfsg/src/common/network.h000066400000000000000000000024731462640461400201270ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_NETWORK_PROTOS #define INCLUDED_NETWORK_PROTOS #define JUST_NEED_TYPES #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { extern int net_recv(int sock, void *buff, int len); extern int net_send(int sock, const void *buff, int len); extern int net_recv_packet(int sock, t_packet * packet, unsigned int * currsize); extern int net_send_packet(int sock, t_packet const * packet, unsigned int * currsize); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/packet.cpp000066400000000000000000001203211462640461400202310ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999,2000,2001 Marco Ziech (mmz@gmx.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/packet.h" #include #include "common/eventlog.h" #include "common/bn_type.h" #include "common/field_sizes.h" #include "common/xalloc.h" #include "common/lstr.h" #include "common/setup_after.h" namespace pvpgn { extern t_packet * packet_create(t_packet_class pclass) { t_packet * temp; if (pclass != packet_class_init && pclass != packet_class_bnet && pclass != packet_class_file && pclass != packet_class_udp && pclass != packet_class_raw && pclass != packet_class_d2game && pclass != packet_class_d2cs && pclass != packet_class_d2gs && pclass != packet_class_d2cs_bnetd && pclass != packet_class_w3route && pclass != packet_class_wolgameres) { eventlog(eventlog_level_error, __FUNCTION__, "invalid packet class {}", (int)pclass); return NULL; } temp = (t_packet*)xmalloc(sizeof(t_packet)); temp->ref = 1; temp->pclass = pclass; temp->flags = 0; packet_set_size(temp, 0); return temp; } extern void packet_destroy(t_packet const * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return; } xfree((void *)packet); /* avoid warning */ } extern t_packet * packet_add_ref(t_packet * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return NULL; } packet->ref++; return packet; } extern void packet_del_ref(t_packet * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return; } if (packet->ref < 2) /* if would go to zero */ packet_destroy(packet); else packet->ref--; } extern t_packet_class packet_get_class(t_packet const * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return packet_class_none; } switch (packet->pclass) { case packet_class_init: return packet_class_init; case packet_class_bnet: return packet_class_bnet; case packet_class_file: return packet_class_file; case packet_class_udp: return packet_class_udp; case packet_class_raw: return packet_class_raw; case packet_class_d2game: return packet_class_d2game; case packet_class_d2cs: return packet_class_d2cs; case packet_class_d2gs: return packet_class_d2gs; case packet_class_d2cs_bnetd: return packet_class_d2cs_bnetd; case packet_class_w3route: return packet_class_w3route; case packet_class_wolgameres: return packet_class_wolgameres; case packet_class_none: return packet_class_none; default: eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return packet_class_none; } } extern char const * packet_get_class_str(t_packet const * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return "unknown"; } switch (packet->pclass) { case packet_class_init: return "init"; case packet_class_bnet: return "bnet"; case packet_class_file: return "file"; case packet_class_udp: return "udp"; case packet_class_raw: return "raw"; case packet_class_d2game: return "d2game"; case packet_class_d2gs: return "d2gs"; case packet_class_d2cs_bnetd: return "d2cs_bnetd"; case packet_class_d2cs: return "d2cs"; case packet_class_w3route: return "w3route"; case packet_class_wolgameres: return "wolgameres"; case packet_class_none: return "none"; default: eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return "unknown"; } } extern int packet_set_class(t_packet * packet, t_packet_class pclass) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (packet->pclass != packet_class_raw) { eventlog(eventlog_level_error, __FUNCTION__, "got non-raw packet"); return -1; } if (pclass != packet_class_init && pclass != packet_class_bnet && pclass != packet_class_file && pclass != packet_class_udp && pclass != packet_class_raw && pclass != packet_class_d2game && pclass != packet_class_d2cs && pclass != packet_class_d2gs && pclass != packet_class_d2cs_bnetd && pclass != packet_class_w3route && pclass != packet_class_wolgameres) { eventlog(eventlog_level_error, __FUNCTION__, "invalid packet class {}", (int)pclass); return -1; } packet->pclass = pclass; return 0; } extern unsigned int packet_get_type(t_packet const * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return 0; } switch (packet->pclass) { case packet_class_init: return CLIENT_INITCONN; /* all init packets are of this type */ case packet_class_bnet: if (packet_get_size(packet) < sizeof(t_bnet_header)) { eventlog(eventlog_level_error, __FUNCTION__, "bnet packet is shorter than header (len={})", packet_get_size(packet)); return 0; } return (unsigned int)bn_short_get(packet->u.bnet.h.type); case packet_class_file: if (packet_get_size(packet) < sizeof(t_file_header)) { eventlog(eventlog_level_error, __FUNCTION__, "file packet is shorter than header (len={})", packet_get_size(packet)); return 0; } return (unsigned int)bn_short_get(packet->u.file.h.type); case packet_class_udp: if (packet_get_size(packet) < sizeof(t_udp_header)) { eventlog(eventlog_level_error, __FUNCTION__, "udp packet is shorter than header (len={})", packet_get_size(packet)); return 0; } return bn_int_get(packet->u.udp.h.type); case packet_class_raw: return 0; /* raw packets don't have a type, but don't warn because the packet dump tries anyway */ case packet_class_d2game: if (packet_get_size(packet) < sizeof(t_d2game_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2game packet is shorter than header (len={})", packet_get_size(packet)); return 0; } return bn_byte_get(packet->u.d2game.h.type); case packet_class_d2gs: if (packet_get_size(packet) < sizeof(t_d2cs_d2gs_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2gs packet is shorter than header (len={})", packet_get_size(packet)); return 0; } return bn_short_get(packet->u.d2cs_d2gs.h.type); case packet_class_d2cs_bnetd: if (packet_get_size(packet) < sizeof(t_d2cs_bnetd_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2cs_bnetd packet shorter than header (len={})", packet_get_size(packet)); return 0; } return bn_short_get(packet->u.d2cs_d2gs.h.type); case packet_class_d2cs: if (packet_get_size(packet) < sizeof(t_d2cs_client_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2cs packet is shorter than header (len={})", packet_get_size(packet)); return 0; } return bn_byte_get(packet->u.d2cs_client.h.type); case packet_class_w3route: if (packet_get_size(packet) < sizeof(t_w3route_header)) { eventlog(eventlog_level_error, __FUNCTION__, "w3route packet is shorter than header (len={})", packet_get_size(packet)); return 0; } return bn_short_get(packet->u.w3route.h.type); case packet_class_wolgameres: return 0; /* wolgameres packets don't have a type */ default: eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return 0; } } extern char const * packet_get_type_str(t_packet const * packet, t_packet_dir dir) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return "unknown"; } switch (dir) { case packet_dir_from_client: switch (packet->pclass) { case packet_class_init: return "CLIENT_INITCONN"; case packet_class_bnet: if (packet_get_size(packet) < sizeof(t_bnet_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_short_get(packet->u.bnet.h.type)) { case CLIENT_COMPINFO1: return "CLIENT_COMPINFO1"; case CLIENT_COMPINFO2: return "CLIENT_COMPINFO2"; case CLIENT_COUNTRYINFO1: return "CLIENT_COUNTRYINFO1"; case CLIENT_AUTH_INFO: return "CLIENT_AUTH_INFO"; case CLIENT_CREATEACCTREQ1: return "CLIENT_CREATEACCTREQ1"; case CLIENT_UNKNOWN_2B: return "CLIENT_UNKNOWN_2B"; case CLIENT_PROGIDENT: return "CLIENT_PROGIDENT"; case CLIENT_AUTHREQ1: return "CLIENT_AUTHREQ1"; case CLIENT_AUTHREQ_109: return "CLIENT_AUTHREQ_109"; case CLIENT_REGSNOOPREPLY: return "CLIENT_REGSNOOPREPLY"; case CLIENT_ICONREQ: return "CLIENT_ICONREQ"; case CLIENT_LADDERSEARCHREQ: return "CLIENT_LADDERSEARCHREQ"; case CLIENT_CDKEY: return "CLIENT_CDKEY"; case CLIENT_CDKEY2: return "CLIENT_CDKEY2"; case CLIENT_CDKEY3: return "CLIENT_CDKEY3"; case CLIENT_REALMLISTREQ: return "CLIENT_REALMLISTREQ"; case CLIENT_REALMLISTREQ_110: return "CLIENT_REALMLISTREQ_110"; case CLIENT_PROFILEREQ: return "CLIENT_PROFILEREQ"; case CLIENT_UNKNOWN_37: return "CLIENT_UNKNOWN_37"; case CLIENT_UNKNOWN_39: return "CLIENT_UNKNOWN_39"; case CLIENT_LOGINREQ2: return "CLIENT_LOGINREQ2"; case CLIENT_MOTD_W3: return "CLIENT_MOTD_W3"; case CLIENT_LOGINREQ_W3: return "CLIENT_LOGINREQ_W3"; case CLIENT_LOGONPROOFREQ: return "CLIENT_LOGONPROOFREQ"; case CLIENT_CREATEACCOUNT_W3: return "CLIENT_CREATEACCOUNT_W3"; case CLIENT_PASSCHANGEREQ: return "CLIENT_PASSCHANGEREQ"; case CLIENT_PASSCHANGEPROOFREQ: return "CLIENT_PASSCHANGEPROOFREQ"; case CLIENT_CHANGEGAMEPORT: return "CLIENT_CHANGEGAMEPORT"; case CLIENT_CREATEACCTREQ2: return "CLIENT_CREATEACCTREQ2"; case CLIENT_UDPOK: return "CLIENT_UDPOK"; case CLIENT_FILEINFOREQ: return "CLIENT_FILEINFOREQ"; case CLIENT_STATSREQ: return "CLIENT_STATSREQ"; case CLIENT_LOGINREQ1: return "CLIENT_LOGINREQ1"; case CLIENT_CHANGEPASSREQ: return "CLIENT_CHANGEPASSREQ"; case CLIENT_PLAYERINFOREQ: return "CLIENT_PLAYERINFOREQ"; case CLIENT_PROGIDENT2: return "CLIENT_PROGIDENT2"; case CLIENT_JOINCHANNEL: return "CLIENT_JOINCHANNEL"; case CLIENT_MESSAGE: return "CLIENT_MESSAGE"; case CLIENT_GAMELISTREQ: return "CLIENT_GAMELISTREQ"; case CLIENT_STARTGAME1: return "CLIENT_STARTGAME1"; case CLIENT_UNKNOWN_1B: return "CLIENT_UNKNOWN_1B"; case CLIENT_STARTGAME3: return "CLIENT_STARTGAME3"; case CLIENT_STARTGAME4: return "CLIENT_STARTGAME4"; case CLIENT_CLOSEGAME: return "CLIENT_CLOSEGAME"; case CLIENT_CLOSEGAME2: return "CLIENT_CLOSEGAME2"; case CLIENT_LEAVECHANNEL: return "CLIENT_LEAVECHANNEL"; case CLIENT_MAPAUTHREQ1: return "CLIENT_MAPAUTHREQ1"; case CLIENT_MAPAUTHREQ2: return "CLIENT_MAPAUTHREQ2"; case CLIENT_ADREQ: return "CLIENT_ADREQ"; case CLIENT_ADACK: return "CLIENT_ADACK"; case CLIENT_ADCLICK: return "CLIENT_ADCLICK"; case CLIENT_ADCLICK2: return "CLIENT_ADCLICK2"; case CLIENT_READMEMORY: return "CLIENT_READMEMORY"; case CLIENT_EXTRAWORK: return "CLIENT_EXTRAWORK"; case CLIENT_UNKNOWN_24: return "CLIENT_UNKNOWN_24"; case CLIENT_LADDERREQ: return "CLIENT_LADDERREQ"; case CLIENT_ECHOREPLY: return "CLIENT_ECHOREPLY"; case CLIENT_PINGREQ: return "CLIENT_PINGREQ"; case CLIENT_GAME_REPORT: return "CLIENT_GAME_REPORT"; case CLIENT_JOIN_GAME: return "CLIENT_JOIN_GAME"; case CLIENT_STATSUPDATE: return "CLIENT_STATSUPDATE"; case CLIENT_REALMJOINREQ_109: return "CLIENT_REALMJOINREQ_109"; case CLIENT_CHANGECLIENT: return "CLIENT_CHANGECLIENT"; case CLIENT_SETEMAILREPLY: return "CLIENT_SETEMAILREPLY"; case CLIENT_GETPASSWORDREQ: return "CLIENT_GETPASSWORDREQ"; case CLIENT_CHANGEEMAILREQ: return "CLIENT_CHANGEEMAILREQ"; case CLIENT_CRASHDUMP: return "CLIENT_CRASHDUMP"; case CLIENT_FINDANONGAME: return "CLIENT_FINDANONGAME"; case CLIENT_ARRANGEDTEAM_FRIENDSCREEN: return "CLIENT_ARRANGEDTEAM_FRIENDSCREEN"; case CLIENT_ARRANGEDTEAM_INVITE_FRIEND: return "CLIENT_ARRANGEDTEAM_INVITE_FRIEND"; case CLIENT_ARRANGEDTEAM_ACCEPT_DECLINE_INVITE: return "CLIENT_ARRANGEDTEAM_ACCEPT_DECLINE_INVITE"; case CLIENT_FRIENDSLISTREQ: return "CLIENT_FRIENDSLISTREQ"; case CLIENT_FRIENDINFOREQ: return "CLIENT_FRIENDINFOREQ"; case CLIENT_CLANINFOREQ: return "CLIENT_CLANINFOREQ"; case CLIENT_CLAN_CREATEREQ: return "CLIENT_CLAN_CREATEREQ"; case CLIENT_CLAN_CREATEINVITEREQ: return "CLIENT_CLAN_CREATEINVITEREQ"; case CLIENT_CLAN_CREATEINVITEREPLY: return "CLIENT_CLAN_CREATEINVITEREPLY"; case CLIENT_CLAN_DISBANDREQ: return "CLIENT_CLAN_DISBANDREQ"; case CLIENT_CLAN_MEMBERNEWCHIEFREQ: return "CLIENT_CLAN_MEMBERNEWCHIEFREQ"; case CLIENT_CLAN_INVITEREQ: return "CLIENT_CLAN_INVITEREQ"; case CLIENT_CLANMEMBER_REMOVE_REQ: return "CLIENT_CLANMEMBER_REMOVE_REQ"; case CLIENT_CLAN_INVITEREPLY: return "CLIENT_CLAN_INVITEREPLY"; case CLIENT_CLANMEMBER_RANKUPDATE_REQ: return "CLIENT_CLANMEMBER_RANKUPDATE_REQ"; case CLIENT_CLAN_MOTDCHG: return "CLIENT_CLAN_MOTDCHG"; case CLIENT_CLAN_MOTDREQ: return "CLIENT_CLAN_MOTDREQ"; case CLIENT_CLANMEMBERLIST_REQ: return "CLIENT_CLANMEMBERLIST_REQ"; } return "unknown"; case packet_class_file: if (packet_get_size(packet) < sizeof(t_file_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_short_get(packet->u.file.h.type)) { case CLIENT_FILE_REQ: return "CLIENT_FILE_REQ"; } return "unknown"; case packet_class_udp: if (packet_get_size(packet) < sizeof(t_udp_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_int_get(packet->u.udp.h.type)) { case SERVER_UDPTEST: /* we get these if we send stuff to ourself */ return "SERVER_UDPTEST"; case CLIENT_UDPPING: return "CLIENT_UDPPING"; case CLIENT_SESSIONADDR1: return "CLIENT_SESSIONADDR1"; case CLIENT_SESSIONADDR2: return "CLIENT_SESSIONADDR2"; } return "unknown"; case packet_class_raw: return "CLIENT_RAW"; case packet_class_d2game: if (packet_get_size(packet) < sizeof(t_d2game_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_byte_get(packet->u.d2game.h.type)) { default: return "CLIENT_D2GAME"; } return "unknown"; case packet_class_d2cs: return "D2CS"; case packet_class_d2gs: return "D2GS"; case packet_class_d2cs_bnetd: return "D2CS_BNETD"; case packet_class_w3route: if (packet_get_size(packet) < sizeof(t_w3route_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_short_get(packet->u.bnet.h.type)) { case CLIENT_W3ROUTE_REQ: return "CLIENT_W3ROUTE_REQ"; case CLIENT_W3ROUTE_LOADINGDONE: return "CLIENT_W3ROUTE_LOADINGDONE"; case CLIENT_W3ROUTE_ABORT: return "CLIENT_W3ROUTE_ABORT"; case CLIENT_W3ROUTE_CONNECTED: return "CLIENT_W3ROUTE_CONNECTED"; case CLIENT_W3ROUTE_ECHOREPLY: return "CLIENT_W3ROUTE_ECHOREPLY"; case CLIENT_W3ROUTE_GAMERESULT: return "CLIENT_W3ROUTE_GAMERESULT"; case CLIENT_W3ROUTE_GAMERESULT_W3XP: return "CLIENT_W3ROUTE_GAMERESULT_W3XP"; } return "unknown"; case packet_class_none: return "unknown"; } eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return "unknown"; case packet_dir_from_server: switch (packet->pclass) { case packet_class_init: return "unknown"; case packet_class_bnet: if (packet_get_size(packet) < sizeof(t_bnet_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_short_get(packet->u.bnet.h.type)) { case SERVER_COMPREPLY: return "SERVER_COMPREPLY"; case SERVER_SESSIONKEY1: return "SERVER_SESSIONKEY1"; case SERVER_SESSIONKEY2: return "SERVER_SESSIONKEY2"; case SERVER_CREATEACCTREPLY1: return "SERVER_CREATEACCTREPLY1"; case SERVER_AUTHREQ1: return "SERVER_AUTHREQ1"; case SERVER_AUTHREQ_109: return "SERVER_AUTHREQ_109"; case SERVER_AUTHREPLY1: return "SERVER_AUTHREPLY1"; case SERVER_AUTHREPLY_109: return "SERVER_AUTHREPLY_109"; case SERVER_REGSNOOPREQ: return "SERVER_REGSNOOPREQ"; case SERVER_ICONREPLY: return "SERVER_ICONREPLY"; case SERVER_LADDERSEARCHREPLY: return "SERVER_LADDERSEARCHREPLY"; case SERVER_CDKEYREPLY: return "SERVER_CDKEYREPLY"; case SERVER_CDKEYREPLY2: return "SERVER_CDKEYREPLY2"; case SERVER_CDKEYREPLY3: return "SERVER_CDKEYREPLY3"; case SERVER_REALMLISTREPLY: return "SERVER_REALMLISTREPLY"; case SERVER_REALMLISTREPLY_110: return "SERVER_REALMLISTREPLY_110"; case SERVER_PROFILEREPLY: return "SERVER_PROFILEREPLY"; case SERVER_UNKNOWN_37: return "SERVER_UNKNOWN_37"; case SERVER_MOTD_W3: return "SERVER_MOTD_W3"; case SERVER_LOGINREPLY_W3: return "SERVER_LOGINREPLY_W3"; case SERVER_LOGONPROOFREPLY: return "SERVER_LOGONPROOFREPLY"; case SERVER_CREATEACCOUNT_W3: return "SERVER_CREATEACCOUNT_W3"; case SERVER_PASSCHANGEREPLY: return "SERVER_PASSCHANGEREPLY"; case SERVER_PASSCHANGEPROOFREPLY: return "SERVER_PASSCHANGEPROOFREPLY"; case SERVER_LOGINREPLY2: return "SERVER_LOGINREPLY2"; case SERVER_CREATEACCTREPLY2: return "SERVER_CREATEACCTREPLY2"; case SERVER_FILEINFOREPLY: return "SERVER_FILEINFOREPLY"; case SERVER_STATSREPLY: return "SERVER_STATSREPLY"; case SERVER_LOGINREPLY1: return "SERVER_LOGINREPLY1"; case SERVER_CHANGEPASSACK: return "SERVER_CHANGEPASSACK"; case SERVER_PLAYERINFOREPLY: return "SERVER_PLAYERINFOREPLY"; case SERVER_CHANNELLIST: return "SERVER_CHANNELLIST"; case SERVER_SERVERLIST: return "SERVER_SERVERLIST"; case SERVER_MESSAGE: return "SERVER_MESSAGE"; case SERVER_GAMELISTREPLY: return "SERVER_GAMELISTREPLY"; case SERVER_STARTGAME1_ACK: return "SERVER_STARTGAME1_ACK"; case SERVER_STARTGAME3_ACK: return "SERVER_STARTGAME3_ACK"; case SERVER_STARTGAME4_ACK: return "SERVER_STARTGAME4_ACK"; case SERVER_MAPAUTHREPLY1: return "SERVER_MAPAUTHREPLY1"; case SERVER_MAPAUTHREPLY2: return "SERVER_MAPAUTHREPLY2"; case SERVER_ADREPLY: return "SERVER_ADREPLY"; case SERVER_ADCLICKREPLY2: return "SERVER_ADCLICKREPLY2"; case SERVER_READMEMORY: return "SERVER_READMEMORY"; case SERVER_LADDERREPLY: return "SERVER_LADDERREPLY"; case SERVER_ECHOREQ: return "SERVER_ECHOREQ"; case SERVER_PINGREPLY: return "SERVER_PINGREPLY"; case SERVER_REALMJOINREPLY_109: return "SERVER_REALMJOINREPLY_109"; case SERVER_SETEMAILREQ: return "SERVER_SETEMAILREQ"; case SERVER_FINDANONGAME: return "SERVER_FINDANONGAME"; case SERVER_ARRANGEDTEAM_FRIENDSCREEN: return "SERVER_ARRANGEDTEAM_FRIENDSCREEN"; case SERVER_ARRANGEDTEAM_INVITE_FRIEND_ACK: return "SERVER_ARRANGEDTEAM_INVITE_FRIEND_ACK"; case SERVER_ARRANGEDTEAM_SEND_INVITE: return "SERVER_ARRANGEDTEAM_SEND_INVITE"; case SERVER_ARRANGEDTEAM_MEMBER_DECLINE: return "SERVER_ARRANGEDTEAM_MEMBER_DECLINE"; case SERVER_FRIENDSLISTREPLY: return "SERVER_FRIENDSLISTREPLY"; case SERVER_FRIENDINFOREPLY: return "SERVER_FRIENDINFOREPLY"; case SERVER_FRIENDADD_ACK: return "SERVER_FRIENDADD_ACK"; case SERVER_FRIENDDEL_ACK: return "SERVER_FRIENDDEL_ACK"; case SERVER_FRIENDMOVE_ACK: return "SERVER_FRIENDMOVE_ACK"; case SERVER_CLANINFOREPLY: return "SERVER_CLANINFO_REPLY"; case SERVER_CLAN_CREATEREPLY: return "SERVER_CLAN_CREATEREPLY"; case SERVER_CLAN_CREATEINVITEREPLY: return "SERVER_CLAN_CREATEINVITEREPLY"; case SERVER_CLAN_CREATEINVITEREQ: return "SERVER_CLAN_CREATEINVITEREQ"; case SERVER_CLAN_DISBANDREPLY: return "SERVER_CLAN_DISBANDREPLY"; case SERVER_CLAN_MEMBERNEWCHIEFREPLY: return "SERVER_CLAN_MEMBERNEWCHIEFREPLY"; case SERVER_CLAN_CLANACK: return "SERVER_CLAN_CLANACK"; case SERVER_CLANQUITNOTIFY: return "SERVER_CLANQUITNOTIFY"; case SERVER_CLAN_INVITEREPLY: return "SERVER_CLAN_INVITEREPLY"; case SERVER_CLANMEMBER_REMOVE_REPLY: return "SERVER_CLANMEMBER_REMOVE_REPLY"; case SERVER_CLAN_INVITEREQ: return "SERVER_CLAN_INVITEREQ"; case SERVER_CLANMEMBER_RANKUPDATE_REPLY: return "SERVER_CLANMEMBER_RANKUPDATE_REPLY"; case SERVER_CLAN_MOTDREPLY: return "SERVER_CLAN_MOTDREPLY"; case SERVER_CLANMEMBERLIST_REPLY: return "SERVER_CLANMEMBERLIST_REPLY"; case SERVER_CLANMEMBER_REMOVED_NOTIFY: return "SERVER_CLANMEMBER_REMOVED_NOTIFY"; case SERVER_CLANMEMBERUPDATE: return "SERVER_CLANMEMBERUPDATE"; case SERVER_MESSAGEBOX: return "SERVER_MESSAGEBOX"; } return "unknown"; case packet_class_file: if (packet_get_size(packet) < sizeof(t_file_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_short_get(packet->u.file.h.type)) { case SERVER_FILE_REPLY: return "SERVER_FILE_REPLY"; } return "unknown"; case packet_class_udp: if (packet_get_size(packet) < sizeof(t_udp_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_int_get(packet->u.udp.h.type)) { case SERVER_UDPTEST: return "SERVER_UDPTEST"; } return "unknown"; case packet_class_raw: return "SERVER_RAW"; case packet_class_d2game: if (packet_get_size(packet) < sizeof(t_d2game_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_byte_get(packet->u.d2game.h.type)) { default: return "SERVER_D2GAME"; } return "unknown"; case packet_class_d2cs: return "D2CS"; case packet_class_d2gs: return "D2GS"; case packet_class_d2cs_bnetd: return "D2CS_BNETD"; case packet_class_w3route: if (packet_get_size(packet) < sizeof(t_w3route_header)) { eventlog(eventlog_level_error, __FUNCTION__, "packet is shorter than header (len={})", packet_get_size(packet)); return "unknown"; } switch (bn_short_get(packet->u.bnet.h.type)) { case SERVER_W3ROUTE_READY: return "SERVER_W3ROUTE_READY"; case SERVER_W3ROUTE_LOADINGACK: return "SERVER_W3ROUTE_LOADINGACK"; case SERVER_W3ROUTE_ECHOREQ: return "SERVER_W3ROUTE_ECHOREQ"; case SERVER_W3ROUTE_ACK: return "SERVER_W3ROUTE_ACK"; case SERVER_W3ROUTE_PLAYERINFO: return "SERVER_W3ROUTE_PLAYERINFO"; case SERVER_W3ROUTE_LEVELINFO: return "SERVER_W3ROUTE_LEVELINFO"; case SERVER_W3ROUTE_STARTGAME1: return "SERVER_W3ROUTE_STARTGAME1"; case SERVER_W3ROUTE_STARTGAME2: return "SERVER_W3ROUTE_STARTGAME2"; } return "unknown"; case packet_class_wolgameres: return "CLIENT_WOLGAMERES"; case packet_class_none: return "unknown"; } eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return "unknown"; } eventlog(eventlog_level_error, __FUNCTION__, "got unknown direction {}", (int)dir); return "unknown"; } extern int packet_set_type(t_packet * packet, unsigned int type) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } switch (packet->pclass) { case packet_class_init: if (type != CLIENT_INITCONN) { eventlog(eventlog_level_error, __FUNCTION__, "init packet type 0x{:08} is not valid", type); return -1; } return 0; case packet_class_bnet: if (packet_get_size(packet)MAX_NORMAL_TYPE) { eventlog(eventlog_level_error, __FUNCTION__, "bnet packet type 0x{:08} is too large", type); return -1; } bn_short_set(&packet->u.bnet.h.type, (unsigned short)type); return 0; case packet_class_file: if (packet_get_size(packet)MAX_FILE_TYPE) { eventlog(eventlog_level_error, __FUNCTION__, "file packet type 0x{:08} is too large", type); return -1; } bn_short_set(&packet->u.file.h.type, (unsigned short)type); return 0; case packet_class_udp: if (packet_get_size(packet) < sizeof(t_udp_header)) { eventlog(eventlog_level_error, __FUNCTION__, "udp packet is shorter than header (len={})", packet_get_size(packet)); return -1; } bn_int_set(&packet->u.udp.h.type, type); return 0; case packet_class_d2game: if (packet_get_size(packet) < sizeof(t_d2game_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2game packet is shorter than header (len={})", packet_get_size(packet)); return -1; } bn_byte_set(&packet->u.d2game.h.type, type); return 0; case packet_class_d2gs: if (packet_get_size(packet) < sizeof(t_d2cs_d2gs_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2gs packet is shorter than header (len={})", packet_get_size(packet)); return -1; } bn_short_set(&packet->u.d2cs_d2gs.h.type, type); return 0; case packet_class_d2cs_bnetd: if (packet_get_size(packet) < sizeof(t_d2cs_bnetd_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2cs_bnetd packet is shorter than header (len={})", packet_get_size(packet)); return -1; } bn_short_set(&packet->u.d2cs_bnetd.h.type, type); return 0; case packet_class_d2cs: if (packet_get_size(packet) < sizeof(t_d2cs_client_header)) { eventlog(eventlog_level_error, __FUNCTION__, "d2cs packet is shorter than header (len={})", packet_get_size(packet)); return -1; } bn_byte_set(&packet->u.d2cs_client.h.type, type); return 0; case packet_class_w3route: if (packet_get_size(packet) < sizeof(t_w3route_header)) { eventlog(eventlog_level_error, __FUNCTION__, "w3route packet is shorter than header (len={})", packet_get_size(packet)); return -1; } bn_short_set(&packet->u.w3route.h.type, (unsigned short)type); return 0; case packet_class_wolgameres: eventlog(eventlog_level_error, __FUNCTION__, "can not set packet type for wol gameres packet"); return 0; case packet_class_raw: eventlog(eventlog_level_error, __FUNCTION__, "can not set packet type for raw packet"); return 0; default: eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return -1; } } /* size of the _complete_ packet, not the amount currently received or sent */ extern unsigned int packet_get_size(t_packet const * packet) { unsigned int size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return 0; } switch (packet->pclass) { case packet_class_init: size = sizeof(t_client_initconn); break; case packet_class_bnet: size = (unsigned int)bn_short_get(packet->u.bnet.h.size); break; case packet_class_file: size = (unsigned int)bn_short_get(packet->u.file.h.size); break; case packet_class_udp: size = packet->len; break; case packet_class_raw: size = packet->len; break; case packet_class_d2game: size = packet->len; /* FIXME: does header not contain the size? */ break; case packet_class_d2gs: size = (unsigned int)bn_short_get(packet->u.d2cs_d2gs.h.size); break; case packet_class_d2cs_bnetd: size = (unsigned int)bn_short_get(packet->u.d2cs_bnetd.h.size); break; case packet_class_d2cs: size = (unsigned int)bn_short_get(packet->u.d2cs_client.h.size); break; case packet_class_w3route: size = (unsigned int)bn_short_get(packet->u.w3route.h.size); break; case packet_class_wolgameres: /* PELISH: We check and return size explicitly in this case, because we need to check MAX_WOL_GAMERES_PACKET_SIZE */ { size = (unsigned int)bn_short_nget(packet->u.wolgameres.h.size); if (size == 0) /* RNGD sends size on rngd_size */ size = (unsigned int)bn_short_nget(packet->u.wolgameres.h.rngd_size); // if (size>MAX_WOL_GAMERES_PACKET_SIZE) { /* PELISH: Fix for bug but also disable WOL gameres */ if (size > MAX_PACKET_SIZE) { // eventlog(eventlog_level_error,__FUNCTION__,"packet has bad size {}",size); return 0; } } return size; default: eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return 0; } if (size > MAX_PACKET_SIZE) { eventlog(eventlog_level_error, __FUNCTION__, "packet has bad size {}", size); return 0; } return size; } extern int packet_set_size(t_packet * packet, unsigned int size) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (size > MAX_PACKET_SIZE) { eventlog(eventlog_level_error, __FUNCTION__, "got bad size {}", size); return -1; } switch (packet->pclass) { case packet_class_init: if (size != 0 && size != sizeof(t_client_initconn)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid size {} for init packet", size); return -1; } packet->len = size; return 0; case packet_class_bnet: if (size != 0 && size < sizeof(t_bnet_header)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid size {} for bnet packet", size); return -1; } bn_short_set(&packet->u.bnet.h.size, size); return 0; case packet_class_file: if (size != 0 && size < sizeof(t_file_header)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid size {} for file packet", size); return -1; } bn_short_set(&packet->u.file.h.size, size); return 0; case packet_class_udp: if (size != 0 && size < sizeof(t_udp_header)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid size {} for udp packet", size); return -1; } packet->len = size; return 0; case packet_class_raw: packet->len = size; return 0; case packet_class_d2game: packet->len = size; /* FIXME: does header not contain the size? */ return 0; case packet_class_d2cs: bn_short_set(&packet->u.d2cs_client.h.size, size); return 0; case packet_class_d2gs: bn_short_set(&packet->u.d2cs_d2gs.h.size, size); return 0; case packet_class_d2cs_bnetd: bn_short_set(&packet->u.d2cs_bnetd.h.size, size); return 0; case packet_class_w3route: if (size != 0 && size < sizeof(t_w3route_header)) { eventlog(eventlog_level_error, __FUNCTION__, "invalid size {} for w3route packet", size); return -1; } bn_short_set(&packet->u.w3route.h.size, size); return 0; case packet_class_wolgameres: /* PELISH: useless - there is no server packet for wolgameres */ packet->len = size; return 0; default: eventlog(eventlog_level_error, __FUNCTION__, "packet has invalid class {}", (int)packet->pclass); return -1; } } extern unsigned int packet_get_header_size(t_packet const * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return MAX_PACKET_SIZE; } switch (packet_get_class(packet)) { case packet_class_init: return 0; case packet_class_bnet: return sizeof(t_bnet_header); case packet_class_file: return sizeof(t_file_header); case packet_class_udp: return sizeof(t_udp_header); case packet_class_raw: return 0; case packet_class_d2game: return 0; /* FIXME: is there no game packet header? */ case packet_class_d2cs: return sizeof(t_d2cs_client_header); case packet_class_d2gs: return sizeof(t_d2cs_d2gs_header); case packet_class_d2cs_bnetd: return sizeof(t_d2cs_bnetd_header); case packet_class_w3route: return sizeof(t_w3route_header); case packet_class_wolgameres: return sizeof(t_wolgameres_header); default: eventlog(eventlog_level_error, __FUNCTION__, "packet has bad class {}", (int)packet_get_class(packet)); return MAX_PACKET_SIZE; } } extern unsigned int packet_get_flags(t_packet const * packet) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return 0; } return packet->flags; } extern int packet_set_flags(t_packet * packet, unsigned int flags) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } packet->flags = flags; return 0; } extern int packet_append_string(t_packet * packet, char const * str) { unsigned int len; unsigned short addlen; unsigned short size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL string"); return -1; } len = std::strlen(str) + 1; size = packet_get_size(packet); if (size >= MAX_PACKET_SIZE) return -1; if (MAX_PACKET_SIZE - (unsigned int)size > len) addlen = len; else addlen = MAX_PACKET_SIZE - size; if (addlen < 1) return -1; std::memcpy(packet->u.data + size, str, addlen - 1); packet->u.data[size + addlen - 1] = '\0'; packet_set_size(packet, size + addlen); return (int)addlen; } extern int packet_append_ntstring(t_packet * packet, char const * str) { unsigned int len; unsigned short addlen; unsigned short size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (!str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL string"); return -1; } len = std::strlen(str); size = packet_get_size(packet); if (size >= MAX_PACKET_SIZE) return -1; if (MAX_PACKET_SIZE - (unsigned int)size > len) addlen = len; else addlen = MAX_PACKET_SIZE - size; if (addlen < 1) return -1; std::memcpy(packet->u.data + size, str, addlen); packet_set_size(packet, size + addlen); return (int)addlen; } extern int packet_append_lstr(t_packet * packet, t_lstr *lstr) { unsigned short addlen; unsigned short size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (!lstr || !lstr_get_str(lstr)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL string"); return -1; } size = packet_get_size(packet); if (size >= MAX_PACKET_SIZE) return -1; if (MAX_PACKET_SIZE - (unsigned int)size > lstr_get_len(lstr)) addlen = lstr_get_len(lstr); else addlen = MAX_PACKET_SIZE - size; if (addlen < 1) return -1; std::memcpy(packet->u.data + size, lstr_get_str(lstr), addlen - 1); packet->u.data[size + addlen - 1] = '\0'; packet_set_size(packet, size + addlen); return (int)addlen; } extern int packet_append_data(t_packet * packet, void const * data, unsigned int len) { unsigned short addlen; unsigned short size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } if (!data) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL data"); return -1; } size = packet_get_size(packet); if (size >= MAX_PACKET_SIZE) return -1; if (MAX_PACKET_SIZE - (unsigned int)size > len) addlen = len; else addlen = MAX_PACKET_SIZE - size; if (addlen < 1) return -1; std::memcpy(packet->u.data + size, data, addlen); packet_set_size(packet, size + addlen); return (int)addlen; } extern void const * packet_get_raw_data_const(t_packet const * packet, unsigned int offset) { unsigned int size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return NULL; } size = (unsigned int)packet_get_size(packet); if (offset >= size || (((offset >= MAX_PACKET_SIZE) && (packet->pclass != packet_class_wolgameres)) || (offset >= MAX_WOL_GAMERES_PACKET_SIZE))) { eventlog(eventlog_level_error, __FUNCTION__, "got bad offset {} for packet size {}", offset, size); return NULL; } return packet->u.data + offset; } extern void * packet_get_raw_data(t_packet * packet, unsigned int offset) { unsigned int size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return NULL; } size = (unsigned int)packet_get_size(packet); if (offset >= size || (((offset >= MAX_PACKET_SIZE) && (packet->pclass != packet_class_wolgameres)) || (offset >= MAX_WOL_GAMERES_PACKET_SIZE))) { eventlog(eventlog_level_error, __FUNCTION__, "got bad offset {} for packet size {}", offset, size); return NULL; } return packet->u.data + offset; } extern void * packet_get_raw_data_build(t_packet * packet, unsigned int offset) { if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return NULL; } if (((offset >= MAX_PACKET_SIZE) && (packet->pclass != packet_class_wolgameres)) || (offset >= MAX_WOL_GAMERES_PACKET_SIZE)) { eventlog(eventlog_level_error, __FUNCTION__, "got bad offset {} for packet", offset); return NULL; } return packet->u.data + offset; } /* maxlen includes room for NUL char */ extern char const * packet_get_str_const(t_packet const * packet, unsigned int offset, unsigned int maxlen) { unsigned int size; unsigned int pos; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return NULL; } size = (unsigned int)packet_get_size(packet); if (offset >= size) { eventlog(eventlog_level_error, __FUNCTION__, "got bad offset {} for packet size {}", offset, size); return NULL; } for (pos = offset; packet->u.data[pos] != '\0'; pos++) if (pos >= size || pos - offset > maxlen) return NULL; if (pos >= size || pos - offset > maxlen) /* NUL must be inside too */ return NULL; return packet->u.data + offset; } extern void const * packet_get_data_const(t_packet const * packet, unsigned int offset, unsigned int len) { unsigned int size; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return NULL; } if (len<1) { eventlog(eventlog_level_error, __FUNCTION__, "got zero length"); return NULL; } size = (unsigned int)packet_get_size(packet); if (offset + len>size) { eventlog(eventlog_level_error, __FUNCTION__, "got bad offset {} and length {} for packet size {}", offset, len, size); return NULL; } return packet->u.data + offset; } extern t_packet * packet_duplicate(t_packet const * src) { t_packet * p; if (!(p = packet_create(packet_get_class(src)))) { eventlog(eventlog_level_error, __FUNCTION__, "could not create packet"); return NULL; } packet_append_data(p, src->u.data, packet_get_size(src)); packet_set_flags(p, packet_get_flags(src)); return p; } } pvpgn-1.99.7.2.1+dfsg/src/common/packet.h000066400000000000000000000447741462640461400177170ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysng@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_PACKET_TYPES #define INCLUDED_PACKET_TYPES #ifdef JUST_NEED_TYPES # include "field_sizes.h" # include "init_protocol.h" # include "bnet_protocol.h" # include "anongame_protocol.h" # include "file_protocol.h" # include "bot_protocol.h" # include "udp_protocol.h" # include "d2game_protocol.h" # include "d2cs_protocol.h" # include "d2cs_d2gs_protocol.h" # include "d2cs_bnetd_protocol.h" # include "wol_gameres_protocol.h" #else # define JUST_NEED_TYPES # include "field_sizes.h" # include "init_protocol.h" # include "bnet_protocol.h" # include "anongame_protocol.h" # include "file_protocol.h" # include "bot_protocol.h" # include "udp_protocol.h" # include "d2game_protocol.h" # include "d2cs_protocol.h" # include "d2cs_d2gs_protocol.h" # include "d2cs_bnetd_protocol.h" # include "wol_gameres_protocol.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { typedef enum { packet_class_none, packet_class_init, packet_class_bnet, packet_class_file, packet_class_raw, packet_class_udp, packet_class_d2game, packet_class_d2gs, packet_class_d2cs, packet_class_d2cs_bnetd, packet_class_w3route, packet_class_wolgameres } t_packet_class; typedef enum { packet_dir_from_client, packet_dir_from_server } t_packet_dir; /* These aren't really packets so much as records in a TCP stream. They are variable- * length structures which make up the Battle.net protocol. It is just easier to call * them "packets". */ typedef struct { unsigned int ref; /* reference count */ t_packet_class pclass; unsigned int flags; /* user-defined flags (used to mark UDP in bnproxy) */ unsigned int len; /* raw packets have no header, so we use this */ /* next part looks just like it would on the network (no padding, byte for byte) */ union { char data[MAX_PACKET_SIZE]; t_bnet_generic bnet; t_file_generic file; t_udp_generic udp; t_d2game_generic d2game; t_w3route_generic w3route; t_wolgameres_generic wolgameres; t_client_initconn client_initconn; t_server_authreply1 server_authreply1; t_server_authreq1 server_authreq1; t_client_authreq1 client_authreq1; t_server_authreply_109 server_authreply_109; t_server_authreq_109 server_authreq_109; t_client_authreq_109 client_authreq_109; t_client_cdkey client_cdkey; t_server_cdkeyreply server_cdkeyreply; t_client_statsreq client_statsreq; t_server_statsreply server_statsreply; t_client_loginreq1 client_loginreq1; t_server_loginreply1 server_loginreply1; t_client_progident client_progident; t_client_progident2 client_progident2; t_client_joinchannel client_joinchannel; t_server_channellist server_channellist; t_server_serverlist server_serverlist; t_server_message server_message; t_client_message client_message; t_client_gamelistreq client_gamelistreq; t_server_gamelistreply server_gamelistreply; t_client_udpok client_udpok; t_client_unknown_1b client_unknown_1b; t_client_startgame1 client_startgame1; t_server_startgame1_ack server_startgame1_ack; t_client_startgame3 client_startgame3; t_server_startgame3_ack server_startgame3_ack; t_client_startgame4 client_startgame4; t_server_startgame4_ack server_startgame4_ack; t_client_leavechannel client_leavechannel; t_client_closegame client_closegame; t_client_mapauthreq1 client_mapauthreq1; t_server_mapauthreply1 server_mapauthreply1; t_client_mapauthreq2 client_mapauthreq2; t_server_mapauthreply2 server_mapauthreply2; t_client_ladderreq client_ladderreq; t_server_ladderreply server_ladderreply; t_client_laddersearchreq client_laddersearchreq; t_server_laddersearchreply server_laddersearchreply; t_client_adreq client_adreq; t_server_adreply server_adreply; t_client_adack client_adack; t_client_adclick client_adclick; t_client_adclick2 client_adclick2; t_server_adclickreply2 server_adclickreply2; t_client_readmemory client_readmemory; t_server_readmemory server_readmemory; t_client_game_report client_gamerep; t_server_sessionkey1 server_sessionkey1; t_server_sessionkey2 server_sessionkey2; t_client_createacctreq1 client_createacctreq1; t_server_createacctreply1 server_createacctreply1; t_client_changepassreq client_changepassreq; t_server_changepassack server_changepassack; t_client_iconreq client_iconreq; t_server_iconreply server_iconreply; t_client_fileinforeq client_fileinforeq; t_server_fileinforeply server_fileinforeply; t_client_statsupdate client_statsupdate; t_client_countryinfo1 client_countryinfo1; t_client_auth_info client_auth_info; t_client_unknown_2b client_unknown_2b; t_client_compinfo1 client_compinfo1; t_client_compinfo2 client_compinfo2; t_server_compreply server_compreply; t_server_echoreq server_echoreq; t_client_echoreply client_echoreply; t_client_playerinforeq client_playerinforeq; t_server_playerinforeply server_playerinforeply; t_client_pingreq client_pingreq; t_server_pingreply server_pingreply; t_client_cdkey2 client_cdkey2; t_server_cdkeyreply2 server_cdkeyreply2; t_client_cdkey3 client_cdkey3; t_server_cdkeyreply3 server_cdkeyreply3; t_server_regsnoopreq server_regsnoopreq; t_client_regsnoopreply client_regsnoopreply; t_client_realmlistreq client_realmlistreq; t_client_realmlistreq_110 client_realmlistreq_110; t_server_realmlistreply server_realmlistreply; t_server_realmlistreply_110 server_realmlistreply_110; t_client_profilereq client_profilereq; t_server_profilereply server_profilereply; t_client_realmjoinreq_109 client_realmjoinreq_109; t_server_realmjoinreply_109 server_realmjoinreply_109; t_client_unknown_37 client_unknown_37; t_server_unknown_37 server_unknown_37; t_client_unknown_39 client_unknown_39; t_client_loginreq2 client_loginreq2; t_server_loginreply2 server_loginreply2; t_client_loginreq_w3 client_loginreq_w3; t_server_loginreply_w3 server_loginreply_w3; t_client_createacctreq2 client_createacctreq2; t_server_createacctreply2 server_createacctreply2; t_client_changegameport client_changegameport; t_client_file_req client_file_req; t_server_file_reply server_file_reply; t_server_file_unknown1 server_file_unknown1; t_client_file_req2 client_file_req2; t_client_file_req3 client_file_req3; t_server_udptest server_udptest; t_client_udpping client_udpping; t_client_sessionaddr1 client_sessionaddr1; t_client_sessionaddr2 client_sessionaddr2; t_client_motd_w3 client_motd_w3; t_server_motd_w3 server_motd_w3; t_client_logonproofreq client_logonproofreq; t_server_logonproofreply server_logonproofreply; t_client_createaccount_w3 client_createaccount_w3; t_server_createaccount_w3 server_createaccount_w3; t_client_passchangereq client_passchangereq; t_server_passchangereply server_passchangereply; t_client_passchangeproofreq client_passchangeproofreq; t_server_passchangeproofreply server_passchangeproofreply; t_client_findanongame client_findanongame; t_client_findanongame_at client_findanongame_at; t_client_findanongame_at_inv client_findanongame_at_inv; t_server_findanongame_playgame_cancel server_findanongame_playgame_cancel; t_server_anongame_found server_anongame_found; t_d2cs_bnetd_generic d2cs_bnetd; t_bnetd_d2cs_authreq bnetd_d2cs_authreq; t_d2cs_bnetd_authreply d2cs_bnetd_authreply; t_bnetd_d2cs_authreply bnetd_d2cs_authreply; t_d2cs_bnetd_accountloginreq d2cs_bnetd_accountloginreq; t_bnetd_d2cs_accountloginreply bnetd_d2cs_accountloginreply; t_d2cs_bnetd_charloginreq d2cs_bnetd_charloginreq; t_bnetd_d2cs_charloginreply bnetd_d2cs_charloginreply; t_bnetd_d2cs_gameinforeq bnetd_d2cs_gameinforeq; t_d2cs_bnetd_gameinforeply d2cs_bnetd_gameinforeply; t_d2cs_d2gs_generic d2cs_d2gs; t_d2cs_d2gs_authreq d2cs_d2gs_authreq; t_d2gs_d2cs_authreply d2gs_d2cs_authreply; t_d2cs_d2gs_authreply d2cs_d2gs_authreply; t_d2cs_d2gs_setinitinfo d2cs_d2gs_setinitinfo; t_d2cs_d2gs_setgsinfo d2cs_d2gs_setgsinfo; t_d2gs_d2cs_setgsinfo d2gs_d2cs_setgsinfo; t_d2cs_d2gs_creategamereq d2cs_d2gs_creategamereq; t_d2gs_d2cs_creategamereply d2gs_d2cs_creategamereply; t_d2cs_d2gs_joingamereq d2cs_d2gs_joingamereq; t_d2gs_d2cs_joingamereply d2gs_d2cs_joingamereply; t_d2gs_d2cs_updategameinfo d2gs_d2cs_updategameinfo; t_d2gs_d2cs_closegame d2gs_d2cs_closegame; t_d2cs_d2gs_echoreq d2cs_d2gs_echoreq; t_d2gs_d2cs_echoreply d2gs_d2cs_echoreply; t_d2cs_d2gs_control d2cs_d2gs_control; t_d2cs_d2gs_setconffile d2cs_d2gs_setconffile; t_d2cs_client_generic d2cs_client; t_client_d2cs_loginreq client_d2cs_loginreq; t_d2cs_client_loginreply d2cs_client_loginreply; t_client_d2cs_createcharreq client_d2cs_createcharreq; t_d2cs_client_createcharreply d2cs_client_createcharreply; t_client_d2cs_creategamereq client_d2cs_creategamereq; t_d2cs_client_creategamereply d2cs_client_creategamereply; t_client_d2cs_joingamereq client_d2cs_joingamereq; t_d2cs_client_joingamereply d2cs_client_joingamereply; t_client_d2cs_gamelistreq client_d2cs_gamelistreq; t_d2cs_client_gamelistreply d2cs_client_gamelistreply; t_client_d2cs_gameinforeq client_d2cs_gameinforeq; t_d2cs_client_gameinforeply d2cs_client_gameinforeply; t_client_d2cs_charloginreq client_d2cs_charloginreq; t_d2cs_client_charloginreply d2cs_client_charloginreply; t_client_d2cs_deletecharreq client_d2cs_deletecharreq; t_d2cs_client_deletecharreply d2cs_client_deletecharreply; t_client_d2cs_ladderreq client_d2cs_ladderreq; t_d2cs_client_ladderreply d2cs_client_ladderreply; t_client_d2cs_motdreq client_d2cs_motdreq; t_d2cs_client_motdreply d2cs_client_motdreply; t_client_d2cs_cancelcreategame client_d2cs_cancelcreategame; t_d2cs_client_creategamewait d2cs_client_creategamewait; t_client_d2cs_charladderreq client_d2cs_charladderreq; t_client_d2cs_charlistreq client_d2cs_charlistreq; t_d2cs_client_charlistreply d2cs_client_charlistreply; t_client_d2cs_charlistreq_110 client_d2cs_charlistreq_110; t_d2cs_client_charlistreply_110 d2cs_client_charlistreply_110; t_client_d2cs_convertcharreq client_d2cs_convertcharreq; t_d2cs_client_convertcharreply d2cs_client_convertcharreply; t_client_friendslistreq client_friendslistreq; t_server_friendslistreply server_friendslistreply; t_client_friendinforeq client_friendinforeq; t_server_friendinforeply server_friendinforeply; t_server_friendadd_ack server_friendadd_ack; t_server_frienddel_ack server_frienddel_ack; t_server_friendmove_ack server_friendmove_ack; t_client_arrangedteam_friendscreen client_arrangedteam_friendscreen; t_server_arrangedteam_friendscreen server_arrangedteam_friendscreen; t_client_arrangedteam_invite_friend client_arrangedteam_invite_friend; t_server_arrangedteam_invite_friend_ack server_arrangedteam_invite_friend_ack; t_server_arrangedteam_send_invite server_arrangedteam_send_invite; t_client_arrangedteam_accept_invite client_arrangedteam_accept_invite; t_client_arrangedteam_accept_decline_invite client_arrangedteam_accept_decline_invite; t_server_arrangedteam_member_decline server_arrangedteam_member_decline; t_client_findanongame_profile client_findanongame_profile; t_server_findanongame_profile2 server_findanongame_profile2; t_client_w3route_req client_w3route_req; t_server_w3route_ack server_w3route_ack; t_server_w3route_playerinfo server_w3route_playerinfo; t_server_w3route_levelinfo server_w3route_levelinfo; t_server_w3route_startgame1 server_w3route_startgame1; t_server_w3route_startgame2 server_w3route_startgame2; t_client_w3route_loadingdone client_w3route_loadingdone; t_server_w3route_loadingack server_w3route_loadingack; t_client_w3route_connected client_w3route_connected; t_server_w3route_echoreq server_w3route_echoreq; t_client_w3route_abort client_w3route_abort; t_server_w3route_ready server_w3route_host; t_client_w3route_gameresult client_w3route_gameresult; t_client_findanongame_inforeq client_findanongame_inforeq; t_server_findanongame_inforeply server_findanongame_inforeply; t_client_clan_invitereq client_clan_invitereq; t_server_clan_invitereply server_clan_invitereply; t_server_clan_invitereq server_clan_invitereq; t_client_clan_invitereply client_clan_invitereply; t_client_clan_disbandreq client_clan_disbandreq; t_server_clan_disbandreply server_clan_disbandreply; t_client_clan_motdchg client_clan_motdchg; t_client_clan_motdreq client_clan_motdreq; t_server_clan_motdreply server_clan_motdreply; t_client_clanmemberlist_req client_clanmemberlist_req; t_server_clanmemberlist_reply server_clanmemberlist_reply; t_client_clan_createreq client_clan_createreq; t_server_clan_createreply server_clan_createreply; t_client_clan_createinvitereq client_clan_createinvitereq; t_server_clan_createinvitereply server_clan_createinvitereply; t_server_clan_createinvitereq server_clan_createinvitereq; t_client_clan_createinvitereply client_clan_createinvitereply; t_server_clan_clanack server_clan_clanack; t_server_clanmemberupdate server_clanmemberupdate; t_client_clanmember_rankupdate_req client_clanmember_rankupdate_req; t_server_clanmember_rankupdate_reply server_clanmember_rankupdate_reply; t_client_clanmember_remove_req client_clanmember_remove_req; t_server_clanmember_remove_reply server_clanmember_remove_reply; t_client_clan_membernewchiefreq client_clan_membernewchiefreq; t_server_clan_membernewchiefreply server_clan_membernewchiefreply; t_server_clanquitnotify server_clanquitnotify; t_server_clanmember_removed_notify server_clanmember_removed_notify; t_server_findanongame_iconreply server_findanongame_iconreply; t_client_changeclient client_changeclient; t_client_anongame client_anongame; t_server_anongame_search_reply server_anongame_search_reply; t_client_anongame_tournament_request client_anongame_tournament_request; t_server_anongame_tournament_reply server_anongame_tournament_reply; t_client_setemailreply client_setemailreq; t_server_setemailreq server_setemailreply; t_client_getpasswordreq client_getpasswordreq; t_client_changeemailreq client_changeemailreq; t_client_crashdump client_crashdump; t_client_claninforeq client_claninforeq; t_server_claninforeply server_claninforeply; t_client_findanongame_profile_clan client_findanongame_profile_clan; t_server_findanongame_profile_clan server_findanongame_profile_clan; t_server_messagebox server_messagebox; t_server_requiredwork server_requiredwork; t_client_extrawork client_extrawork; } u; } t_packet; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_PACKET_PROTOS #define INCLUDED_PACKET_PROTOS #include "lstr.h" namespace pvpgn { extern t_packet * packet_create(t_packet_class pclass); extern void packet_destroy(t_packet const * packet); extern t_packet * packet_add_ref(t_packet * packet); extern void packet_del_ref(t_packet * packet); extern t_packet_class packet_get_class(t_packet const * packet); extern char const * packet_get_class_str(t_packet const * packet); extern int packet_set_class(t_packet * packet, t_packet_class pclass); extern unsigned int packet_get_type(t_packet const * packet); extern char const * packet_get_type_str(t_packet const * packet, t_packet_dir dir); extern int packet_set_type(t_packet * packet, unsigned int type); extern unsigned int packet_get_size(t_packet const * packet); extern int packet_set_size(t_packet * packet, unsigned int size); extern unsigned int packet_get_header_size(t_packet const * packet); extern unsigned int packet_get_flags(t_packet const * packet); extern int packet_set_flags(t_packet * packet, unsigned int flags); extern int packet_append_string(t_packet * packet, char const * str); extern int packet_append_ntstring(t_packet * packet, char const * str); extern int packet_append_lstr(t_packet * packet, t_lstr *lstr); extern int packet_append_data(t_packet * packet, void const * data, unsigned int len); extern void const * packet_get_raw_data_const(t_packet const * packet, unsigned int offset); extern void * packet_get_raw_data(t_packet * packet, unsigned int offset); extern void * packet_get_raw_data_build(t_packet * packet, unsigned int offset); extern char const * packet_get_str_const(t_packet const * packet, unsigned int offset, unsigned int maxlen); extern void const * packet_get_data_const(t_packet const * packet, unsigned int offset, unsigned int len); extern t_packet * packet_duplicate(t_packet const * src); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/peerchat.cpp000066400000000000000000000052171462640461400205630ustar00rootroot00000000000000/* GS peerchat encryption/decryption algorithm 0.2 by Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org LICENSE ======= Copyright 2004,2005,2006 Luigi Auriemma This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA http://www.gnu.org/licenses/gpl.txt */ #include "common/setup_before.h" #include "common/peerchat.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { extern gs_peerchat_ctx * gs_peerchat_create() { gs_peerchat_ctx * temp; temp = (gs_peerchat_ctx*)xmalloc(sizeof(gs_peerchat_ctx)); return temp; } extern void gs_peerchat_destroy(gs_peerchat_ctx const * ctx) { if (!ctx) { ERROR0("got NULL ctx"); return; } xfree((void *)ctx); /* avoid warning */ } void gs_peerchat_init(gs_peerchat_ctx *ctx, unsigned char *chall, unsigned char *gamekey) { unsigned char challenge[16], *l, *l1, *p, *p1, *crypt, t, t1; ctx->gs_peerchat_1 = 0; ctx->gs_peerchat_2 = 0; crypt = ctx->gs_peerchat_crypt; p = challenge; l = challenge + 16; p1 = gamekey; l1 = gamekey + 6; do { *p++ = *chall++ ^ *p1++; // avoid a memcpy(challenge, chall, 16); if (p1 == l1) p1 = gamekey; } while (p != l); t1 = 255; p1 = crypt; l1 = crypt + 256; do { *p1++ = t1--; } while (p1 != l1); t1++; // means t1 = 0; p = crypt; p1 = challenge; do { t1 += *p1 + *p; t = crypt[t1]; crypt[t1] = *p; *p = t; p++; p1++; if (p1 == l) p1 = challenge; } while (p != l1); // DEBUG3("initialised: %u/%u/%s",ctx->gs_peerchat_1,ctx->gs_peerchat_2,ctx->gs_peerchat_crypt); } void gs_peerchat(gs_peerchat_ctx *ctx, unsigned char *data, int size) { unsigned char num1, num2, t, *crypt; num1 = ctx->gs_peerchat_1; num2 = ctx->gs_peerchat_2; crypt = ctx->gs_peerchat_crypt; while (size--) { t = crypt[++num1]; num2 += t; crypt[num1] = crypt[num2]; crypt[num2] = t; t += crypt[num1]; *data++ ^= crypt[t]; } ctx->gs_peerchat_1 = num1; ctx->gs_peerchat_2 = num2; } } pvpgn-1.99.7.2.1+dfsg/src/common/peerchat.h000066400000000000000000000026541462640461400202320ustar00rootroot00000000000000/* GS peerchat encryption/decryption algorithm 0.2 by Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org LICENSE ======= Copyright 2004,2005,2006 Luigi Auriemma This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA http://www.gnu.org/licenses/gpl.txt */ #ifndef __PEERCHAT_INCLUDED__ #define __PEERCHAT_INCLUDED__ namespace pvpgn { typedef struct { unsigned char gs_peerchat_1; unsigned char gs_peerchat_2; unsigned char gs_peerchat_crypt[256]; } gs_peerchat_ctx; extern gs_peerchat_ctx * gs_peerchat_create(); extern void gs_peerchat_destroy(gs_peerchat_ctx const * ctx); extern void gs_peerchat_init(gs_peerchat_ctx *ctx, unsigned char *chall, unsigned char *gamekey); extern void gs_peerchat(gs_peerchat_ctx *ctx, unsigned char *data, int size); } #endif /* __PEERCHAT_INCLUDED__ */ pvpgn-1.99.7.2.1+dfsg/src/common/proginfo.cpp000066400000000000000000000043311462640461400206070ustar00rootroot00000000000000/* * Copyright (C) 2000 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 2001 Ross Combs (ross@bnetd.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/proginfo.h" #include #include #include #include "common/setup_after.h" namespace pvpgn { extern int verparts_to_vernum(unsigned short v1, unsigned short v2, unsigned short v3, unsigned short v4, unsigned long * vernum) { if (!vernum) return -1; *vernum = (((unsigned long)v4) << 24) | (((unsigned long)v3) << 16) | (((unsigned long)v2) << 8) | (((unsigned long)v1)); return 0; } extern int verstr_to_vernum(char const * verstr, unsigned long * vernum) { unsigned long v1, v2, v3, v4; if (!vernum) return -1; if (std::strchr(verstr, '.')) { int count; count = std::sscanf(verstr, "%lu.%lu.%lu.%lu", &v4, &v3, &v2, &v1); if (count < 4) { v1 = 0; if (count < 3) { v2 = 0; if (count < 2) return -1; /* no data */ } } } else { unsigned long temp; temp = std::strtoul(verstr, NULL, 10); v4 = (temp / 100); v3 = (temp / 10) % 10; v2 = (temp) % 10; v1 = 0; } if (v1 > 255 || v2 > 255 || v3 > 255 || v4 > 255) return -1; *vernum = (v4 << 24) | (v3 << 16) | (v2 << 8) | (v1); return 0; } extern char const * vernum_to_verstr(unsigned long vernum) { static char verstr[16]; std::sprintf(verstr, "%lu.%lu.%lu.%lu", (vernum >> 24), (vernum >> 16) & 0xff, (vernum >> 8) & 0xff, (vernum)& 0xff); return verstr; } } pvpgn-1.99.7.2.1+dfsg/src/common/proginfo.h000066400000000000000000000022241462640461400202530ustar00rootroot00000000000000/* * Copyright (C) 2001 Ross Combs (ross@bnetd.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_PROGINFO_PROTOS #define INCLUDED_PROGINFO_PROTOS namespace pvpgn { extern int verparts_to_vernum(unsigned short v1, unsigned short v2, unsigned short v3, unsigned short v4, unsigned long * vernum); extern int verstr_to_vernum(char const * verstr, unsigned long * vernum); extern char const * vernum_to_verstr(unsigned long vernum); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/pugiconfig.h000066400000000000000000000052721462640461400205700ustar00rootroot00000000000000/** * pugixml parser - version 1.4 * -------------------------------------------------------- * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at http://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef HEADER_PUGICONFIG_HPP #define HEADER_PUGICONFIG_HPP // Uncomment this to enable wchar_t mode //#define PUGIXML_WCHAR_MODE // Uncomment this to disable XPath // #define PUGIXML_NO_XPATH // Uncomment this to disable STL // #define PUGIXML_NO_STL // Uncomment this to disable exceptions // #define PUGIXML_NO_EXCEPTIONS // Set this to control attributes for public classes/functions, i.e.: // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead // Tune these constants to adjust memory-related behavior // #define PUGIXML_MEMORY_PAGE_SIZE 32768 // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 // Uncomment this to switch to header-only version // #define PUGIXML_HEADER_ONLY // #include "pugixml.cpp" // Uncomment this to enable long long support // #define PUGIXML_HAS_LONG_LONG #endif /** * Copyright (c) 2006-2014 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ pvpgn-1.99.7.2.1+dfsg/src/common/pugixml.cpp000066400000000000000000010273041462640461400204570ustar00rootroot00000000000000/** * pugixml parser - version 1.4 * -------------------------------------------------------- * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at http://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef SOURCE_PUGIXML_CPP #define SOURCE_PUGIXML_CPP #include "pugixml.h" #include #include #include #include #ifdef PUGIXML_WCHAR_MODE # include #endif #ifndef PUGIXML_NO_XPATH # include # include # ifdef PUGIXML_NO_EXCEPTIONS # include # endif #endif #ifndef PUGIXML_NO_STL # include # include # include #endif // For placement new #include #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4324) // structure was padded due to __declspec(align()) # pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable # pragma warning(disable: 4702) // unreachable code # pragma warning(disable: 4996) // this function or variable may be unsafe # pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged #endif #ifdef __INTEL_COMPILER # pragma warning(disable: 177) // function was declared but never referenced # pragma warning(disable: 279) // controlling expression is constant # pragma warning(disable: 1478 1786) // function was declared "deprecated" # pragma warning(disable: 1684) // conversion from pointer to same-sized integral type #endif #if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) # pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away #endif #ifdef __BORLANDC__ # pragma option push # pragma warn -8008 // condition is always false # pragma warn -8066 // unreachable code #endif #ifdef __SNC__ // Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug # pragma diag_suppress=178 // function was declared but never referenced # pragma diag_suppress=237 // controlling expression is constant #endif // Inlining controls #if defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGI__NO_INLINE __declspec(noinline) #elif defined(__GNUC__) # define PUGI__NO_INLINE __attribute__((noinline)) #else # define PUGI__NO_INLINE #endif // Simple static assertion #define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } // Digital Mars C++ bug workaround for passing char loaded from memory via stack #ifdef __DMC__ # define PUGI__DMC_VOLATILE volatile #else # define PUGI__DMC_VOLATILE #endif // Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) #if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) using std::memcpy; using std::memmove; #endif // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features #if defined(_MSC_VER) && !defined(__S3E__) # define PUGI__MSVC_CRT_VERSION _MSC_VER #endif #ifdef PUGIXML_HEADER_ONLY # define PUGI__NS_BEGIN namespace pugi { namespace impl { # define PUGI__NS_END } } # define PUGI__FN inline # define PUGI__FN_NO_INLINE inline #else # if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces # define PUGI__NS_BEGIN namespace pugi { namespace impl { # define PUGI__NS_END } } # else # define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { # define PUGI__NS_END } } } # endif # define PUGI__FN # define PUGI__FN_NO_INLINE PUGI__NO_INLINE #endif // uintptr_t #if !defined(_MSC_VER) || _MSC_VER >= 1600 # include #else # ifndef _UINTPTR_T_DEFINED // No native uintptr_t in MSVC6 and in some WinCE versions typedef size_t uintptr_t; #define _UINTPTR_T_DEFINED # endif PUGI__NS_BEGIN typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; PUGI__NS_END #endif // Memory allocation PUGI__NS_BEGIN PUGI__FN void* default_allocate(size_t size) { return malloc(size); } PUGI__FN void default_deallocate(void* ptr) { free(ptr); } template struct xml_memory_management_function_storage { static allocation_function allocate; static deallocation_function deallocate; }; template allocation_function xml_memory_management_function_storage::allocate = default_allocate; template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; typedef xml_memory_management_function_storage xml_memory; PUGI__NS_END // String utilities PUGI__NS_BEGIN // Get string length PUGI__FN size_t strlength(const char_t* s) { assert(s); #ifdef PUGIXML_WCHAR_MODE return wcslen(s); #else return strlen(s); #endif } // Compare two strings PUGI__FN bool strequal(const char_t* src, const char_t* dst) { assert(src && dst); #ifdef PUGIXML_WCHAR_MODE return wcscmp(src, dst) == 0; #else return strcmp(src, dst) == 0; #endif } // Compare lhs with [rhs_begin, rhs_end) PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) { for (size_t i = 0; i < count; ++i) if (lhs[i] != rhs[i]) return false; return lhs[count] == 0; } // Get length of wide string, even if CRT lacks wide character support PUGI__FN size_t strlength_wide(const wchar_t* s) { assert(s); #ifdef PUGIXML_WCHAR_MODE return wcslen(s); #else const wchar_t* end = s; while (*end) end++; return static_cast(end - s); #endif } #ifdef PUGIXML_WCHAR_MODE // Convert string to wide string, assuming all symbols are ASCII PUGI__FN void widen_ascii(wchar_t* dest, const char* source) { for (const char* i = source; *i; ++i) *dest++ = *i; *dest = 0; } #endif PUGI__NS_END #if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) // auto_ptr-like buffer holder for exception recovery PUGI__NS_BEGIN struct buffer_holder { void* data; void (*deleter)(void*); buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_) { } ~buffer_holder() { if (data) deleter(data); } void* release() { void* result = data; data = 0; return result; } }; PUGI__NS_END #endif PUGI__NS_BEGIN static const size_t xml_memory_page_size = #ifdef PUGIXML_MEMORY_PAGE_SIZE PUGIXML_MEMORY_PAGE_SIZE #else 32768 #endif ; static const uintptr_t xml_memory_page_alignment = 32; static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); static const uintptr_t xml_memory_page_name_allocated_mask = 16; static const uintptr_t xml_memory_page_value_allocated_mask = 8; static const uintptr_t xml_memory_page_type_mask = 7; struct xml_allocator; struct xml_memory_page { static xml_memory_page* construct(void* memory) { if (!memory) return 0; //$ redundant, left for performance xml_memory_page* result = static_cast(memory); result->allocator = 0; result->memory = 0; result->prev = 0; result->next = 0; result->busy_size = 0; result->freed_size = 0; return result; } xml_allocator* allocator; void* memory; xml_memory_page* prev; xml_memory_page* next; size_t busy_size; size_t freed_size; char data[1]; }; struct xml_memory_string_header { uint16_t page_offset; // offset from page->data uint16_t full_size; // 0 if string occupies whole page }; struct xml_allocator { xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) { } xml_memory_page* allocate_page(size_t data_size) { size_t size = offsetof(xml_memory_page, data) + data_size; // allocate block with some alignment, leaving memory for worst-case padding void* memory = xml_memory::allocate(size + xml_memory_page_alignment); if (!memory) return 0; // align upwards to page boundary void* page_memory = reinterpret_cast((reinterpret_cast(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1)); // prepare page structure xml_memory_page* page = xml_memory_page::construct(page_memory); assert(page); page->memory = memory; page->allocator = _root->allocator; return page; } static void deallocate_page(xml_memory_page* page) { xml_memory::deallocate(page->memory); } void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); void* allocate_memory(size_t size, xml_memory_page*& out_page) { if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page); void* buf = _root->data + _busy_size; _busy_size += size; out_page = _root; return buf; } void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) { if (page == _root) page->busy_size = _busy_size; assert(ptr >= page->data && ptr < page->data + page->busy_size); (void)!ptr; page->freed_size += size; assert(page->freed_size <= page->busy_size); if (page->freed_size == page->busy_size) { if (page->next == 0) { assert(_root == page); // top page freed, just reset sizes page->busy_size = page->freed_size = 0; _busy_size = 0; } else { assert(_root != page); assert(page->prev); // remove from the list page->prev->next = page->next; page->next->prev = page->prev; // deallocate deallocate_page(page); } } } char_t* allocate_string(size_t length) { // allocate memory for string and header block size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); // round size up to pointer alignment boundary size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1); xml_memory_page* page; xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); if (!header) return 0; // setup header ptrdiff_t page_offset = reinterpret_cast(header) - page->data; assert(page_offset >= 0 && page_offset < (1 << 16)); header->page_offset = static_cast(page_offset); // full_size == 0 for large strings that occupy the whole page assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0)); header->full_size = static_cast(full_size < (1 << 16) ? full_size : 0); // round-trip through void* to avoid 'cast increases required alignment of target type' warning // header is guaranteed a pointer-sized alignment, which should be enough for char_t return static_cast(static_cast(header + 1)); } void deallocate_string(char_t* string) { // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string // get header xml_memory_string_header* header = static_cast(static_cast(string)) - 1; // deallocate size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset; xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); // if full_size == 0 then this string occupies the whole page size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size; deallocate_memory(header, full_size, page); } xml_memory_page* _root; size_t _busy_size; }; PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) { const size_t large_allocation_threshold = xml_memory_page_size / 4; xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); out_page = page; if (!page) return 0; if (size <= large_allocation_threshold) { _root->busy_size = _busy_size; // insert page at the end of linked list page->prev = _root; _root->next = page; _root = page; _busy_size = size; } else { // insert page before the end of linked list, so that it is deleted as soon as possible // the last page is not deleted even if it's empty (see deallocate_memory) assert(_root->prev); page->prev = _root->prev; page->next = _root; _root->prev->next = page; _root->prev = page; } // allocate inside page page->busy_size = size; return page->data; } PUGI__NS_END namespace pugi { /// A 'name=value' XML attribute structure. struct xml_attribute_struct { /// Default ctor xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) { } uintptr_t header; char_t* name; ///< Pointer to attribute name. char_t* value; ///< Pointer to attribute value. xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list) xml_attribute_struct* next_attribute; ///< Next attribute }; /// An XML document tree node. struct xml_node_struct { /// Default ctor /// \param type - node type xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) { } uintptr_t header; xml_node_struct* parent; ///< Pointer to parent char_t* name; ///< Pointer to element name. char_t* value; ///< Pointer to any associated string data. xml_node_struct* first_child; ///< First child xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list) xml_node_struct* next_sibling; ///< Right brother xml_attribute_struct* first_attribute; ///< First attribute }; } PUGI__NS_BEGIN struct xml_extra_buffer { char_t* buffer; xml_extra_buffer* next; }; struct xml_document_struct: public xml_node_struct, public xml_allocator { xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) { } const char_t* buffer; xml_extra_buffer* extra_buffers; }; inline xml_allocator& get_allocator(const xml_node_struct* node) { assert(node); return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; } PUGI__NS_END // Low-level DOM operations PUGI__NS_BEGIN inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) { xml_memory_page* page; void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page); return new (memory) xml_attribute_struct(page); } inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) { xml_memory_page* page; void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page); return new (memory) xml_node_struct(page, type); } inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) { uintptr_t header = a->header; if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); } inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) { uintptr_t header = n->header; if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value); for (xml_attribute_struct* attr = n->first_attribute; attr; ) { xml_attribute_struct* next = attr->next_attribute; destroy_attribute(attr, alloc); attr = next; } for (xml_node_struct* child = n->first_child; child; ) { xml_node_struct* next = child->next_sibling; destroy_node(child, alloc); child = next; } alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); } PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) { xml_node_struct* child = allocate_node(alloc, type); if (!child) return 0; child->parent = node; xml_node_struct* first_child = node->first_child; if (first_child) { xml_node_struct* last_child = first_child->prev_sibling_c; last_child->next_sibling = child; child->prev_sibling_c = last_child; first_child->prev_sibling_c = child; } else { node->first_child = child; child->prev_sibling_c = child; } return child; } PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc) { xml_attribute_struct* a = allocate_attribute(alloc); if (!a) return 0; xml_attribute_struct* first_attribute = node->first_attribute; if (first_attribute) { xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c; last_attribute->next_attribute = a; a->prev_attribute_c = last_attribute; first_attribute->prev_attribute_c = a; } else { node->first_attribute = a; a->prev_attribute_c = a; } return a; } PUGI__NS_END // Helper classes for code generation PUGI__NS_BEGIN struct opt_false { enum { value = 0 }; }; struct opt_true { enum { value = 1 }; }; PUGI__NS_END // Unicode utilities PUGI__NS_BEGIN inline uint16_t endian_swap(uint16_t value) { return static_cast(((value & 0xff) << 8) | (value >> 8)); } inline uint32_t endian_swap(uint32_t value) { return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); } struct utf8_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) return result + 1; // U+0080..U+07FF else if (ch < 0x800) return result + 2; // U+0800..U+FFFF else return result + 3; } static value_type high(value_type result, uint32_t) { // U+10000..U+10FFFF return result + 4; } }; struct utf8_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) { *result = static_cast(ch); return result + 1; } // U+0080..U+07FF else if (ch < 0x800) { result[0] = static_cast(0xC0 | (ch >> 6)); result[1] = static_cast(0x80 | (ch & 0x3F)); return result + 2; } // U+0800..U+FFFF else { result[0] = static_cast(0xE0 | (ch >> 12)); result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[2] = static_cast(0x80 | (ch & 0x3F)); return result + 3; } } static value_type high(value_type result, uint32_t ch) { // U+10000..U+10FFFF result[0] = static_cast(0xF0 | (ch >> 18)); result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[3] = static_cast(0x80 | (ch & 0x3F)); return result + 4; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf16_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 2; } }; struct utf16_writer { typedef uint16_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { uint32_t msh = static_cast(ch - 0x10000) >> 10; uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; result[0] = static_cast(0xD800 + msh); result[1] = static_cast(0xDC00 + lsh); return result + 2; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf32_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 1; } }; struct utf32_writer { typedef uint32_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type high(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type any(value_type result, uint32_t ch) { *result = ch; return result + 1; } }; struct latin1_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch > 255 ? '?' : ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { (void)ch; *result = '?'; return result + 1; } }; template struct wchar_selector; template <> struct wchar_selector<2> { typedef uint16_t type; typedef utf16_counter counter; typedef utf16_writer writer; }; template <> struct wchar_selector<4> { typedef uint32_t type; typedef utf32_counter counter; typedef utf32_writer writer; }; typedef wchar_selector::counter wchar_counter; typedef wchar_selector::writer wchar_writer; template struct utf_decoder { static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result) { const uint8_t utf8_byte_mask = 0x3f; while (size) { uint8_t lead = *data; // 0xxxxxxx -> U+0000..U+007F if (lead < 0x80) { result = Traits::low(result, lead); data += 1; size -= 1; // process aligned single-byte (ascii) blocks if ((reinterpret_cast(data) & 3) == 0) { // round-trip through void* to silence 'cast increases required alignment of target type' warnings while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) { result = Traits::low(result, data[0]); result = Traits::low(result, data[1]); result = Traits::low(result, data[2]); result = Traits::low(result, data[3]); data += 4; size -= 4; } } } // 110xxxxx -> U+0080..U+07FF else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); data += 2; size -= 2; } // 1110xxxx -> U+0800-U+FFFF else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); data += 3; size -= 3; } // 11110xxx -> U+10000..U+10FFFF else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) { result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); data += 4; size -= 4; } // 10xxxxxx or 11111xxx -> invalid else { data += 1; size -= 1; } } return result; } static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result) { const uint16_t* end = data + size; while (data < end) { unsigned int lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+D7FF if (lead < 0xD800) { result = Traits::low(result, lead); data += 1; } // U+E000..U+FFFF else if (static_cast(lead - 0xE000) < 0x2000) { result = Traits::low(result, lead); data += 1; } // surrogate pair lead else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end) { uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; if (static_cast(next - 0xDC00) < 0x400) { result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); data += 2; } else { data += 1; } } else { data += 1; } } return result; } static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result) { const uint32_t* end = data + size; while (data < end) { uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+FFFF if (lead < 0x10000) { result = Traits::low(result, lead); data += 1; } // U+10000..U+10FFFF else { result = Traits::high(result, lead); data += 1; } } return result; } static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result) { for (size_t i = 0; i < size; ++i) { result = Traits::low(result, data[i]); } return result; } static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result) { return decode_utf16_block(data, size, result); } static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result) { return decode_utf32_block(data, size, result); } static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result) { return decode_wchar_block_impl(reinterpret_cast::type*>(data), size, result); } }; template PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length) { for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]); } #ifdef PUGIXML_WCHAR_MODE PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) { for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); } #endif PUGI__NS_END PUGI__NS_BEGIN enum chartype_t { ct_parse_pcdata = 1, // \0, &, \r, < ct_parse_attr = 2, // \0, &, \r, ', " ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab ct_space = 8, // \r, \n, space, tab ct_parse_cdata = 16, // \0, ], >, \r ct_parse_comment = 32, // \0, -, >, \r ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : }; static const unsigned char chartype_table[256] = { 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 }; enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . }; static const unsigned char chartypex_table[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 }; #ifdef PUGIXML_WCHAR_MODE #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) #else #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) #endif #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) PUGI__FN bool is_little_endian() { unsigned int ui = 1; return *reinterpret_cast(&ui) == 1; } PUGI__FN xml_encoding get_wchar_encoding() { PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); if (sizeof(wchar_t) == 2) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; else return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; } PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) { // look for BOM in first few bytes if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; // look for <, (contents); PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; return guess_buffer_encoding(d0, d1, d2, d3); } PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { size_t length = size / sizeof(char_t); if (is_mutable) { out_buffer = static_cast(const_cast(contents)); out_length = length; } else { char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; memcpy(buffer, contents, length * sizeof(char_t)); buffer[length] = 0; out_buffer = buffer; out_length = length + 1; } return true; } #ifdef PUGIXML_WCHAR_MODE PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) { return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); } PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const char_t* data = static_cast(contents); size_t length = size / sizeof(char_t); if (is_mutable) { char_t* buffer = const_cast(data); convert_wchar_endian_swap(buffer, data, length); out_buffer = buffer; out_length = length; } else { char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; convert_wchar_endian_swap(buffer, data, length); buffer[length] = 0; out_buffer = buffer; out_length = length + 1; } return true; } PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) { const uint8_t* data = static_cast(contents); size_t data_length = size; // first pass: get length in wchar_t units size_t length = utf_decoder::decode_utf8_block(data, data_length, 0); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf8 input to wchar_t wchar_writer::value_type obegin = reinterpret_cast(buffer); wchar_writer::value_type oend = utf_decoder::decode_utf8_block(data, data_length, obegin); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint16_t* data = static_cast(contents); size_t data_length = size / sizeof(uint16_t); // first pass: get length in wchar_t units size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf16 input to wchar_t wchar_writer::value_type obegin = reinterpret_cast(buffer); wchar_writer::value_type oend = utf_decoder::decode_utf16_block(data, data_length, obegin); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint32_t* data = static_cast(contents); size_t data_length = size / sizeof(uint32_t); // first pass: get length in wchar_t units size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf32 input to wchar_t wchar_writer::value_type obegin = reinterpret_cast(buffer); wchar_writer::value_type oend = utf_decoder::decode_utf32_block(data, data_length, obegin); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) { const uint8_t* data = static_cast(contents); size_t data_length = size; // get length in wchar_t units size_t length = data_length; // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // convert latin1 input to wchar_t wchar_writer::value_type obegin = reinterpret_cast(buffer); wchar_writer::value_type oend = utf_decoder::decode_latin1_block(data, data_length, obegin); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // get native encoding xml_encoding wchar_encoding = get_wchar_encoding(); // fast path: no conversion required if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // only endian-swapping is required if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf8 if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size); assert(!"Invalid encoding"); return false; } #else template PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint16_t* data = static_cast(contents); size_t data_length = size / sizeof(uint16_t); // first pass: get length in utf8 units size_t length = utf_decoder::decode_utf16_block(data, data_length, 0); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf16 input to utf8 uint8_t* obegin = reinterpret_cast(buffer); uint8_t* oend = utf_decoder::decode_utf16_block(data, data_length, obegin); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } template PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) { const uint32_t* data = static_cast(contents); size_t data_length = size / sizeof(uint32_t); // first pass: get length in utf8 units size_t length = utf_decoder::decode_utf32_block(data, data_length, 0); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf32 input to utf8 uint8_t* obegin = reinterpret_cast(buffer); uint8_t* oend = utf_decoder::decode_utf32_block(data, data_length, obegin); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) { for (size_t i = 0; i < size; ++i) if (data[i] > 127) return i; return size; } PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const uint8_t* data = static_cast(contents); size_t data_length = size; // get size of prefix that does not need utf8 conversion size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); assert(prefix_length <= data_length); const uint8_t* postfix = data + prefix_length; size_t postfix_length = data_length - prefix_length; // if no conversion is needed, just return the original buffer if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // first pass: get length in utf8 units size_t length = prefix_length + utf_decoder::decode_latin1_block(postfix, postfix_length, 0); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert latin1 input to utf8 memcpy(buffer, data, prefix_length); uint8_t* obegin = reinterpret_cast(buffer); uint8_t* oend = utf_decoder::decode_latin1_block(postfix, postfix_length, obegin + prefix_length); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // fast path: no conversion required if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) : convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); assert(!"Invalid encoding"); return false; } #endif PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) { // get length in utf8 characters return utf_decoder::decode_wchar_block(str, length, 0); } PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) { // convert to utf8 uint8_t* begin = reinterpret_cast(buffer); uint8_t* end = utf_decoder::decode_wchar_block(str, length, begin); assert(begin + size == end); (void)!end; // zero-terminate buffer[size] = 0; } #ifndef PUGIXML_NO_STL PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) { // first pass: get length in utf8 characters size_t size = as_utf8_begin(str, length); // allocate resulting string std::string result; result.resize(size); // second pass: convert to utf8 if (size > 0) as_utf8_end(&result[0], size, str, length); return result; } PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) { const uint8_t* data = reinterpret_cast(str); // first pass: get length in wchar_t units size_t length = utf_decoder::decode_utf8_block(data, size, 0); // allocate resulting string std::basic_string result; result.resize(length); // second pass: convert to wchar_t if (length > 0) { wchar_writer::value_type begin = reinterpret_cast(&result[0]); wchar_writer::value_type end = utf_decoder::decode_utf8_block(data, size, begin); assert(begin + length == end); (void)!end; } return result; } #endif inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target) { assert(target); size_t target_length = strlength(target); // always reuse document buffer memory if possible if (!allocated) return target_length >= length; // reuse heap memory if waste is not too great const size_t reuse_threshold = 32; return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); } PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source) { assert(header); size_t source_length = strlength(source); if (source_length == 0) { // empty string and null pointer are equivalent, so just deallocate old memory xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; if (header & header_mask) alloc->deallocate_string(dest); // mark the string as not allocated dest = 0; header &= ~header_mask; return true; } else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest)) { // we can reuse old buffer, so just copy the new data (including zero terminator) memcpy(dest, source, (source_length + 1) * sizeof(char_t)); return true; } else { xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; // allocate new buffer char_t* buf = alloc->allocate_string(source_length + 1); if (!buf) return false; // copy the string (including zero terminator) memcpy(buf, source, (source_length + 1) * sizeof(char_t)); // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) if (header & header_mask) alloc->deallocate_string(dest); // the string is now allocated, so set the flag dest = buf; header |= header_mask; return true; } } struct gap { char_t* end; size_t size; gap(): end(0), size(0) { } // Push new gap, move s count bytes further (skipping the gap). // Collapse previous gap. void push(char_t*& s, size_t count) { if (end) // there was a gap already; collapse it { // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); } s += count; // end of current gap // "merge" two gaps end = s; size += count; } // Collapse all gaps, return past-the-end pointer char_t* flush(char_t* s) { if (end) { // Move [old_gap_end, current_pos) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); return s - size; } else return s; } }; PUGI__FN char_t* strconv_escape(char_t* s, gap& g) { char_t* stre = s + 1; switch (*stre) { case '#': // &#... { unsigned int ucsc = 0; if (stre[1] == 'x') // &#x... (hex code) { stre += 2; char_t ch = *stre; if (ch == ';') return stre; for (;;) { if (static_cast(ch - '0') <= 9) ucsc = 16 * ucsc + (ch - '0'); else if (static_cast((ch | ' ') - 'a') <= 5) ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } else // &#... (dec code) { char_t ch = *++stre; if (ch == ';') return stre; for (;;) { if (static_cast(static_cast(ch) - '0') <= 9) ucsc = 10 * ucsc + (ch - '0'); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } #ifdef PUGIXML_WCHAR_MODE s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); #else s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); #endif g.push(s, stre - s); return stre; } case 'a': // &a { ++stre; if (*stre == 'm') // &am { if (*++stre == 'p' && *++stre == ';') // & { *s++ = '&'; ++stre; g.push(s, stre - s); return stre; } } else if (*stre == 'p') // &ap { if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' { *s++ = '\''; ++stre; g.push(s, stre - s); return stre; } } break; } case 'g': // &g { if (*++stre == 't' && *++stre == ';') // > { *s++ = '>'; ++stre; g.push(s, stre - s); return stre; } break; } case 'l': // &l { if (*++stre == 't' && *++stre == ';') // < { *s++ = '<'; ++stre; g.push(s, stre - s); return stre; } break; } case 'q': // &q { if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " { *s++ = '"'; ++stre; g.push(s, stre - s); return stre; } break; } default: break; } return stre; } // Utility macro for last character handling #define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s; if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here { *g.flush(s) = 0; return s + (s[2] == '>' ? 3 : 2); } else if (*s == 0) { return 0; } else ++s; } } PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s; if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here { *g.flush(s) = 0; return s + 1; } else if (*s == 0) { return 0; } else ++s; } } typedef char_t* (*strconv_pcdata_t)(char_t*); template struct strconv_pcdata_impl { static char_t* parse(char_t* s) { gap g; char_t* begin = s; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s; if (*s == '<') // PCDATA ends here { char_t* end = g.flush(s); if (opt_trim::value) while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; return s + 1; } else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (*s == 0) { char_t* end = g.flush(s); if (opt_trim::value) while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; return s; } else ++s; } } }; PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) { case 0: return strconv_pcdata_impl::parse; case 1: return strconv_pcdata_impl::parse; case 2: return strconv_pcdata_impl::parse; case 3: return strconv_pcdata_impl::parse; case 4: return strconv_pcdata_impl::parse; case 5: return strconv_pcdata_impl::parse; case 6: return strconv_pcdata_impl::parse; case 7: return strconv_pcdata_impl::parse; default: assert(false); return 0; // should not get here } } typedef char_t* (*strconv_attribute_t)(char_t*, char_t); template struct strconv_attribute_impl { static char_t* parse_wnorm(char_t* s, char_t end_quote) { gap g; // trim leading whitespaces if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s; do ++str; while (PUGI__IS_CHARTYPE(*str, ct_space)); g.push(s, str - s); } while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s; if (*s == end_quote) { char_t* str = g.flush(s); do *str-- = 0; while (PUGI__IS_CHARTYPE(*str, ct_space)); return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { *s++ = ' '; if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s + 1; while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; g.push(s, str - s); } } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_wconv(char_t* s, char_t end_quote) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s; if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { if (*s == '\r') { *s++ = ' '; if (*s == '\n') g.push(s, 1); } else *s++ = ' '; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_eol(char_t* s, char_t end_quote) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s; if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (*s == '\r') { *s++ = '\n'; if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_simple(char_t* s, char_t end_quote) { gap g; while (true) { while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s; if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } }; PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) { case 0: return strconv_attribute_impl::parse_simple; case 1: return strconv_attribute_impl::parse_simple; case 2: return strconv_attribute_impl::parse_eol; case 3: return strconv_attribute_impl::parse_eol; case 4: return strconv_attribute_impl::parse_wconv; case 5: return strconv_attribute_impl::parse_wconv; case 6: return strconv_attribute_impl::parse_wconv; case 7: return strconv_attribute_impl::parse_wconv; case 8: return strconv_attribute_impl::parse_wnorm; case 9: return strconv_attribute_impl::parse_wnorm; case 10: return strconv_attribute_impl::parse_wnorm; case 11: return strconv_attribute_impl::parse_wnorm; case 12: return strconv_attribute_impl::parse_wnorm; case 13: return strconv_attribute_impl::parse_wnorm; case 14: return strconv_attribute_impl::parse_wnorm; case 15: return strconv_attribute_impl::parse_wnorm; default: assert(false); return 0; // should not get here } } inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) { xml_parse_result result; result.status = status; result.offset = offset; return result; } struct xml_parser { xml_allocator alloc; char_t* error_offset; xml_parse_status error_status; // Parser utilities. #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) #define PUGI__PUSHNODE(TYPE) { cursor = append_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } #define PUGI__POPNODE() { cursor = cursor->parent; } #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } #define PUGI__SCANWHILE(X) { while ((X)) ++s; } #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) { } // DOCTYPE consists of nested sections of the following possible types: // , , "...", '...' // // // First group can not contain nested groups // Second group can contain nested groups of the same type // Third group can contain all other groups char_t* parse_doctype_primitive(char_t* s) { if (*s == '"' || *s == '\'') { // quoted string char_t ch = *s++; PUGI__SCANFOR(*s == ch); if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s++; } else if (s[0] == '<' && s[1] == '?') { // s += 2; PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 2; } else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') { s += 4; PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 4; } else PUGI__THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_doctype_ignore(char_t* s) { assert(s[0] == '<' && s[1] == '!' && s[2] == '['); s++; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] == '[') { // nested ignore section s = parse_doctype_ignore(s); if (!s) return s; } else if (s[0] == ']' && s[1] == ']' && s[2] == '>') { // ignore section end s += 3; return s; } else s++; } PUGI__THROW_ERROR(status_bad_doctype, s); } char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel) { assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); s++; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] != '-') { if (s[2] == '[') { // ignore s = parse_doctype_ignore(s); if (!s) return s; } else { // some control group s = parse_doctype_group(s, endch, false); if (!s) return s; // skip > assert(*s == '>'); s++; } } else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') { // unknown tag (forbidden), or some primitive group s = parse_doctype_primitive(s); if (!s) return s; } else if (*s == '>') { return s; } else s++; } if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) { // parse node contents, starting with exclamation mark ++s; if (*s == '-') // 'value = s; // Save the offset. } if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) { s = strconv_comment(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); } else { // Scan for terminating '-->'. PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_comment, s); if (PUGI__OPTSET(parse_comments)) *s = 0; // Zero-terminate this segment at the first terminating '-'. s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. } } else PUGI__THROW_ERROR(status_bad_comment, s); } else if (*s == '[') { // 'value = s; // Save the offset. if (PUGI__OPTSET(parse_eol)) { s = strconv_cdata(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); } else { // Scan for terminating ']]>'. PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_cdata, s); *s++ = 0; // Zero-terminate this segment. } } else // Flagged for discard, but we still have to scan for the terminator. { // Scan for terminating ']]>'. PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_cdata, s); ++s; } s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. } else PUGI__THROW_ERROR(status_bad_cdata, s); } else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E')) { s -= 2; if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); char_t* mark = s + 9; s = parse_doctype_group(s, endch, true); if (!s) return s; assert((*s == 0 && endch == '>') || *s == '>'); if (*s) *s++ = 0; if (PUGI__OPTSET(parse_doctype)) { while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; PUGI__PUSHNODE(node_doctype); cursor->value = mark; PUGI__POPNODE(); } } else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); else PUGI__THROW_ERROR(status_unrecognized_tag, s); return s; } char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) { // load into registers xml_node_struct* cursor = ref_cursor; char_t ch = 0; // parse node contents, starting with question mark ++s; // read PI target char_t* target = s; if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); PUGI__CHECK_ERROR(status_bad_pi, s); // determine node type; stricmp / strcasecmp is not portable bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) { if (declaration) { // disallow non top-level declarations if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__PUSHNODE(node_declaration); } else { PUGI__PUSHNODE(node_pi); } cursor->name = target; PUGI__ENDSEG(); // parse value/attributes if (ch == '?') { // empty node if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); s += (*s == '>'); PUGI__POPNODE(); } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // scan for tag end char_t* value = s; PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); PUGI__CHECK_ERROR(status_bad_pi, s); if (declaration) { // replace ending ? with / so that 'element' terminates properly *s = '/'; // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES s = value; } else { // store value and step over > cursor->value = value; PUGI__POPNODE(); PUGI__ENDSEG(); s += (*s == '>'); } } else PUGI__THROW_ERROR(status_bad_pi, s); } else { // scan for tag end PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); PUGI__CHECK_ERROR(status_bad_pi, s); s += (s[1] == '>' ? 2 : 1); } // store from registers ref_cursor = cursor; return s; } char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) { strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); char_t ch = 0; xml_node_struct* cursor = root; char_t* mark = s; while (*s != 0) { if (*s == '<') { ++s; LOC_TAG: if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' { PUGI__PUSHNODE(node_element); // Append a new node to the tree. cursor->name = s; PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. if (ch == '>') { // end of tag } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { LOC_ATTRIBUTES: while (true) { PUGI__SKIPWS(); // Eat any whitespace. if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... { xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute. if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); a->name = s; // Save the offset. PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // Eat any whitespace. PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance ch = *s; ++s; } if (ch == '=') // '<... #=...' { PUGI__SKIPWS(); // Eat any whitespace. if (*s == '"' || *s == '\'') // '<... #="...' { ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. ++s; // Step over the quote. a->value = s; // Save the offset. s = strconv_attribute(s, ch); if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); // After this line the loop continues from the start; // Whitespaces, / and > are ok, symbols and EOF are wrong, // everything else will be detected if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else if (*s == '/') { ++s; if (*s == '>') { PUGI__POPNODE(); s++; break; } else if (*s == 0 && endch == '>') { PUGI__POPNODE(); break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '>') { ++s; break; } else if (*s == 0 && endch == '>') { break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } // !!! } else if (ch == '/') // '<#.../' { if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); PUGI__POPNODE(); // Pop. s += (*s == '>'); } else if (ch == 0) { // we stepped over null terminator, backtrack & handle closing tag --s; if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '/') { ++s; char_t* name = cursor->name; if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s); while (PUGI__IS_CHARTYPE(*s, ct_symbol)) { if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s); } if (*name) { if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); else PUGI__THROW_ERROR(status_end_element_mismatch, s); } PUGI__POPNODE(); // Pop. PUGI__SKIPWS(); if (*s == 0) { if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); } else { if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); ++s; } } else if (*s == '?') // 'header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES; } else if (*s == '!') // 'first_child) continue; } } if (!PUGI__OPTSET(parse_trim_pcdata)) s = mark; if (cursor->parent || PUGI__OPTSET(parse_fragment)) { PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. cursor->value = s; // Save the offset. s = strconv_pcdata(s); PUGI__POPNODE(); // Pop since this is a standalone. if (!*s) break; } else { PUGI__SCANFOR(*s == '<'); // '...<' if (!*s) break; ++s; } // We're after '<' goto LOC_TAG; } } // check that last tag is closed if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); return s; } #ifdef PUGIXML_WCHAR_MODE static char_t* parse_skip_bom(char_t* s) { unsigned int bom = 0xfeff; return (s[0] == static_cast(bom)) ? s + 1 : s; } #else static char_t* parse_skip_bom(char_t* s) { return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; } #endif static bool has_element_node_siblings(xml_node_struct* node) { while (node) { xml_node_type type = static_cast((node->header & impl::xml_memory_page_type_mask) + 1); if (type == node_element) return true; node = node->next_sibling; } return false; } static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) { // allocator object is a part of document object xml_allocator& alloc = *static_cast(xmldoc); // early-out for empty documents if (length == 0) return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); // get last child of the root before parsing xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c : 0; // create parser on stack xml_parser parser(alloc); // save last character and make buffer zero-terminated (speeds up parsing) char_t endch = buffer[length - 1]; buffer[length - 1] = 0; // skip BOM to make sure it does not end up as part of parse output char_t* buffer_data = parse_skip_bom(buffer); // perform actual parsing parser.parse_tree(buffer_data, root, optmsk, endch); // update allocator state alloc = parser.alloc; xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); assert(result.offset >= 0 && static_cast(result.offset) <= length); if (result) { // since we removed last character, we have to handle the only possible false positive (stray <) if (endch == '<') return make_parse_result(status_unrecognized_tag, length - 1); // check if there are any element nodes parsed xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling : root->first_child; if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) return make_parse_result(status_no_document_element, length - 1); } else { // roll back offset if it occurs on a null terminator in the source buffer if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) result.offset--; } return result; } }; // Output facilities PUGI__FN xml_encoding get_write_native_encoding() { #ifdef PUGIXML_WCHAR_MODE return get_wchar_encoding(); #else return encoding_utf8; #endif } PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) { // replace wchar encoding with utf implementation if (encoding == encoding_wchar) return get_wchar_encoding(); // replace utf16 encoding with utf16 with specific endianness if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; // replace utf32 encoding with utf32 with specific endianness if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; // only do autodetection if no explicit encoding is requested if (encoding != encoding_auto) return encoding; // assume utf8 encoding return encoding_utf8; } #ifdef PUGIXML_WCHAR_MODE PUGI__FN size_t get_valid_length(const char_t* data, size_t length) { assert(length > 0); // discard last character if it's the lead of a surrogate pair return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; } PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { // only endian-swapping is required if (need_endian_swap_utf(encoding, get_wchar_encoding())) { convert_wchar_endian_swap(r_char, data, length); return length * sizeof(char_t); } // convert to utf8 if (encoding == encoding_utf8) { uint8_t* dest = r_u8; uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); return static_cast(end - dest); } // convert to utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { uint16_t* dest = r_u16; // convert to native utf16 uint16_t* end = utf_decoder::decode_wchar_block(data, length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint16_t); } // convert to utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { uint32_t* dest = r_u32; // convert to native utf32 uint32_t* end = utf_decoder::decode_wchar_block(data, length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint32_t); } // convert to latin1 if (encoding == encoding_latin1) { uint8_t* dest = r_u8; uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); return static_cast(end - dest); } assert(!"Invalid encoding"); return 0; } #else PUGI__FN size_t get_valid_length(const char_t* data, size_t length) { assert(length > 4); for (size_t i = 1; i <= 4; ++i) { uint8_t ch = static_cast(data[length - i]); // either a standalone character or a leading one if ((ch & 0xc0) != 0x80) return length - i; } // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk return length; } PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { uint16_t* dest = r_u16; // convert to native utf16 uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint16_t); } if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { uint32_t* dest = r_u32; // convert to native utf32 uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); // swap if necessary xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); return static_cast(end - dest) * sizeof(uint32_t); } if (encoding == encoding_latin1) { uint8_t* dest = r_u8; uint8_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); return static_cast(end - dest); } assert(!"Invalid encoding"); return 0; } #endif class xml_buffered_writer { xml_buffered_writer(const xml_buffered_writer&); xml_buffered_writer& operator=(const xml_buffered_writer&); public: xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) { PUGI__STATIC_ASSERT(bufcapacity >= 8); } ~xml_buffered_writer() { flush(); } void flush() { flush(buffer, bufsize); bufsize = 0; } void flush(const char_t* data, size_t size) { if (size == 0) return; // fast path, just write data if (encoding == get_write_native_encoding()) writer.write(data, size * sizeof(char_t)); else { // convert chunk size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); assert(result <= sizeof(scratch)); // write data writer.write(scratch.data_u8, result); } } void write(const char_t* data, size_t length) { if (bufsize + length > bufcapacity) { // flush the remaining buffer contents flush(); // handle large chunks if (length > bufcapacity) { if (encoding == get_write_native_encoding()) { // fast path, can just write data chunk writer.write(data, length * sizeof(char_t)); return; } // need to convert in suitable chunks while (length > bufcapacity) { // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) size_t chunk_size = get_valid_length(data, bufcapacity); // convert chunk and write flush(data, chunk_size); // iterate data += chunk_size; length -= chunk_size; } // small tail is copied below bufsize = 0; } } memcpy(buffer + bufsize, data, length * sizeof(char_t)); bufsize += length; } void write(const char_t* data) { write(data, strlength(data)); } void write(char_t d0) { if (bufsize + 1 > bufcapacity) flush(); buffer[bufsize + 0] = d0; bufsize += 1; } void write(char_t d0, char_t d1) { if (bufsize + 2 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; bufsize += 2; } void write(char_t d0, char_t d1, char_t d2) { if (bufsize + 3 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; bufsize += 3; } void write(char_t d0, char_t d1, char_t d2, char_t d3) { if (bufsize + 4 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; buffer[bufsize + 3] = d3; bufsize += 4; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) { if (bufsize + 5 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; buffer[bufsize + 3] = d3; buffer[bufsize + 4] = d4; bufsize += 5; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) { if (bufsize + 6 > bufcapacity) flush(); buffer[bufsize + 0] = d0; buffer[bufsize + 1] = d1; buffer[bufsize + 2] = d2; buffer[bufsize + 3] = d3; buffer[bufsize + 4] = d4; buffer[bufsize + 5] = d5; bufsize += 6; } // utf8 maximum expansion: x4 (-> utf32) // utf16 maximum expansion: x2 (-> utf32) // utf32 maximum expansion: x1 enum { bufcapacitybytes = #ifdef PUGIXML_MEMORY_OUTPUT_STACK PUGIXML_MEMORY_OUTPUT_STACK #else 10240 #endif , bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) }; char_t buffer[bufcapacity]; union { uint8_t data_u8[4 * bufcapacity]; uint16_t data_u16[2 * bufcapacity]; uint32_t data_u32[bufcapacity]; char_t data_char[bufcapacity]; } scratch; xml_writer& writer; size_t bufsize; xml_encoding encoding; }; PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) { while (*s) { const char_t* prev = s; // While *s is a usual symbol while (!PUGI__IS_CHARTYPEX(*s, type)) ++s; writer.write(prev, static_cast(s - prev)); switch (*s) { case 0: break; case '&': writer.write('&', 'a', 'm', 'p', ';'); ++s; break; case '<': writer.write('&', 'l', 't', ';'); ++s; break; case '>': writer.write('&', 'g', 't', ';'); ++s; break; case '"': writer.write('&', 'q', 'u', 'o', 't', ';'); ++s; break; default: // s is not a usual symbol { unsigned int ch = static_cast(*s++); assert(ch < 32); writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); } } } } PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { if (flags & format_no_escapes) writer.write(s); else text_output_escaped(writer, s, type); } PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) { do { writer.write('<', '!', '[', 'C', 'D'); writer.write('A', 'T', 'A', '['); const char_t* prev = s; // look for ]]> sequence - we can't output it as is since it terminates CDATA while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; // skip ]] if we stopped at ]]>, > will go to the next CDATA section if (*s) s += 2; writer.write(prev, static_cast(s - prev)); writer.write(']', ']', '>'); } while (*s); } PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute()) { writer.write(' '); writer.write(a.name()[0] ? a.name() : default_name); writer.write('=', '"'); text_output(writer, a.value(), ctx_special_attr, flags); writer.write('"'); } } PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); if ((flags & format_indent) != 0 && (flags & format_raw) == 0) for (unsigned int i = 0; i < depth; ++i) writer.write(indent); switch (node.type()) { case node_document: { for (xml_node n = node.first_child(); n; n = n.next_sibling()) node_output(writer, n, indent, flags, depth); break; } case node_element: { const char_t* name = node.name()[0] ? node.name() : default_name; writer.write('<'); writer.write(name); node_output_attributes(writer, node, flags); if (flags & format_raw) { if (!node.first_child()) writer.write(' ', '/', '>'); else { writer.write('>'); for (xml_node n = node.first_child(); n; n = n.next_sibling()) node_output(writer, n, indent, flags, depth + 1); writer.write('<', '/'); writer.write(name); writer.write('>'); } } else if (!node.first_child()) writer.write(' ', '/', '>', '\n'); else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata)) { writer.write('>'); if (node.first_child().type() == node_pcdata) text_output(writer, node.first_child().value(), ctx_special_pcdata, flags); else text_output_cdata(writer, node.first_child().value()); writer.write('<', '/'); writer.write(name); writer.write('>', '\n'); } else { writer.write('>', '\n'); for (xml_node n = node.first_child(); n; n = n.next_sibling()) node_output(writer, n, indent, flags, depth + 1); if ((flags & format_indent) != 0 && (flags & format_raw) == 0) for (unsigned int i = 0; i < depth; ++i) writer.write(indent); writer.write('<', '/'); writer.write(name); writer.write('>', '\n'); } break; } case node_pcdata: text_output(writer, node.value(), ctx_special_pcdata, flags); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_cdata: text_output_cdata(writer, node.value()); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_comment: writer.write('<', '!', '-', '-'); writer.write(node.value()); writer.write('-', '-', '>'); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_pi: case node_declaration: writer.write('<', '?'); writer.write(node.name()[0] ? node.name() : default_name); if (node.type() == node_declaration) { node_output_attributes(writer, node, flags); } else if (node.value()[0]) { writer.write(' '); writer.write(node.value()); } writer.write('?', '>'); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_doctype: writer.write('<', '!', 'D', 'O', 'C'); writer.write('T', 'Y', 'P', 'E'); if (node.value()[0]) { writer.write(' '); writer.write(node.value()); } writer.write('>'); if ((flags & format_raw) == 0) writer.write('\n'); break; default: assert(!"Invalid node type"); } } inline bool has_declaration(const xml_node& node) { for (xml_node child = node.first_child(); child; child = child.next_sibling()) { xml_node_type type = child.type(); if (type == node_declaration) return true; if (type == node_element) return false; } return false; } inline bool allow_insert_child(xml_node_type parent, xml_node_type child) { if (parent != node_document && parent != node_element) return false; if (child == node_document || child == node_null) return false; if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; return true; } PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip) { assert(dest.type() == source.type()); switch (source.type()) { case node_element: { dest.set_name(source.name()); for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) dest.append_attribute(a.name()).set_value(a.value()); for (xml_node c = source.first_child(); c; c = c.next_sibling()) { if (c == skip) continue; xml_node cc = dest.append_child(c.type()); assert(cc); recursive_copy_skip(cc, c, skip); } break; } case node_pcdata: case node_cdata: case node_comment: case node_doctype: dest.set_value(source.value()); break; case node_pi: dest.set_name(source.name()); dest.set_value(source.value()); break; case node_declaration: { dest.set_name(source.name()); for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) dest.append_attribute(a.name()).set_value(a.value()); break; } default: assert(!"Invalid node type"); } } inline bool is_text_node(xml_node_struct* node) { xml_node_type type = static_cast((node->header & impl::xml_memory_page_type_mask) + 1); return type == node_pcdata || type == node_cdata; } // get value with conversion functions PUGI__FN int get_integer_base(const char_t* value) { const char_t* s = value; while (PUGI__IS_CHARTYPE(*s, ct_space)) s++; if (*s == '-') s++; return (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; } PUGI__FN int get_value_int(const char_t* value, int def) { if (!value) return def; int base = get_integer_base(value); #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstol(value, 0, base)); #else return static_cast(strtol(value, 0, base)); #endif } PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def) { if (!value) return def; int base = get_integer_base(value); #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstoul(value, 0, base)); #else return static_cast(strtoul(value, 0, base)); #endif } PUGI__FN double get_value_double(const char_t* value, double def) { if (!value) return def; #ifdef PUGIXML_WCHAR_MODE return wcstod(value, 0); #else return strtod(value, 0); #endif } PUGI__FN float get_value_float(const char_t* value, float def) { if (!value) return def; #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstod(value, 0)); #else return static_cast(strtod(value, 0)); #endif } PUGI__FN bool get_value_bool(const char_t* value, bool def) { if (!value) return def; // only look at first char char_t first = *value; // 1*, t* (true), T* (True), y* (yes), Y* (YES) return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long get_value_llong(const char_t* value, long long def) { if (!value) return def; int base = get_integer_base(value); #ifdef PUGIXML_WCHAR_MODE #ifdef PUGI__MSVC_CRT_VERSION return _wcstoi64(value, 0, base); #else return wcstoll(value, 0, base); #endif #else #ifdef PUGI__MSVC_CRT_VERSION return _strtoi64(value, 0, base); #else return strtoll(value, 0, base); #endif #endif } PUGI__FN unsigned long long get_value_ullong(const char_t* value, unsigned long long def) { if (!value) return def; int base = get_integer_base(value); #ifdef PUGIXML_WCHAR_MODE #ifdef PUGI__MSVC_CRT_VERSION return _wcstoui64(value, 0, base); #else return wcstoull(value, 0, base); #endif #else #ifdef PUGI__MSVC_CRT_VERSION return _strtoui64(value, 0, base); #else return strtoull(value, 0, base); #endif #endif } #endif // set value with conversion functions PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128]) { #ifdef PUGIXML_WCHAR_MODE char_t wbuf[128]; impl::widen_ascii(wbuf, buf); return strcpy_insitu(dest, header, header_mask, wbuf); #else return strcpy_insitu(dest, header, header_mask, buf); #endif } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) { char buf[128]; sprintf(buf, "%d", value); return set_value_buffer(dest, header, header_mask, buf); } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value) { char buf[128]; sprintf(buf, "%u", value); return set_value_buffer(dest, header, header_mask, buf); } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value) { char buf[128]; sprintf(buf, "%g", value); return set_value_buffer(dest, header, header_mask, buf); } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value) { return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, long long value) { char buf[128]; sprintf(buf, "%lld", value); return set_value_buffer(dest, header, header_mask, buf); } PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned long long value) { char buf[128]; sprintf(buf, "%llu", value); return set_value_buffer(dest, header, header_mask, buf); } #endif // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) { #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) // there are 64-bit versions of fseek/ftell, let's use them typedef __int64 length_type; _fseeki64(file, 0, SEEK_END); length_type length = _ftelli64(file); _fseeki64(file, 0, SEEK_SET); #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__) // there are 64-bit versions of fseek/ftell, let's use them typedef off64_t length_type; fseeko64(file, 0, SEEK_END); length_type length = ftello64(file); fseeko64(file, 0, SEEK_SET); #else // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. typedef long length_type; fseek(file, 0, SEEK_END); length_type length = ftell(file); fseek(file, 0, SEEK_SET); #endif // check for I/O errors if (length < 0) return status_io_error; // check for overflow size_t result = static_cast(length); if (static_cast(result) != length) return status_out_of_memory; // finalize out_result = result; return status_ok; } PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) { // We only need to zero-terminate if encoding conversion does not do it for us #ifdef PUGIXML_WCHAR_MODE xml_encoding wchar_encoding = get_wchar_encoding(); if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) { size_t length = size / sizeof(char_t); static_cast(buffer)[length] = 0; return (length + 1) * sizeof(char_t); } #else if (encoding == encoding_utf8) { static_cast(buffer)[size] = 0; return size + 1; } #endif return size; } PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding) { if (!file) return make_parse_result(status_file_not_found); // get file size (can result in I/O errors) size_t size = 0; xml_parse_status size_status = get_file_size(file, size); if (size_status != status_ok) { fclose(file); return make_parse_result(size_status); } size_t max_suffix_size = sizeof(char_t); // allocate buffer for the whole file char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); if (!contents) { fclose(file); return make_parse_result(status_out_of_memory); } // read file in memory size_t read_size = fread(contents, 1, size, file); fclose(file); if (read_size != size) { xml_memory::deallocate(contents); return make_parse_result(status_io_error); } xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); return doc.load_buffer_inplace_own(contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding); } #ifndef PUGIXML_NO_STL template struct xml_stream_chunk { static xml_stream_chunk* create() { void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); return new (memory) xml_stream_chunk(); } static void destroy(void* ptr) { xml_stream_chunk* chunk = static_cast(ptr); // free chunk chain while (chunk) { xml_stream_chunk* next = chunk->next; xml_memory::deallocate(chunk); chunk = next; } } xml_stream_chunk(): next(0), size(0) { } xml_stream_chunk* next; size_t size; T data[xml_memory_page_size / sizeof(T)]; }; template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { buffer_holder chunks(0, xml_stream_chunk::destroy); // read file to a chunk list size_t total = 0; xml_stream_chunk* last = 0; while (!stream.eof()) { // allocate new chunk xml_stream_chunk* chunk = xml_stream_chunk::create(); if (!chunk) return status_out_of_memory; // append chunk to list if (last) last = last->next = chunk; else chunks.data = last = chunk; // read data to chunk stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); chunk->size = static_cast(stream.gcount()) * sizeof(T); // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // guard against huge files (chunk size is small enough to make this overflow check work) if (total + chunk->size < total) return status_out_of_memory; total += chunk->size; } size_t max_suffix_size = sizeof(char_t); // copy chunk list to a contiguous buffer char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); if (!buffer) return status_out_of_memory; char* write = buffer; for (xml_stream_chunk* chunk = static_cast*>(chunks.data); chunk; chunk = chunk->next) { assert(write + chunk->size <= buffer + total); memcpy(write, chunk->data, chunk->size); write += chunk->size; } assert(write == buffer + total); // return buffer *out_buffer = buffer; *out_size = total; return status_ok; } template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { // get length of remaining data in stream typename std::basic_istream::pos_type pos = stream.tellg(); stream.seekg(0, std::ios::end); std::streamoff length = stream.tellg() - pos; stream.seekg(pos); if (stream.fail() || pos < 0) return status_io_error; // guard against huge files size_t read_length = static_cast(length); if (static_cast(read_length) != length || length < 0) return status_out_of_memory; size_t max_suffix_size = sizeof(char_t); // read stream data into memory (guard against stream exceptions with buffer holder) buffer_holder buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); if (!buffer.data) return status_out_of_memory; stream.read(static_cast(buffer.data), static_cast(read_length)); // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // return buffer size_t actual_length = static_cast(stream.gcount()); assert(actual_length <= read_length); *out_buffer = buffer.release(); *out_size = actual_length * sizeof(T); return status_ok; } template PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) { void* buffer = 0; size_t size = 0; xml_parse_status status = status_ok; // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) if (stream.fail()) return make_parse_result(status_io_error); // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) if (stream.tellg() < 0) { stream.clear(); // clear error flags that could be set by a failing tellg status = load_stream_data_noseek(stream, &buffer, &size); } else status = load_stream_data_seek(stream, &buffer, &size); if (status != status_ok) return make_parse_result(status); xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); return doc.load_buffer_inplace_own(buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding); } #endif #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__)) PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { return _wfopen(path, mode); } #else PUGI__FN char* convert_path_heap(const wchar_t* str) { assert(str); // first pass: get length in utf8 characters size_t length = strlength_wide(str); size_t size = as_utf8_begin(str, length); // allocate resulting string char* result = static_cast(xml_memory::allocate(size + 1)); if (!result) return 0; // second pass: convert to utf8 as_utf8_end(result, size, str, length); return result; } PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { // there is no standard function to open wide paths, so our best bet is to try utf8 path char* path_utf8 = convert_path_heap(path); if (!path_utf8) return 0; // convert mode to ASCII (we mirror _wfopen interface) char mode_ascii[4] = {0}; for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); // try to open the utf8 path FILE* result = fopen(path_utf8, mode_ascii); // free dummy buffer xml_memory::deallocate(path_utf8); return result; } #endif PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; xml_writer_file writer(file); doc.save(writer, indent, flags, encoding); int result = ferror(file); fclose(file); return result == 0; } PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) { // check input buffer assert(contents || size == 0); // get actual encoding xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); // get private buffer char_t* buffer = 0; size_t length = 0; if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); // store buffer for offset_debug doc->buffer = buffer; // parse xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); // remember encoding res.encoding = buffer_encoding; // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself if (own || buffer != contents) *out_buffer = buffer; return res; } PUGI__NS_END namespace pugi { PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) { } PUGI__FN void xml_writer_file::write(const void* data, size_t size) { size_t result = fwrite(data, 1, size, static_cast(file)); (void)!result; // unfortunately we can't do proper error handling here } #ifndef PUGIXML_NO_STL PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) { } PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) { } PUGI__FN void xml_writer_stream::write(const void* data, size_t size) { if (narrow_stream) { assert(!wide_stream); narrow_stream->write(reinterpret_cast(data), static_cast(size)); } else { assert(wide_stream); assert(size % sizeof(wchar_t) == 0); wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); } } #endif PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) { } PUGI__FN xml_tree_walker::~xml_tree_walker() { } PUGI__FN int xml_tree_walker::depth() const { return _depth; } PUGI__FN bool xml_tree_walker::begin(xml_node&) { return true; } PUGI__FN bool xml_tree_walker::end(xml_node&) { return true; } PUGI__FN xml_attribute::xml_attribute(): _attr(0) { } PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) { } PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) { } PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const { return _attr ? unspecified_bool_xml_attribute : 0; } PUGI__FN bool xml_attribute::operator!() const { return !_attr; } PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const { return (_attr == r._attr); } PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const { return (_attr != r._attr); } PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const { return (_attr < r._attr); } PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const { return (_attr > r._attr); } PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const { return (_attr <= r._attr); } PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const { return (_attr >= r._attr); } PUGI__FN xml_attribute xml_attribute::next_attribute() const { return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_attribute::previous_attribute() const { return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); } PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const { return (_attr && _attr->value) ? _attr->value : def; } PUGI__FN int xml_attribute::as_int(int def) const { return impl::get_value_int(_attr ? _attr->value : 0, def); } PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const { return impl::get_value_uint(_attr ? _attr->value : 0, def); } PUGI__FN double xml_attribute::as_double(double def) const { return impl::get_value_double(_attr ? _attr->value : 0, def); } PUGI__FN float xml_attribute::as_float(float def) const { return impl::get_value_float(_attr ? _attr->value : 0, def); } PUGI__FN bool xml_attribute::as_bool(bool def) const { return impl::get_value_bool(_attr ? _attr->value : 0, def); } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_attribute::as_llong(long long def) const { return impl::get_value_llong(_attr ? _attr->value : 0, def); } PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const { return impl::get_value_ullong(_attr ? _attr->value : 0, def); } #endif PUGI__FN bool xml_attribute::empty() const { return !_attr; } PUGI__FN const char_t* xml_attribute::name() const { return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_attribute::value() const { return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT(""); } PUGI__FN size_t xml_attribute::hash_value() const { return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); } PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const { return _attr; } PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) { set_value(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) { set_value(rhs); return *this; } #endif PUGI__FN bool xml_attribute::set_name(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(int rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(unsigned int rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(double rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(bool rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN bool xml_attribute::set_value(long long rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } #endif #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node::xml_node(): _root(0) { } PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) { } PUGI__FN static void unspecified_bool_xml_node(xml_node***) { } PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const { return _root ? unspecified_bool_xml_node : 0; } PUGI__FN bool xml_node::operator!() const { return !_root; } PUGI__FN xml_node::iterator xml_node::begin() const { return iterator(_root ? _root->first_child : 0, _root); } PUGI__FN xml_node::iterator xml_node::end() const { return iterator(0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const { return attribute_iterator(_root ? _root->first_attribute : 0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const { return attribute_iterator(0, _root); } PUGI__FN xml_object_range xml_node::children() const { return xml_object_range(begin(), end()); } PUGI__FN xml_object_range xml_node::children(const char_t* name_) const { return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); } PUGI__FN xml_object_range xml_node::attributes() const { return xml_object_range(attributes_begin(), attributes_end()); } PUGI__FN bool xml_node::operator==(const xml_node& r) const { return (_root == r._root); } PUGI__FN bool xml_node::operator!=(const xml_node& r) const { return (_root != r._root); } PUGI__FN bool xml_node::operator<(const xml_node& r) const { return (_root < r._root); } PUGI__FN bool xml_node::operator>(const xml_node& r) const { return (_root > r._root); } PUGI__FN bool xml_node::operator<=(const xml_node& r) const { return (_root <= r._root); } PUGI__FN bool xml_node::operator>=(const xml_node& r) const { return (_root >= r._root); } PUGI__FN bool xml_node::empty() const { return !_root; } PUGI__FN const char_t* xml_node::name() const { return (_root && _root->name) ? _root->name : PUGIXML_TEXT(""); } PUGI__FN xml_node_type xml_node::type() const { return _root ? static_cast((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null; } PUGI__FN const char_t* xml_node::value() const { return (_root && _root->value) ? _root->value : PUGIXML_TEXT(""); } PUGI__FN xml_node xml_node::child(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const { if (!_root) return xml_attribute(); for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) if (i->name && impl::strequal(name_, i->name)) return xml_attribute(i); return xml_attribute(); } PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_node xml_node::next_sibling() const { if (!_root) return xml_node(); if (_root->next_sibling) return xml_node(_root->next_sibling); else return xml_node(); } PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); else return xml_node(); } PUGI__FN xml_node xml_node::parent() const { return _root ? xml_node(_root->parent) : xml_node(); } PUGI__FN xml_node xml_node::root() const { if (!_root) return xml_node(); impl::xml_memory_page* page = reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); return xml_node(static_cast(page->allocator)); } PUGI__FN xml_text xml_node::text() const { return xml_text(_root); } PUGI__FN const char_t* xml_node::child_value() const { if (!_root) return PUGIXML_TEXT(""); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->value && impl::is_text_node(i)) return i->value; return PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const { return child(name_).child_value(); } PUGI__FN xml_attribute xml_node::first_attribute() const { return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_node::last_attribute() const { return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); } PUGI__FN xml_node xml_node::first_child() const { return _root ? xml_node(_root->first_child) : xml_node(); } PUGI__FN xml_node xml_node::last_child() const { return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); } PUGI__FN bool xml_node::set_name(const char_t* rhs) { switch (type()) { case node_pi: case node_declaration: case node_element: return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs); default: return false; } } PUGI__FN bool xml_node::set_value(const char_t* rhs) { switch (type()) { case node_pi: case node_cdata: case node_pcdata: case node_comment: case node_doctype: return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs); default: return false; } } PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) { if (type() != node_element && type() != node_declaration) return xml_attribute(); xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root))); a.set_name(name_); return a; } PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) { if (type() != node_element && type() != node_declaration) return xml_attribute(); xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); if (!a) return xml_attribute(); a.set_name(name_); xml_attribute_struct* head = _root->first_attribute; if (head) { a._attr->prev_attribute_c = head->prev_attribute_c; head->prev_attribute_c = a._attr; } else a._attr->prev_attribute_c = a._attr; a._attr->next_attribute = head; _root->first_attribute = a._attr; return a; } PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) { if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); // check that attribute belongs to *this xml_attribute_struct* cur = attr._attr; while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; if (cur != _root->first_attribute) return xml_attribute(); xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); if (!a) return xml_attribute(); a.set_name(name_); if (attr._attr->prev_attribute_c->next_attribute) attr._attr->prev_attribute_c->next_attribute = a._attr; else _root->first_attribute = a._attr; a._attr->prev_attribute_c = attr._attr->prev_attribute_c; a._attr->next_attribute = attr._attr; attr._attr->prev_attribute_c = a._attr; return a; } PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) { if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); // check that attribute belongs to *this xml_attribute_struct* cur = attr._attr; while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; if (cur != _root->first_attribute) return xml_attribute(); xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root))); if (!a) return xml_attribute(); a.set_name(name_); if (attr._attr->next_attribute) attr._attr->next_attribute->prev_attribute_c = a._attr; else _root->first_attribute->prev_attribute_c = a._attr; a._attr->next_attribute = attr._attr->next_attribute; a._attr->prev_attribute_c = attr._attr; attr._attr->next_attribute = a._attr; return a; } PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); xml_attribute result = append_attribute(proto.name()); result.set_value(proto.value()); return result; } PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); xml_attribute result = prepend_attribute(proto.name()); result.set_value(proto.value()); return result; } PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); xml_attribute result = insert_attribute_after(proto.name(), attr); result.set_value(proto.value()); return result; } PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); xml_attribute result = insert_attribute_before(proto.name(), attr); result.set_value(proto.value()); return result; } PUGI__FN xml_node xml_node::append_child(xml_node_type type_) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_)); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); if (!n) return xml_node(); n._root->parent = _root; xml_node_struct* head = _root->first_child; if (head) { n._root->prev_sibling_c = head->prev_sibling_c; head->prev_sibling_c = n._root; } else n._root->prev_sibling_c = n._root; n._root->next_sibling = head; _root->first_child = n._root; if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); if (!n) return xml_node(); n._root->parent = _root; if (node._root->prev_sibling_c->next_sibling) node._root->prev_sibling_c->next_sibling = n._root; else _root->first_child = n._root; n._root->prev_sibling_c = node._root->prev_sibling_c; n._root->next_sibling = node._root; node._root->prev_sibling_c = n._root; if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(this->type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); xml_node n(impl::allocate_node(impl::get_allocator(_root), type_)); if (!n) return xml_node(); n._root->parent = _root; if (node._root->next_sibling) node._root->next_sibling->prev_sibling_c = n._root; else _root->first_child->prev_sibling_c = n._root; n._root->next_sibling = node._root->next_sibling; n._root->prev_sibling_c = node._root; node._root->next_sibling = n._root; if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::append_child(const char_t* name_) { xml_node result = append_child(node_element); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) { xml_node result = prepend_child(node_element); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) { xml_node result = insert_child_after(node_element, node); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) { xml_node result = insert_child_before(node_element, node); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) { xml_node result = append_child(proto.type()); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) { xml_node result = prepend_child(proto.type()); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) { xml_node result = insert_child_after(proto.type(), node); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) { xml_node result = insert_child_before(proto.type(), node); if (result) impl::recursive_copy_skip(result, proto, result); return result; } PUGI__FN bool xml_node::remove_attribute(const char_t* name_) { return remove_attribute(attribute(name_)); } PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) { if (!_root || !a._attr) return false; // check that attribute belongs to *this xml_attribute_struct* attr = a._attr; while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c; if (attr != _root->first_attribute) return false; if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c; else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c; if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute; else _root->first_attribute = a._attr->next_attribute; impl::destroy_attribute(a._attr, impl::get_allocator(_root)); return true; } PUGI__FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); } PUGI__FN bool xml_node::remove_child(const xml_node& n) { if (!_root || !n._root || n._root->parent != _root) return false; if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c; else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c; if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling; else _root->first_child = n._root->next_sibling; impl::destroy_node(n._root, impl::get_allocator(_root)); return true; } PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { // append_buffer is only valid for elements/documents if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); // get document node impl::xml_document_struct* doc = static_cast(root()._root); assert(doc); // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) impl::xml_memory_page* page = 0; impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); (void)page; if (!extra) return impl::make_parse_result(status_out_of_memory); // save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level char_t* rootname = _root->name; _root->name = 0; // parse char_t* buffer = 0; xml_parse_result res = impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &buffer); // restore name _root->name = rootname; // add extra buffer to the list extra->buffer = buffer; extra->next = doc->extra_buffers; doc->extra_buffers = extra; return res; } PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) { for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) return xml_node(i); } return xml_node(); } PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value : PUGIXML_TEXT(""))) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__FN string_t xml_node::path(char_t delimiter) const { xml_node cursor = *this; // Make a copy. string_t result = cursor.name(); while (cursor.parent()) { cursor = cursor.parent(); string_t temp = cursor.name(); temp += delimiter; temp += result; result.swap(temp); } return result; } #endif PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const { xml_node found = *this; // Current search context. if (!_root || !path_ || !path_[0]) return found; if (path_[0] == delimiter) { // Absolute path; e.g. '/foo/bar' found = found.root(); ++path_; } const char_t* path_segment = path_; while (*path_segment == delimiter) ++path_segment; const char_t* path_segment_end = path_segment; while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; if (path_segment == path_segment_end) return found; const char_t* next_segment = path_segment_end; while (*next_segment == delimiter) ++next_segment; if (*path_segment == '.' && path_segment + 1 == path_segment_end) return found.first_element_by_path(next_segment, delimiter); else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) return found.parent().first_element_by_path(next_segment, delimiter); else { for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) { if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) { xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); if (subsearch) return subsearch; } } return xml_node(); } } PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) { walker._depth = -1; xml_node arg_begin = *this; if (!walker.begin(arg_begin)) return false; xml_node cur = first_child(); if (cur) { ++walker._depth; do { xml_node arg_for_each = cur; if (!walker.for_each(arg_for_each)) return false; if (cur.first_child()) { ++walker._depth; cur = cur.first_child(); } else if (cur.next_sibling()) cur = cur.next_sibling(); else { // Borland C++ workaround while (!cur.next_sibling() && cur != *this && !cur.parent().empty()) { --walker._depth; cur = cur.parent(); } if (cur != *this) cur = cur.next_sibling(); } } while (cur && cur != *this); } assert(walker._depth == -1); xml_node arg_end = *this; return walker.end(arg_end); } PUGI__FN size_t xml_node::hash_value() const { return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); } PUGI__FN xml_node_struct* xml_node::internal_object() const { return _root; } PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { if (!_root) return; impl::xml_buffered_writer buffered_writer(writer, encoding); impl::node_output(buffered_writer, *this, indent, flags, depth); } #ifndef PUGIXML_NO_STL PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding, depth); } PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding_wchar, depth); } #endif PUGI__FN ptrdiff_t xml_node::offset_debug() const { xml_node_struct* r = root()._root; if (!r) return -1; const char_t* buffer = static_cast(r)->buffer; if (!buffer) return -1; switch (type()) { case node_document: return 0; case node_element: case node_declaration: case node_pi: return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer; case node_pcdata: case node_cdata: case node_comment: case node_doctype: return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer; default: return -1; } } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) { } PUGI__FN xml_node_struct* xml_text::_data() const { if (!_root || impl::is_text_node(_root)) return _root; for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) if (impl::is_text_node(node)) return node; return 0; } PUGI__FN xml_node_struct* xml_text::_data_new() { xml_node_struct* d = _data(); if (d) return d; return xml_node(_root).append_child(node_pcdata).internal_object(); } PUGI__FN xml_text::xml_text(): _root(0) { } PUGI__FN static void unspecified_bool_xml_text(xml_text***) { } PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const { return _data() ? unspecified_bool_xml_text : 0; } PUGI__FN bool xml_text::operator!() const { return !_data(); } PUGI__FN bool xml_text::empty() const { return _data() == 0; } PUGI__FN const char_t* xml_text::get() const { xml_node_struct* d = _data(); return (d && d->value) ? d->value : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_text::as_string(const char_t* def) const { xml_node_struct* d = _data(); return (d && d->value) ? d->value : def; } PUGI__FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); return impl::get_value_int(d ? d->value : 0, def); } PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const { xml_node_struct* d = _data(); return impl::get_value_uint(d ? d->value : 0, def); } PUGI__FN double xml_text::as_double(double def) const { xml_node_struct* d = _data(); return impl::get_value_double(d ? d->value : 0, def); } PUGI__FN float xml_text::as_float(float def) const { xml_node_struct* d = _data(); return impl::get_value_float(d ? d->value : 0, def); } PUGI__FN bool xml_text::as_bool(bool def) const { xml_node_struct* d = _data(); return impl::get_value_bool(d ? d->value : 0, def); } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_text::as_llong(long long def) const { xml_node_struct* d = _data(); return impl::get_value_llong(d ? d->value : 0, def); } PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const { xml_node_struct* d = _data(); return impl::get_value_ullong(d ? d->value : 0, def); } #endif PUGI__FN bool xml_text::set(const char_t* rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(unsigned int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(double rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(bool rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN bool xml_text::set(long long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(unsigned long long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } #endif PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(double rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(bool rhs) { set(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN xml_text& xml_text::operator=(long long rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) { set(rhs); return *this; } #endif PUGI__FN xml_node xml_text::data() const { return xml_node(_data()); } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_text& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node_iterator::xml_node_iterator() { } PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) { } PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI__FN xml_node& xml_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI__FN xml_node* xml_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() { assert(_wrap._root); _wrap._root = _wrap._root->next_sibling; return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) { xml_node_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() { _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) { xml_node_iterator temp = *this; --*this; return temp; } PUGI__FN xml_attribute_iterator::xml_attribute_iterator() { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const { return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; } PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const { return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; } PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const { assert(_wrap._attr); return _wrap; } PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const { assert(_wrap._attr); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() { assert(_wrap._attr); _wrap._attr = _wrap._attr->next_attribute; return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) { xml_attribute_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() { _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) { xml_attribute_iterator temp = *this; --*this; return temp; } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) { } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) { } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) { } PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI__FN xml_node& xml_named_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI__FN xml_node* xml_named_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() { assert(_wrap._root); _wrap = _wrap.next_sibling(_name); return *this; } PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) { xml_named_node_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() { if (_wrap._root) _wrap = _wrap.previous_sibling(_name); else { _wrap = _parent.last_child(); if (!impl::strequal(_wrap.name(), _name)) _wrap = _wrap.previous_sibling(_name); } return *this; } PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) { xml_named_node_iterator temp = *this; --*this; return temp; } PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) { } PUGI__FN xml_parse_result::operator bool() const { return status == status_ok; } PUGI__FN const char* xml_parse_result::description() const { switch (status) { case status_ok: return "No error"; case status_file_not_found: return "File was not found"; case status_io_error: return "Error reading from file/stream"; case status_out_of_memory: return "Could not allocate memory"; case status_internal_error: return "Internal error occurred"; case status_unrecognized_tag: return "Could not determine tag type"; case status_bad_pi: return "Error parsing document declaration/processing instruction"; case status_bad_comment: return "Error parsing comment"; case status_bad_cdata: return "Error parsing CDATA section"; case status_bad_doctype: return "Error parsing document type declaration"; case status_bad_pcdata: return "Error parsing PCDATA section"; case status_bad_start_element: return "Error parsing start element tag"; case status_bad_attribute: return "Error parsing element attribute"; case status_bad_end_element: return "Error parsing end element tag"; case status_end_element_mismatch: return "Start-end tags mismatch"; case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; case status_no_document_element: return "No document element found"; default: return "Unknown error"; } } PUGI__FN xml_document::xml_document(): _buffer(0) { create(); } PUGI__FN xml_document::~xml_document() { destroy(); } PUGI__FN void xml_document::reset() { destroy(); create(); } PUGI__FN void xml_document::reset(const xml_document& proto) { reset(); for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) append_copy(cur); } PUGI__FN void xml_document::create() { assert(!_root); // initialize sentinel page PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment <= sizeof(_memory)); // align upwards to page boundary void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1)); // prepare page structure impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory); assert(page); page->busy_size = impl::xml_memory_page_size; // allocate new root _root = new (page->data) impl::xml_document_struct(page); _root->prev_sibling_c = _root; // setup sentinel page page->allocator = static_cast(_root); } PUGI__FN void xml_document::destroy() { assert(_root); // destroy static storage if (_buffer) { impl::xml_memory::deallocate(_buffer); _buffer = 0; } // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) { if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); } // destroy dynamic storage, leave sentinel page (it's in static memory) impl::xml_memory_page* root_page = reinterpret_cast(_root->header & impl::xml_memory_page_pointer_mask); assert(root_page && !root_page->prev && !root_page->memory); for (impl::xml_memory_page* page = root_page->next; page; ) { impl::xml_memory_page* next = page->next; impl::xml_allocator::deallocate_page(page); page = next; } _root = 0; } #ifndef PUGIXML_NO_STL PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) { reset(); return impl::load_stream_impl(*this, stream, options, encoding); } PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) { reset(); return impl::load_stream_impl(*this, stream, options, encoding_wchar); } #endif PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) { // Force native encoding (skip autodetection) #ifdef PUGIXML_WCHAR_MODE xml_encoding encoding = encoding_wchar; #else xml_encoding encoding = encoding_utf8; #endif return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); } PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) { reset(); FILE* file = fopen(path_, "rb"); return impl::load_file_impl(*this, file, options, encoding); } PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) { reset(); FILE* file = impl::open_file_wide(path_, L"rb"); return impl::load_file_impl(*this, file, options, encoding); } PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); } PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); } PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); } PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const { impl::xml_buffered_writer buffered_writer(writer, encoding); if ((flags & format_write_bom) && encoding != encoding_latin1) { // BOM always represents the codepoint U+FEFF, so just write it in native encoding #ifdef PUGIXML_WCHAR_MODE unsigned int bom = 0xfeff; buffered_writer.write(static_cast(bom)); #else buffered_writer.write('\xef', '\xbb', '\xbf'); #endif } if (!(flags & format_no_declaration) && !impl::has_declaration(*this)) { buffered_writer.write(PUGIXML_TEXT("'); if (!(flags & format_raw)) buffered_writer.write('\n'); } impl::node_output(buffered_writer, *this, indent, flags, 0); } #ifndef PUGIXML_NO_STL PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding); } PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding_wchar); } #endif PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb"); return impl::save_file_impl(*this, file, indent, flags, encoding); } PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"); return impl::save_file_impl(*this, file, indent, flags, encoding); } PUGI__FN xml_node xml_document::document_element() const { assert(_root); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if ((i->header & impl::xml_memory_page_type_mask) + 1 == node_element) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) { assert(str); return impl::as_utf8_impl(str, impl::strlength_wide(str)); } PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) { return impl::as_utf8_impl(str.c_str(), str.size()); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) { assert(str); return impl::as_wide_impl(str, strlen(str)); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) { return impl::as_wide_impl(str.c_str(), str.size()); } #endif PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) { impl::xml_memory::allocate = allocate; impl::xml_memory::deallocate = deallocate; } PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() { return impl::xml_memory::allocate; } PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() { return impl::xml_memory::deallocate; } } #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } } #endif #ifndef PUGIXML_NO_XPATH // STL replacements PUGI__NS_BEGIN struct equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } }; struct not_equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs != rhs; } }; struct less { template bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; struct less_equal { template bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; } }; template void swap(T& lhs, T& rhs) { T temp = lhs; lhs = rhs; rhs = temp; } template I min_element(I begin, I end, const Pred& pred) { I result = begin; for (I it = begin + 1; it != end; ++it) if (pred(*it, *result)) result = it; return result; } template void reverse(I begin, I end) { while (end - begin > 1) swap(*begin++, *--end); } template I unique(I begin, I end) { // fast skip head while (end - begin > 1 && *begin != *(begin + 1)) begin++; if (begin == end) return begin; // last written element I write = begin++; // merge unique elements while (begin != end) { if (*begin != *write) *++write = *begin++; else begin++; } // past-the-end (write points to live element) return write + 1; } template void copy_backwards(I begin, I end, I target) { while (begin != end) *--target = *--end; } template void insertion_sort(I begin, I end, const Pred& pred, T*) { assert(begin != end); for (I it = begin + 1; it != end; ++it) { T val = *it; if (pred(val, *begin)) { // move to front copy_backwards(begin, it, it + 1); *begin = val; } else { I hole = it; // move hole backwards while (pred(val, *(hole - 1))) { *hole = *(hole - 1); hole--; } // fill hole with element *hole = val; } } } // std variant for elements with == template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) { I eqbeg = middle, eqend = middle + 1; // expand equal range while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; while (eqend != end && *eqend == *eqbeg) ++eqend; // process outer elements I ltend = eqbeg, gtbeg = eqend; for (;;) { // find the element from the right side that belongs to the left one for (; gtbeg != end; ++gtbeg) if (!pred(*eqbeg, *gtbeg)) { if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); else break; } // find the element from the left side that belongs to the right one for (; ltend != begin; --ltend) if (!pred(*(ltend - 1), *eqbeg)) { if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); else break; } // scanned all elements if (gtbeg == end && ltend == begin) { *out_eqbeg = eqbeg; *out_eqend = eqend; return; } // make room for elements by moving equal area if (gtbeg == end) { if (--ltend != --eqbeg) swap(*ltend, *eqbeg); swap(*eqbeg, *--eqend); } else if (ltend == begin) { if (eqend != gtbeg) swap(*eqbeg, *eqend); ++eqend; swap(*gtbeg++, *eqbeg++); } else swap(*gtbeg++, *--ltend); } } template void median3(I first, I middle, I last, const Pred& pred) { if (pred(*middle, *first)) swap(*middle, *first); if (pred(*last, *middle)) swap(*last, *middle); if (pred(*middle, *first)) swap(*middle, *first); } template void median(I first, I middle, I last, const Pred& pred) { if (last - first <= 40) { // median of three for small chunks median3(first, middle, last, pred); } else { // median of nine size_t step = (last - first + 1) / 8; median3(first, first + step, first + 2 * step, pred); median3(middle - step, middle, middle + step, pred); median3(last - 2 * step, last - step, last, pred); median3(first + step, middle, last - step, pred); } } template void sort(I begin, I end, const Pred& pred) { // sort large chunks while (end - begin > 32) { // find median element I middle = begin + (end - begin) / 2; median(begin, middle, end - 1, pred); // partition in three chunks (< = >) I eqbeg, eqend; partition(begin, middle, end, pred, &eqbeg, &eqend); // loop on larger half if (eqbeg - begin > end - eqend) { sort(eqend, end, pred); end = eqbeg; } else { sort(begin, eqbeg, pred); begin = eqend; } } // insertion sort small chunk if (begin != end) insertion_sort(begin, end, pred, &*begin); } PUGI__NS_END // Allocator used for AST and evaluation stacks PUGI__NS_BEGIN struct xpath_memory_block { xpath_memory_block* next; char data[ #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE PUGIXML_MEMORY_XPATH_PAGE_SIZE #else 4096 #endif ]; }; class xpath_allocator { xpath_memory_block* _root; size_t _root_size; public: #ifdef PUGIXML_NO_EXCEPTIONS jmp_buf* error_handler; #endif xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) { #ifdef PUGIXML_NO_EXCEPTIONS error_handler = 0; #endif } void* allocate_nothrow(size_t size) { const size_t block_capacity = sizeof(_root->data); // align size so that we're able to store pointers in subsequent blocks size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); if (_root_size + size <= block_capacity) { void* buf = _root->data + _root_size; _root_size += size; return buf; } else { size_t block_data_size = (size > block_capacity) ? size : block_capacity; size_t block_size = block_data_size + offsetof(xpath_memory_block, data); xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); if (!block) return 0; block->next = _root; _root = block; _root_size = size; return block->data; } } void* allocate(size_t size) { void* result = allocate_nothrow(size); if (!result) { #ifdef PUGIXML_NO_EXCEPTIONS assert(error_handler); longjmp(*error_handler, 1); #else throw std::bad_alloc(); #endif } return result; } void* reallocate(void* ptr, size_t old_size, size_t new_size) { // align size so that we're able to store pointers in subsequent blocks old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); // we can only reallocate the last object assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); // adjust root size so that we have not allocated the object at all bool only_object = (_root_size == old_size); if (ptr) _root_size -= old_size; // allocate a new version (this will obviously reuse the memory if possible) void* result = allocate(new_size); assert(result); // we have a new block if (result != ptr && ptr) { // copy old data assert(new_size >= old_size); memcpy(result, ptr, old_size); // free the previous page if it had no other objects if (only_object) { assert(_root->data == result); assert(_root->next); xpath_memory_block* next = _root->next->next; if (next) { // deallocate the whole page, unless it was the first one xml_memory::deallocate(_root->next); _root->next = next; } } } return result; } void revert(const xpath_allocator& state) { // free all new pages xpath_memory_block* cur = _root; while (cur != state._root) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } // restore state _root = state._root; _root_size = state._root_size; } void release() { xpath_memory_block* cur = _root; assert(cur); while (cur->next) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } } }; struct xpath_allocator_capture { xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) { } ~xpath_allocator_capture() { _target->revert(_state); } xpath_allocator* _target; xpath_allocator _state; }; struct xpath_stack { xpath_allocator* result; xpath_allocator* temp; }; struct xpath_stack_data { xpath_memory_block blocks[2]; xpath_allocator result; xpath_allocator temp; xpath_stack stack; #ifdef PUGIXML_NO_EXCEPTIONS jmp_buf error_handler; #endif xpath_stack_data(): result(blocks + 0), temp(blocks + 1) { blocks[0].next = blocks[1].next = 0; stack.result = &result; stack.temp = &temp; #ifdef PUGIXML_NO_EXCEPTIONS result.error_handler = temp.error_handler = &error_handler; #endif } ~xpath_stack_data() { result.release(); temp.release(); } }; PUGI__NS_END // String class PUGI__NS_BEGIN class xpath_string { const char_t* _buffer; bool _uses_heap; static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) { char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); assert(result); memcpy(result, string, length * sizeof(char_t)); result[length] = 0; return result; } static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc) { return duplicate_string(string, strlength(string), alloc); } public: xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false) { } explicit xpath_string(const char_t* str, xpath_allocator* alloc) { bool empty_ = (*str == 0); _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc); _uses_heap = !empty_; } explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap) { } xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc) { assert(begin <= end); bool empty_ = (begin == end); _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast(end - begin), alloc); _uses_heap = !empty_; } void append(const xpath_string& o, xpath_allocator* alloc) { // skip empty sources if (!*o._buffer) return; // fast append for constant empty target and constant source if (!*_buffer && !_uses_heap && !o._uses_heap) { _buffer = o._buffer; } else { // need to make heap copy size_t target_length = strlength(_buffer); size_t source_length = strlength(o._buffer); size_t result_length = target_length + source_length; // allocate new buffer char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); assert(result); // append first string to the new buffer in case there was no reallocation if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); // append second string to the new buffer memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); result[result_length] = 0; // finalize _buffer = result; _uses_heap = true; } } const char_t* c_str() const { return _buffer; } size_t length() const { return strlength(_buffer); } char_t* data(xpath_allocator* alloc) { // make private heap copy if (!_uses_heap) { _buffer = duplicate_string(_buffer, alloc); _uses_heap = true; } return const_cast(_buffer); } bool empty() const { return *_buffer == 0; } bool operator==(const xpath_string& o) const { return strequal(_buffer, o._buffer); } bool operator!=(const xpath_string& o) const { return !strequal(_buffer, o._buffer); } bool uses_heap() const { return _uses_heap; } }; PUGI__FN xpath_string xpath_string_const(const char_t* str) { return xpath_string(str, false); } PUGI__NS_END PUGI__NS_BEGIN PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) { while (*pattern && *string == *pattern) { string++; pattern++; } return *pattern == 0; } PUGI__FN const char_t* find_char(const char_t* s, char_t c) { #ifdef PUGIXML_WCHAR_MODE return wcschr(s, c); #else return strchr(s, c); #endif } PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) { #ifdef PUGIXML_WCHAR_MODE // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) return (*p == 0) ? s : wcsstr(s, p); #else return strstr(s, p); #endif } // Converts symbol to lower case, if it is an ASCII one PUGI__FN char_t tolower_ascii(char_t ch) { return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; } PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) { if (na.attribute()) return xpath_string_const(na.attribute().value()); else { const xml_node& n = na.node(); switch (n.type()) { case node_pcdata: case node_cdata: case node_comment: case node_pi: return xpath_string_const(n.value()); case node_document: case node_element: { xpath_string result; xml_node cur = n.first_child(); while (cur && cur != n) { if (cur.type() == node_pcdata || cur.type() == node_cdata) result.append(xpath_string_const(cur.value()), alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur != n) cur = cur.parent(); if (cur != n) cur = cur.next_sibling(); } } return result; } default: return xpath_string(); } } } PUGI__FN unsigned int node_height(xml_node n) { unsigned int result = 0; while (n) { ++result; n = n.parent(); } return result; } PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh) { // normalize heights for (unsigned int i = rh; i < lh; i++) ln = ln.parent(); for (unsigned int j = lh; j < rh; j++) rn = rn.parent(); // one node is the ancestor of the other if (ln == rn) return lh < rh; // find common ancestor while (ln.parent() != rn.parent()) { ln = ln.parent(); rn = rn.parent(); } // there is no common ancestor (the shared parent is null), nodes are from different documents if (!ln.parent()) return ln < rn; // determine sibling order for (; ln; ln = ln.next_sibling()) if (ln == rn) return true; return false; } PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node) { while (node && node != parent) node = node.parent(); return parent && node == parent; } PUGI__FN const void* document_order(const xpath_node& xnode) { xml_node_struct* node = xnode.node().internal_object(); if (node) { if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name; if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value; return 0; } xml_attribute_struct* attr = xnode.attribute().internal_object(); if (attr) { if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name; if ((attr->header & xml_memory_page_value_allocated_mask) == 0) return attr->value; return 0; } return 0; } struct document_order_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const { // optimized document order based check const void* lo = document_order(lhs); const void* ro = document_order(rhs); if (lo && ro) return lo < ro; // slow comparison xml_node ln = lhs.node(), rn = rhs.node(); // compare attributes if (lhs.attribute() && rhs.attribute()) { // shared parent if (lhs.parent() == rhs.parent()) { // determine sibling order for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) if (a == rhs.attribute()) return true; return false; } // compare attribute parents ln = lhs.parent(); rn = rhs.parent(); } else if (lhs.attribute()) { // attributes go after the parent element if (lhs.parent() == rhs.node()) return false; ln = lhs.parent(); } else if (rhs.attribute()) { // attributes go after the parent element if (rhs.parent() == lhs.node()) return true; rn = rhs.parent(); } if (ln == rn) return false; unsigned int lh = node_height(ln); unsigned int rh = node_height(rn); return node_is_before(ln, lh, rn, rh); } }; struct duplicate_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const { if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; else return rhs.attribute() ? false : lhs.node() < rhs.node(); } }; PUGI__FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; u[0].i = 0x7fc00000; return u[0].f; #else // fallback const volatile double zero = 0.0; return zero / zero; #endif } PUGI__FN bool is_nan(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) return !!_isnan(value); #elif defined(fpclassify) && defined(FP_NAN) return fpclassify(value) == FP_NAN; #else // fallback const volatile double v = value; return v != v; #endif } PUGI__FN const char_t* convert_number_to_string_special(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; if (_isnan(value)) return PUGIXML_TEXT("NaN"); return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) switch (fpclassify(value)) { case FP_NAN: return PUGIXML_TEXT("NaN"); case FP_INFINITE: return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); case FP_ZERO: return PUGIXML_TEXT("0"); default: return 0; } #else // fallback const volatile double v = value; if (v == 0) return PUGIXML_TEXT("0"); if (v != v) return PUGIXML_TEXT("NaN"); if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); return 0; #endif } PUGI__FN bool convert_number_to_boolean(double value) { return (value != 0 && !is_nan(value)); } PUGI__FN void truncate_zeros(char* begin, char* end) { while (begin != end && end[-1] == '0') end--; *end = 0; } // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) { // get base values int sign, exponent; _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); // truncate redundant zeros truncate_zeros(buffer, buffer + strlen(buffer)); // fill results *out_mantissa = buffer; *out_exponent = exponent; } #else PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) { // get a scientific notation value with IEEE DBL_DIG decimals sprintf(buffer, "%.*e", DBL_DIG, value); assert(strlen(buffer) < buffer_size); (void)!buffer_size; // get the exponent (possibly negative) char* exponent_string = strchr(buffer, 'e'); assert(exponent_string); int exponent = atoi(exponent_string + 1); // extract mantissa string: skip sign char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; assert(mantissa[0] != '0' && mantissa[1] == '.'); // divide mantissa by 10 to eliminate integer part mantissa[1] = mantissa[0]; mantissa++; exponent++; // remove extra mantissa digits and zero-terminate mantissa truncate_zeros(mantissa, exponent_string); // fill results *out_mantissa = mantissa; *out_exponent = exponent; } #endif PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) { // try special number conversion const char_t* special = convert_number_to_string_special(value); if (special) return xpath_string_const(special); // get mantissa + exponent form char mantissa_buffer[32]; char* mantissa; int exponent; convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); // allocate a buffer of suitable length for the number size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); assert(result); // make the number! char_t* s = result; // sign if (value < 0) *s++ = '-'; // integer part if (exponent <= 0) { *s++ = '0'; } else { while (exponent > 0) { assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9); *s++ = *mantissa ? *mantissa++ : '0'; exponent--; } } // fractional part if (*mantissa) { // decimal point *s++ = '.'; // extra zeroes from negative exponent while (exponent < 0) { *s++ = '0'; exponent++; } // extra mantissa digits while (*mantissa) { assert(static_cast(*mantissa - '0') <= 9); *s++ = *mantissa++; } } // zero-terminate assert(s < result + result_size); *s = 0; return xpath_string(result, true); } PUGI__FN bool check_string_to_number_format(const char_t* string) { // parse leading whitespace while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; // parse sign if (*string == '-') ++string; if (!*string) return false; // if there is no integer part, there should be a decimal part with at least one digit if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; // parse integer part while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; // parse decimal part if (*string == '.') { ++string; while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; } // parse trailing whitespace while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; return *string == 0; } PUGI__FN double convert_string_to_number(const char_t* string) { // check string format if (!check_string_to_number_format(string)) return gen_nan(); // parse string #ifdef PUGIXML_WCHAR_MODE return wcstod(string, 0); #else return atof(string); #endif } PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return false; } // copy string to zero-terminated buffer and perform conversion memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; *out_result = convert_string_to_number(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return true; } PUGI__FN double round_nearest(double value) { return floor(value + 0.5); } PUGI__FN double round_nearest_nzero(double value) { // same as round_nearest, but returns -0 for [-0.5, -0] // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); } PUGI__FN const char_t* qualified_name(const xpath_node& node) { return node.attribute() ? node.attribute().name() : node.node().name(); } PUGI__FN const char_t* local_name(const xpath_node& node) { const char_t* name = qualified_name(node); const char_t* p = find_char(name, ':'); return p ? p + 1 : name; } struct namespace_uri_predicate { const char_t* prefix; size_t prefix_length; namespace_uri_predicate(const char_t* name) { const char_t* pos = find_char(name, ':'); prefix = pos ? name : 0; prefix_length = pos ? static_cast(pos - name) : 0; } bool operator()(const xml_attribute& a) const { const char_t* name = a.name(); if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; } }; PUGI__FN const char_t* namespace_uri(const xml_node& node) { namespace_uri_predicate pred = node.name(); xml_node p = node; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI__FN const char_t* namespace_uri(const xml_attribute& attr, const xml_node& parent) { namespace_uri_predicate pred = attr.name(); // Default namespace does not apply to attributes if (!pred.prefix) return PUGIXML_TEXT(""); xml_node p = parent; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI__FN const char_t* namespace_uri(const xpath_node& node) { return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); } PUGI__FN void normalize_space(char_t* buffer) { char_t* write = buffer; for (char_t* it = buffer; *it; ) { char_t ch = *it++; if (PUGI__IS_CHARTYPE(ch, ct_space)) { // replace whitespace sequence with single space while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; // avoid leading spaces if (write != buffer) *write++ = ' '; } else *write++ = ch; } // remove trailing space if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; // zero-terminate *write = 0; } PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to) { size_t to_length = strlength(to); char_t* write = buffer; while (*buffer) { PUGI__DMC_VOLATILE char_t ch = *buffer++; const char_t* pos = find_char(from, ch); if (!pos) *write++ = ch; // do not process else if (static_cast(pos - from) < to_length) *write++ = to[pos - from]; // replace } // zero-terminate *write = 0; } struct xpath_variable_boolean: xpath_variable { xpath_variable_boolean(): value(false) { } bool value; char_t name[1]; }; struct xpath_variable_number: xpath_variable { xpath_variable_number(): value(0) { } double value; char_t name[1]; }; struct xpath_variable_string: xpath_variable { xpath_variable_string(): value(0) { } ~xpath_variable_string() { if (value) xml_memory::deallocate(value); } char_t* value; char_t name[1]; }; struct xpath_variable_node_set: xpath_variable { xpath_node_set value; char_t name[1]; }; static const xpath_node_set dummy_node_set; PUGI__FN unsigned int hash_string(const char_t* str) { // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) unsigned int result = 0; while (*str) { result += static_cast(*str++); result += result << 10; result ^= result >> 6; } result += result << 3; result ^= result >> 11; result += result << 15; return result; } template PUGI__FN T* new_xpath_variable(const char_t* name) { size_t length = strlength(name); if (length == 0) return 0; // empty variable names are invalid // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); if (!memory) return 0; T* result = new (memory) T(); memcpy(result->name, name, (length + 1) * sizeof(char_t)); return result; } PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) { switch (type) { case xpath_type_node_set: return new_xpath_variable(name); case xpath_type_number: return new_xpath_variable(name); case xpath_type_string: return new_xpath_variable(name); case xpath_type_boolean: return new_xpath_variable(name); default: return 0; } } template PUGI__FN void delete_xpath_variable(T* var) { var->~T(); xml_memory::deallocate(var); } PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) { switch (type) { case xpath_type_node_set: delete_xpath_variable(static_cast(var)); break; case xpath_type_number: delete_xpath_variable(static_cast(var)); break; case xpath_type_string: delete_xpath_variable(static_cast(var)); break; case xpath_type_boolean: delete_xpath_variable(static_cast(var)); break; default: assert(!"Invalid variable type"); } } PUGI__FN xpath_variable* get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end) { size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return 0; } // copy string to zero-terminated buffer and perform lookup memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; xpath_variable* result = set->get(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return result; } PUGI__NS_END // Internal node set class PUGI__NS_BEGIN PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) { xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; if (type == xpath_node_set::type_unsorted) { sort(begin, end, document_order_comparator()); type = xpath_node_set::type_sorted; } if (type != order) reverse(begin, end); return order; } PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) { if (begin == end) return xpath_node(); switch (type) { case xpath_node_set::type_sorted: return *begin; case xpath_node_set::type_sorted_reverse: return *(end - 1); case xpath_node_set::type_unsorted: return *min_element(begin, end, document_order_comparator()); default: assert(!"Invalid node set type"); return xpath_node(); } } class xpath_node_set_raw { xpath_node_set::type_t _type; xpath_node* _begin; xpath_node* _end; xpath_node* _eos; public: xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) { } xpath_node* begin() const { return _begin; } xpath_node* end() const { return _end; } bool empty() const { return _begin == _end; } size_t size() const { return static_cast(_end - _begin); } xpath_node first() const { return xpath_first(_begin, _end, _type); } void push_back(const xpath_node& node, xpath_allocator* alloc) { if (_end == _eos) { size_t capacity = static_cast(_eos - _begin); // get new capacity (1.5x rule) size_t new_capacity = capacity + capacity / 2 + 1; // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); assert(data); // finalize _begin = data; _end = data + capacity; _eos = data + new_capacity; } *_end++ = node; } void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) { size_t size_ = static_cast(_end - _begin); size_t capacity = static_cast(_eos - _begin); size_t count = static_cast(end_ - begin_); if (size_ + count > capacity) { // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); assert(data); // finalize _begin = data; _end = data + size_; _eos = data + size_ + count; } memcpy(_end, begin_, count * sizeof(xpath_node)); _end += count; } void sort_do() { _type = xpath_sort(_begin, _end, _type, false); } void truncate(xpath_node* pos) { assert(_begin <= pos && pos <= _end); _end = pos; } void remove_duplicates() { if (_type == xpath_node_set::type_unsorted) sort(_begin, _end, duplicate_comparator()); _end = unique(_begin, _end); } xpath_node_set::type_t type() const { return _type; } void set_type(xpath_node_set::type_t value) { _type = value; } }; PUGI__NS_END PUGI__NS_BEGIN struct xpath_context { xpath_node n; size_t position, size; xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) { } }; enum lexeme_t { lex_none = 0, lex_equal, lex_not_equal, lex_less, lex_greater, lex_less_or_equal, lex_greater_or_equal, lex_plus, lex_minus, lex_multiply, lex_union, lex_var_ref, lex_open_brace, lex_close_brace, lex_quoted_string, lex_number, lex_slash, lex_double_slash, lex_open_square_brace, lex_close_square_brace, lex_string, lex_comma, lex_axis_attribute, lex_dot, lex_double_dot, lex_double_colon, lex_eof }; struct xpath_lexer_string { const char_t* begin; const char_t* end; xpath_lexer_string(): begin(0), end(0) { } bool operator==(const char_t* other) const { size_t length = static_cast(end - begin); return strequalrange(other, begin, length); } }; class xpath_lexer { const char_t* _cur; const char_t* _cur_lexeme_pos; xpath_lexer_string _cur_lexeme_contents; lexeme_t _cur_lexeme; public: explicit xpath_lexer(const char_t* query): _cur(query) { next(); } const char_t* state() const { return _cur; } void next() { const char_t* cur = _cur; while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; // save lexeme position for error reporting _cur_lexeme_pos = cur; switch (*cur) { case 0: _cur_lexeme = lex_eof; break; case '>': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_greater_or_equal; } else { cur += 1; _cur_lexeme = lex_greater; } break; case '<': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_less_or_equal; } else { cur += 1; _cur_lexeme = lex_less; } break; case '!': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_not_equal; } else { _cur_lexeme = lex_none; } break; case '=': cur += 1; _cur_lexeme = lex_equal; break; case '+': cur += 1; _cur_lexeme = lex_plus; break; case '-': cur += 1; _cur_lexeme = lex_minus; break; case '*': cur += 1; _cur_lexeme = lex_multiply; break; case '|': cur += 1; _cur_lexeme = lex_union; break; case '$': cur += 1; if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname { cur++; // : while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_var_ref; } else { _cur_lexeme = lex_none; } break; case '(': cur += 1; _cur_lexeme = lex_open_brace; break; case ')': cur += 1; _cur_lexeme = lex_close_brace; break; case '[': cur += 1; _cur_lexeme = lex_open_square_brace; break; case ']': cur += 1; _cur_lexeme = lex_close_square_brace; break; case ',': cur += 1; _cur_lexeme = lex_comma; break; case '/': if (*(cur+1) == '/') { cur += 2; _cur_lexeme = lex_double_slash; } else { cur += 1; _cur_lexeme = lex_slash; } break; case '.': if (*(cur+1) == '.') { cur += 2; _cur_lexeme = lex_double_dot; } else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) { _cur_lexeme_contents.begin = cur; // . ++cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else { cur += 1; _cur_lexeme = lex_dot; } break; case '@': cur += 1; _cur_lexeme = lex_axis_attribute; break; case '"': case '\'': { char_t terminator = *cur; ++cur; _cur_lexeme_contents.begin = cur; while (*cur && *cur != terminator) cur++; _cur_lexeme_contents.end = cur; if (!*cur) _cur_lexeme = lex_none; else { cur += 1; _cur_lexeme = lex_quoted_string; } break; } case ':': if (*(cur+1) == ':') { cur += 2; _cur_lexeme = lex_double_colon; } else { _cur_lexeme = lex_none; } break; default: if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; if (*cur == '.') { cur++; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':') { if (cur[1] == '*') // namespace test ncname:* { cur += 2; // :* } else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname { cur++; // : while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_string; } else { _cur_lexeme = lex_none; } } _cur = cur; } lexeme_t current() const { return _cur_lexeme; } const char_t* current_pos() const { return _cur_lexeme_pos; } const xpath_lexer_string& contents() const { assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); return _cur_lexeme_contents; } }; enum ast_type_t { ast_unknown, ast_op_or, // left or right ast_op_and, // left and right ast_op_equal, // left = right ast_op_not_equal, // left != right ast_op_less, // left < right ast_op_greater, // left > right ast_op_less_or_equal, // left <= right ast_op_greater_or_equal, // left >= right ast_op_add, // left + right ast_op_subtract, // left - right ast_op_multiply, // left * right ast_op_divide, // left / right ast_op_mod, // left % right ast_op_negate, // left - right ast_op_union, // left | right ast_predicate, // apply predicate to set; next points to next predicate ast_filter, // select * from left where right ast_filter_posinv, // select * from left where right; proximity position invariant ast_string_constant, // string constant ast_number_constant, // number constant ast_variable, // variable ast_func_last, // last() ast_func_position, // position() ast_func_count, // count(left) ast_func_id, // id(left) ast_func_local_name_0, // local-name() ast_func_local_name_1, // local-name(left) ast_func_namespace_uri_0, // namespace-uri() ast_func_namespace_uri_1, // namespace-uri(left) ast_func_name_0, // name() ast_func_name_1, // name(left) ast_func_string_0, // string() ast_func_string_1, // string(left) ast_func_concat, // concat(left, right, siblings) ast_func_starts_with, // starts_with(left, right) ast_func_contains, // contains(left, right) ast_func_substring_before, // substring-before(left, right) ast_func_substring_after, // substring-after(left, right) ast_func_substring_2, // substring(left, right) ast_func_substring_3, // substring(left, right, third) ast_func_string_length_0, // string-length() ast_func_string_length_1, // string-length(left) ast_func_normalize_space_0, // normalize-space() ast_func_normalize_space_1, // normalize-space(left) ast_func_translate, // translate(left, right, third) ast_func_boolean, // boolean(left) ast_func_not, // not(left) ast_func_true, // true() ast_func_false, // false() ast_func_lang, // lang(left) ast_func_number_0, // number() ast_func_number_1, // number(left) ast_func_sum, // sum(left) ast_func_floor, // floor(left) ast_func_ceiling, // ceiling(left) ast_func_round, // round(left) ast_step, // process set left with step ast_step_root // select root node }; enum axis_t { axis_ancestor, axis_ancestor_or_self, axis_attribute, axis_child, axis_descendant, axis_descendant_or_self, axis_following, axis_following_sibling, axis_namespace, axis_parent, axis_preceding, axis_preceding_sibling, axis_self }; enum nodetest_t { nodetest_none, nodetest_name, nodetest_type_node, nodetest_type_comment, nodetest_type_pi, nodetest_type_text, nodetest_pi, nodetest_all, nodetest_all_in_namespace }; template struct axis_to_type { static const axis_t axis; }; template const axis_t axis_to_type::axis = N; class xpath_ast_node { private: // node type char _type; char _rettype; // for ast_step / ast_predicate char _axis; char _test; // tree node structure xpath_ast_node* _left; xpath_ast_node* _right; xpath_ast_node* _next; union { // value for ast_string_constant const char_t* string; // value for ast_number_constant double number; // variable for ast_variable xpath_variable* variable; // node test for ast_step (node name/namespace/node type/pi target) const char_t* nodetest; } _data; xpath_ast_node(const xpath_ast_node&); xpath_ast_node& operator=(const xpath_ast_node&); template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) { if (lt == xpath_type_boolean || rt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number || rt == xpath_type_number) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_string || rt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string ls = lhs->eval_string(c, stack); xpath_string rs = rhs->eval_string(c, stack); return comp(ls, rs); } } else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) return true; } return false; } else { if (lt == xpath_type_node_set) { swap(lhs, rhs); swap(lt, rt); } if (lt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string l = lhs->eval_string(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, string_value(*ri, stack.result))) return true; } return false; } } assert(!"Wrong types"); return false; } template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); double l = convert_string_to_number(string_value(*li, stack.result).c_str()); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture crii(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } } return false; } else if (lt != xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_node_set && rt != xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack); double r = rhs->eval_number(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) return true; } return false; } else { assert(!"Wrong types"); return false; } } void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) { assert(ns.size() >= first); size_t i = 1; size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; // remove_if... or well, sort of for (xpath_node* it = last; it != ns.end(); ++it, ++i) { xpath_context c(*it, i, size); if (expr->rettype() == xpath_type_number) { if (expr->eval_number(c, stack) == i) *last++ = *it; } else if (expr->eval_boolean(c, stack)) *last++ = *it; } ns.truncate(last); } void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack) { if (ns.size() == first) return; for (xpath_ast_node* pred = _right; pred; pred = pred->_next) { apply_predicate(ns, first, pred->_left, stack); } } void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc) { if (!a) return; const char_t* name = a.name(); // There are no attribute nodes corresponding to attributes that declare namespaces // That is, "xmlns:..." or "xmlns" if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return; switch (_test) { case nodetest_name: if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc); break; case nodetest_type_node: case nodetest_all: ns.push_back(xpath_node(a, parent), alloc); break; case nodetest_all_in_namespace: if (starts_with(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc); break; default: ; } } void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc) { if (!n) return; switch (_test) { case nodetest_name: if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc); break; case nodetest_type_node: ns.push_back(n, alloc); break; case nodetest_type_comment: if (n.type() == node_comment) ns.push_back(n, alloc); break; case nodetest_type_text: if (n.type() == node_pcdata || n.type() == node_cdata) ns.push_back(n, alloc); break; case nodetest_type_pi: if (n.type() == node_pi) ns.push_back(n, alloc); break; case nodetest_pi: if (n.type() == node_pi && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc); break; case nodetest_all: if (n.type() == node_element) ns.push_back(n, alloc); break; case nodetest_all_in_namespace: if (n.type() == node_element && starts_with(n.name(), _data.nodetest)) ns.push_back(n, alloc); break; default: assert(!"Unknown axis"); } } template void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T) { const axis_t axis = T::axis; switch (axis) { case axis_attribute: { for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute()) step_push(ns, a, n, alloc); break; } case axis_child: { for (xml_node c = n.first_child(); c; c = c.next_sibling()) step_push(ns, c, alloc); break; } case axis_descendant: case axis_descendant_or_self: { if (axis == axis_descendant_or_self) step_push(ns, n, alloc); xml_node cur = n.first_child(); while (cur && cur != n) { step_push(ns, cur, alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur != n) cur = cur.parent(); if (cur != n) cur = cur.next_sibling(); } } break; } case axis_following_sibling: { for (xml_node c = n.next_sibling(); c; c = c.next_sibling()) step_push(ns, c, alloc); break; } case axis_preceding_sibling: { for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling()) step_push(ns, c, alloc); break; } case axis_following: { xml_node cur = n; // exit from this node so that we don't include descendants while (cur && !cur.next_sibling()) cur = cur.parent(); cur = cur.next_sibling(); for (;;) { step_push(ns, cur, alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (cur && !cur.next_sibling()) cur = cur.parent(); cur = cur.next_sibling(); if (!cur) break; } } break; } case axis_preceding: { xml_node cur = n; while (cur && !cur.previous_sibling()) cur = cur.parent(); cur = cur.previous_sibling(); for (;;) { if (cur.last_child()) cur = cur.last_child(); else { // leaf node, can't be ancestor step_push(ns, cur, alloc); if (cur.previous_sibling()) cur = cur.previous_sibling(); else { do { cur = cur.parent(); if (!cur) break; if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc); } while (!cur.previous_sibling()); cur = cur.previous_sibling(); if (!cur) break; } } } break; } case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self) step_push(ns, n, alloc); xml_node cur = n.parent(); while (cur) { step_push(ns, cur, alloc); cur = cur.parent(); } break; } case axis_self: { step_push(ns, n, alloc); break; } case axis_parent: { if (n.parent()) step_push(ns, n.parent(), alloc); break; } default: assert(!"Unimplemented axis"); } } template void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, T v) { const axis_t axis = T::axis; switch (axis) { case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test step_push(ns, a, p, alloc); xml_node cur = p; while (cur) { step_push(ns, cur, alloc); cur = cur.parent(); } break; } case axis_descendant_or_self: case axis_self: { if (_test == nodetest_type_node) // reject attributes based on principal node type test step_push(ns, a, p, alloc); break; } case axis_following: { xml_node cur = p; for (;;) { if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (cur && !cur.next_sibling()) cur = cur.parent(); cur = cur.next_sibling(); if (!cur) break; } step_push(ns, cur, alloc); } break; } case axis_parent: { step_push(ns, p, alloc); break; } case axis_preceding: { // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding step_fill(ns, p, alloc, v); break; } default: assert(!"Unimplemented axis"); } } template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v) { const axis_t axis = T::axis; bool attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); xpath_node_set_raw ns; ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted); if (_left) { xpath_node_set_raw s = _left->eval_node_set(c, stack); // self axis preserves the original order if (axis == axis_self) ns.set_type(s.type()); for (const xpath_node* it = s.begin(); it != s.end(); ++it) { size_t size = ns.size(); // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); if (it->node()) step_fill(ns, it->node(), stack.result, v); else if (attributes) step_fill(ns, it->attribute(), it->parent(), stack.result, v); apply_predicates(ns, size, stack); } } else { if (c.n.node()) step_fill(ns, c.n.node(), stack.result, v); else if (attributes) step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v); apply_predicates(ns, 0, stack); } // child, attribute and self axes always generate unique set of nodes // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) ns.remove_duplicates(); return ns; } public: xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_string_constant); _data.string = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_number_constant); _data.number = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_variable); _data.variable = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) { } xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) { _data.nodetest = contents; } void set_next(xpath_ast_node* value) { _next = value; } void set_right(xpath_ast_node* value) { _right = value; } bool eval_boolean(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_or: return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); case ast_op_and: return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); case ast_op_equal: return compare_eq(_left, _right, c, stack, equal_to()); case ast_op_not_equal: return compare_eq(_left, _right, c, stack, not_equal_to()); case ast_op_less: return compare_rel(_left, _right, c, stack, less()); case ast_op_greater: return compare_rel(_right, _left, c, stack, less()); case ast_op_less_or_equal: return compare_rel(_left, _right, c, stack, less_equal()); case ast_op_greater_or_equal: return compare_rel(_right, _left, c, stack, less_equal()); case ast_func_starts_with: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return starts_with(lr.c_str(), rr.c_str()); } case ast_func_contains: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return find_substring(lr.c_str(), rr.c_str()) != 0; } case ast_func_boolean: return _left->eval_boolean(c, stack); case ast_func_not: return !_left->eval_boolean(c, stack); case ast_func_true: return true; case ast_func_false: return false; case ast_func_lang: { if (c.n.attribute()) return false; xpath_allocator_capture cr(stack.result); xpath_string lang = _left->eval_string(c, stack); for (xml_node n = c.n.node(); n; n = n.parent()) { xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); if (a) { const char_t* value = a.value(); // strnicmp / strncasecmp is not portable for (const char_t* lit = lang.c_str(); *lit; ++lit) { if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; ++value; } return *value == 0 || *value == '-'; } } return false; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_boolean) return _data.variable->get_boolean(); // fallthrough to type conversion } default: { switch (_rettype) { case xpath_type_number: return convert_number_to_boolean(eval_number(c, stack)); case xpath_type_string: { xpath_allocator_capture cr(stack.result); return !eval_string(c, stack).empty(); } case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); return !eval_node_set(c, stack).empty(); } default: assert(!"Wrong expression for return type boolean"); return false; } } } } double eval_number(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_add: return _left->eval_number(c, stack) + _right->eval_number(c, stack); case ast_op_subtract: return _left->eval_number(c, stack) - _right->eval_number(c, stack); case ast_op_multiply: return _left->eval_number(c, stack) * _right->eval_number(c, stack); case ast_op_divide: return _left->eval_number(c, stack) / _right->eval_number(c, stack); case ast_op_mod: return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); case ast_op_negate: return -_left->eval_number(c, stack); case ast_number_constant: return _data.number; case ast_func_last: return static_cast(c.size); case ast_func_position: return static_cast(c.position); case ast_func_count: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_node_set(c, stack).size()); } case ast_func_string_length_0: { xpath_allocator_capture cr(stack.result); return static_cast(string_value(c.n, stack.result).length()); } case ast_func_string_length_1: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_string(c, stack).length()); } case ast_func_number_0: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(string_value(c.n, stack.result).c_str()); } case ast_func_number_1: return _left->eval_number(c, stack); case ast_func_sum: { xpath_allocator_capture cr(stack.result); double r = 0; xpath_node_set_raw ns = _left->eval_node_set(c, stack); for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) { xpath_allocator_capture cri(stack.result); r += convert_string_to_number(string_value(*it, stack.result).c_str()); } return r; } case ast_func_floor: { double r = _left->eval_number(c, stack); return r == r ? floor(r) : r; } case ast_func_ceiling: { double r = _left->eval_number(c, stack); return r == r ? ceil(r) : r; } case ast_func_round: return round_nearest_nzero(_left->eval_number(c, stack)); case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_number) return _data.variable->get_number(); // fallthrough to type conversion } default: { switch (_rettype) { case xpath_type_boolean: return eval_boolean(c, stack) ? 1 : 0; case xpath_type_string: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } default: assert(!"Wrong expression for return type number"); return 0; } } } } xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) { assert(_type == ast_func_concat); xpath_allocator_capture ct(stack.temp); // count the string number size_t count = 1; for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; // gather all strings xpath_string static_buffer[4]; xpath_string* buffer = static_buffer; // allocate on-heap for large concats if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) { buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); assert(buffer); } // evaluate all strings to temporary stack xpath_stack swapped_stack = {stack.temp, stack.result}; buffer[0] = _left->eval_string(c, swapped_stack); size_t pos = 1; for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); assert(pos == count); // get total length size_t length = 0; for (size_t i = 0; i < count; ++i) length += buffer[i].length(); // create final string char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); assert(result); char_t* ri = result; for (size_t j = 0; j < count; ++j) for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) *ri++ = *bi; *ri = 0; return xpath_string(result, true); } xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_string_constant: return xpath_string_const(_data.string); case ast_func_local_name_0: { xpath_node na = c.n; return xpath_string_const(local_name(na)); } case ast_func_local_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack); xpath_node na = ns.first(); return xpath_string_const(local_name(na)); } case ast_func_name_0: { xpath_node na = c.n; return xpath_string_const(qualified_name(na)); } case ast_func_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack); xpath_node na = ns.first(); return xpath_string_const(qualified_name(na)); } case ast_func_namespace_uri_0: { xpath_node na = c.n; return xpath_string_const(namespace_uri(na)); } case ast_func_namespace_uri_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack); xpath_node na = ns.first(); return xpath_string_const(namespace_uri(na)); } case ast_func_string_0: return string_value(c.n, stack.result); case ast_func_string_1: return _left->eval_string(c, stack); case ast_func_concat: return eval_string_concat(c, stack); case ast_func_substring_before: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); return pos ? xpath_string(s.c_str(), pos, stack.result) : xpath_string(); } case ast_func_substring_after: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); if (!pos) return xpath_string(); const char_t* result = pos + p.length(); return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result); } case ast_func_substring_2: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); if (is_nan(first)) return xpath_string(); // NaN else if (first >= s_length + 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_const(rbegin); } case ast_func_substring_3: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); double last = first + round_nearest(_right->_next->eval_number(c, stack)); if (is_nan(first) || is_nan(last)) return xpath_string(); else if (first >= s_length + 1) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); assert(1 <= pos && pos <= end && end <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + (end - 1); return (end == s_length + 1 && !s.uses_heap()) ? xpath_string_const(rbegin) : xpath_string(rbegin, rend, stack.result); } case ast_func_normalize_space_0: { xpath_string s = string_value(c.n, stack.result); normalize_space(s.data(stack.result)); return s; } case ast_func_normalize_space_1: { xpath_string s = _left->eval_string(c, stack); normalize_space(s.data(stack.result)); return s; } case ast_func_translate: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, stack); xpath_string from = _right->eval_string(c, swapped_stack); xpath_string to = _right->_next->eval_string(c, swapped_stack); translate(s.data(stack.result), from.c_str(), to.c_str()); return s; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_string) return xpath_string_const(_data.variable->get_string()); // fallthrough to type conversion } default: { switch (_rettype) { case xpath_type_boolean: return xpath_string_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); case xpath_type_number: return convert_number_to_string(eval_number(c, stack), stack.result); case xpath_type_node_set: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ns = eval_node_set(c, swapped_stack); return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); } default: assert(!"Wrong expression for return type string"); return xpath_string(); } } } } xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_union: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack); xpath_node_set_raw rs = _right->eval_node_set(c, stack); // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother rs.set_type(xpath_node_set::type_unsorted); rs.append(ls.begin(), ls.end(), stack.result); rs.remove_duplicates(); return rs; } case ast_filter: case ast_filter_posinv: { xpath_node_set_raw set = _left->eval_node_set(c, stack); // either expression is a number or it contains position() call; sort by document order if (_type == ast_filter) set.sort_do(); apply_predicate(set, 0, _right, stack); return set; } case ast_func_id: return xpath_node_set_raw(); case ast_step: { switch (_axis) { case axis_ancestor: return step_do(c, stack, axis_to_type()); case axis_ancestor_or_self: return step_do(c, stack, axis_to_type()); case axis_attribute: return step_do(c, stack, axis_to_type()); case axis_child: return step_do(c, stack, axis_to_type()); case axis_descendant: return step_do(c, stack, axis_to_type()); case axis_descendant_or_self: return step_do(c, stack, axis_to_type()); case axis_following: return step_do(c, stack, axis_to_type()); case axis_following_sibling: return step_do(c, stack, axis_to_type()); case axis_namespace: // namespaced axis is not supported return xpath_node_set_raw(); case axis_parent: return step_do(c, stack, axis_to_type()); case axis_preceding: return step_do(c, stack, axis_to_type()); case axis_preceding_sibling: return step_do(c, stack, axis_to_type()); case axis_self: return step_do(c, stack, axis_to_type()); default: assert(!"Unknown axis"); return xpath_node_set_raw(); } } case ast_step_root: { assert(!_right); // root step can't have any predicates xpath_node_set_raw ns; ns.set_type(xpath_node_set::type_sorted); if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); return ns; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_node_set) { const xpath_node_set& s = _data.variable->get_node_set(); xpath_node_set_raw ns; ns.set_type(s.type()); ns.append(s.begin(), s.end(), stack.result); return ns; } // fallthrough to type conversion } default: assert(!"Wrong expression for return type node set"); return xpath_node_set_raw(); } } bool is_posinv() { switch (_type) { case ast_func_position: return false; case ast_string_constant: case ast_number_constant: case ast_variable: return true; case ast_step: case ast_step_root: return true; case ast_predicate: case ast_filter: case ast_filter_posinv: return true; default: if (_left && !_left->is_posinv()) return false; for (xpath_ast_node* n = _right; n; n = n->_next) if (!n->is_posinv()) return false; return true; } } xpath_value_type rettype() const { return static_cast(_rettype); } }; struct xpath_parser { xpath_allocator* _alloc; xpath_lexer _lexer; const char_t* _query; xpath_variable_set* _variables; xpath_parse_result* _result; char_t _scratch[32]; #ifdef PUGIXML_NO_EXCEPTIONS jmp_buf _error_handler; #endif void throw_error(const char* message) { _result->error = message; _result->offset = _lexer.current_pos() - _query; #ifdef PUGIXML_NO_EXCEPTIONS longjmp(_error_handler, 1); #else throw xpath_exception(*_result); #endif } void throw_error_oom() { #ifdef PUGIXML_NO_EXCEPTIONS throw_error("Out of memory"); #else throw std::bad_alloc(); #endif } void* alloc_node() { void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); if (!result) throw_error_oom(); return result; } const char_t* alloc_string(const xpath_lexer_string& value) { if (value.begin) { size_t length = static_cast(value.end - value.begin); char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); if (!c) throw_error_oom(); assert(c); // workaround for clang static analysis memcpy(c, value.begin, length * sizeof(char_t)); c[length] = 0; return c; } else return 0; } xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) { assert(argc <= 1); if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); } xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) { switch (name.begin[0]) { case 'b': if (name == PUGIXML_TEXT("boolean") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]); break; case 'c': if (name == PUGIXML_TEXT("count") && argc == 1) { if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); } else if (name == PUGIXML_TEXT("contains") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); else if (name == PUGIXML_TEXT("concat") && argc >= 2) return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("ceiling") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); break; case 'f': if (name == PUGIXML_TEXT("false") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); else if (name == PUGIXML_TEXT("floor") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); break; case 'i': if (name == PUGIXML_TEXT("id") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); break; case 'l': if (name == PUGIXML_TEXT("last") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); else if (name == PUGIXML_TEXT("lang") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("local-name") && argc <= 1) return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); break; case 'n': if (name == PUGIXML_TEXT("name") && argc <= 1) return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("not") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("number") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); break; case 'p': if (name == PUGIXML_TEXT("position") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); break; case 'r': if (name == PUGIXML_TEXT("round") && argc == 1) return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); break; case 's': if (name == PUGIXML_TEXT("string") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); else if (name == PUGIXML_TEXT("string-length") && argc <= 1) return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); else if (name == PUGIXML_TEXT("starts-with") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); else if (name == PUGIXML_TEXT("substring-before") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("substring-after") && argc == 2) return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("sum") && argc == 1) { if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); } break; case 't': if (name == PUGIXML_TEXT("translate") && argc == 3) return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("true") && argc == 0) return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); break; default: break; } throw_error("Unrecognized function or wrong parameter count"); return 0; } axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) { specified = true; switch (name.begin[0]) { case 'a': if (name == PUGIXML_TEXT("ancestor")) return axis_ancestor; else if (name == PUGIXML_TEXT("ancestor-or-self")) return axis_ancestor_or_self; else if (name == PUGIXML_TEXT("attribute")) return axis_attribute; break; case 'c': if (name == PUGIXML_TEXT("child")) return axis_child; break; case 'd': if (name == PUGIXML_TEXT("descendant")) return axis_descendant; else if (name == PUGIXML_TEXT("descendant-or-self")) return axis_descendant_or_self; break; case 'f': if (name == PUGIXML_TEXT("following")) return axis_following; else if (name == PUGIXML_TEXT("following-sibling")) return axis_following_sibling; break; case 'n': if (name == PUGIXML_TEXT("namespace")) return axis_namespace; break; case 'p': if (name == PUGIXML_TEXT("parent")) return axis_parent; else if (name == PUGIXML_TEXT("preceding")) return axis_preceding; else if (name == PUGIXML_TEXT("preceding-sibling")) return axis_preceding_sibling; break; case 's': if (name == PUGIXML_TEXT("self")) return axis_self; break; default: break; } specified = false; return axis_child; } nodetest_t parse_node_test_type(const xpath_lexer_string& name) { switch (name.begin[0]) { case 'c': if (name == PUGIXML_TEXT("comment")) return nodetest_type_comment; break; case 'n': if (name == PUGIXML_TEXT("node")) return nodetest_type_node; break; case 'p': if (name == PUGIXML_TEXT("processing-instruction")) return nodetest_type_pi; break; case 't': if (name == PUGIXML_TEXT("text")) return nodetest_type_text; break; default: break; } return nodetest_none; } // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall xpath_ast_node* parse_primary_expression() { switch (_lexer.current()) { case lex_var_ref: { xpath_lexer_string name = _lexer.contents(); if (!_variables) throw_error("Unknown variable: variable set is not provided"); xpath_variable* var = get_variable_scratch(_scratch, _variables, name.begin, name.end); if (!var) throw_error("Unknown variable: variable set does not contain the given name"); _lexer.next(); return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); } case lex_open_brace: { _lexer.next(); xpath_ast_node* n = parse_expression(); if (_lexer.current() != lex_close_brace) throw_error("Unmatched braces"); _lexer.next(); return n; } case lex_quoted_string: { const char_t* value = alloc_string(_lexer.contents()); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); _lexer.next(); return n; } case lex_number: { double value = 0; if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) throw_error_oom(); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); _lexer.next(); return n; } case lex_string: { xpath_ast_node* args[2] = {0}; size_t argc = 0; xpath_lexer_string function = _lexer.contents(); _lexer.next(); xpath_ast_node* last_arg = 0; if (_lexer.current() != lex_open_brace) throw_error("Unrecognized function call"); _lexer.next(); if (_lexer.current() != lex_close_brace) args[argc++] = parse_expression(); while (_lexer.current() != lex_close_brace) { if (_lexer.current() != lex_comma) throw_error("No comma between function arguments"); _lexer.next(); xpath_ast_node* n = parse_expression(); if (argc < 2) args[argc] = n; else last_arg->set_next(n); argc++; last_arg = n; } _lexer.next(); return parse_function(function, argc, args); } default: throw_error("Unrecognizable primary expression"); return 0; } } // FilterExpr ::= PrimaryExpr | FilterExpr Predicate // Predicate ::= '[' PredicateExpr ']' // PredicateExpr ::= Expr xpath_ast_node* parse_filter_expression() { xpath_ast_node* n = parse_primary_expression(); while (_lexer.current() == lex_open_square_brace) { _lexer.next(); xpath_ast_node* expr = parse_expression(); if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv(); n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); _lexer.next(); } return n; } // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep // AxisSpecifier ::= AxisName '::' | '@'? // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' // NameTest ::= '*' | NCName ':' '*' | QName // AbbreviatedStep ::= '.' | '..' xpath_ast_node* parse_step(xpath_ast_node* set) { if (set && set->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); bool axis_specified = false; axis_t axis = axis_child; // implied child axis if (_lexer.current() == lex_axis_attribute) { axis = axis_attribute; axis_specified = true; _lexer.next(); } else if (_lexer.current() == lex_dot) { _lexer.next(); return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); } else if (_lexer.current() == lex_double_dot) { _lexer.next(); return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0); } nodetest_t nt_type = nodetest_none; xpath_lexer_string nt_name; if (_lexer.current() == lex_string) { // node name test nt_name = _lexer.contents(); _lexer.next(); // was it an axis name? if (_lexer.current() == lex_double_colon) { // parse axis name if (axis_specified) throw_error("Two axis specifiers in one step"); axis = parse_axis_name(nt_name, axis_specified); if (!axis_specified) throw_error("Unknown axis"); // read actual node test _lexer.next(); if (_lexer.current() == lex_multiply) { nt_type = nodetest_all; nt_name = xpath_lexer_string(); _lexer.next(); } else if (_lexer.current() == lex_string) { nt_name = _lexer.contents(); _lexer.next(); } else throw_error("Unrecognized node test"); } if (nt_type == nodetest_none) { // node type test or processing-instruction if (_lexer.current() == lex_open_brace) { _lexer.next(); if (_lexer.current() == lex_close_brace) { _lexer.next(); nt_type = parse_node_test_type(nt_name); if (nt_type == nodetest_none) throw_error("Unrecognized node type"); nt_name = xpath_lexer_string(); } else if (nt_name == PUGIXML_TEXT("processing-instruction")) { if (_lexer.current() != lex_quoted_string) throw_error("Only literals are allowed as arguments to processing-instruction()"); nt_type = nodetest_pi; nt_name = _lexer.contents(); _lexer.next(); if (_lexer.current() != lex_close_brace) throw_error("Unmatched brace near processing-instruction()"); _lexer.next(); } else throw_error("Unmatched brace near node type test"); } // QName or NCName:* else { if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* { nt_name.end--; // erase * nt_type = nodetest_all_in_namespace; } else nt_type = nodetest_name; } } } else if (_lexer.current() == lex_multiply) { nt_type = nodetest_all; _lexer.next(); } else throw_error("Unrecognized node test"); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); xpath_ast_node* last = 0; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); xpath_ast_node* expr = parse_expression(); xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); _lexer.next(); if (last) last->set_next(pred); else n->set_right(pred); last = pred; } return n; } // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) { xpath_ast_node* n = parse_step(set); while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); if (l == lex_double_slash) n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); n = parse_step(n); } return n; } // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath xpath_ast_node* parse_location_path() { if (_lexer.current() == lex_slash) { _lexer.next(); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path lexeme_t l = _lexer.current(); if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) return parse_relative_location_path(n); else return n; } else if (_lexer.current() == lex_double_slash) { _lexer.next(); xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); return parse_relative_location_path(n); } // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 return parse_relative_location_path(0); } // PathExpr ::= LocationPath // | FilterExpr // | FilterExpr '/' RelativeLocationPath // | FilterExpr '//' RelativeLocationPath // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr // UnaryExpr ::= UnionExpr | '-' UnaryExpr xpath_ast_node* parse_path_or_unary_expression() { // Clarification. // PathExpr begins with either LocationPath or FilterExpr. // FilterExpr begins with PrimaryExpr // PrimaryExpr begins with '$' in case of it being a variable reference, // '(' in case of it being an expression, string literal, number constant or // function call. if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || _lexer.current() == lex_string) { if (_lexer.current() == lex_string) { // This is either a function call, or not - if not, we shall proceed with location path const char_t* state = _lexer.state(); while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; if (*state != '(') return parse_location_path(); // This looks like a function call; however this still can be a node-test. Check it. if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); } xpath_ast_node* n = parse_filter_expression(); if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); if (l == lex_double_slash) { if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set"); n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); } // select from location path return parse_relative_location_path(n); } return n; } else if (_lexer.current() == lex_minus) { _lexer.next(); // precedence 7+ - only parses union expressions xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7); return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); } else return parse_location_path(); } struct binary_op_t { ast_type_t asttype; xpath_value_type rettype; int precedence; binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) { } binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) { } static binary_op_t parse(xpath_lexer& lexer) { switch (lexer.current()) { case lex_string: if (lexer.contents() == PUGIXML_TEXT("or")) return binary_op_t(ast_op_or, xpath_type_boolean, 1); else if (lexer.contents() == PUGIXML_TEXT("and")) return binary_op_t(ast_op_and, xpath_type_boolean, 2); else if (lexer.contents() == PUGIXML_TEXT("div")) return binary_op_t(ast_op_divide, xpath_type_number, 6); else if (lexer.contents() == PUGIXML_TEXT("mod")) return binary_op_t(ast_op_mod, xpath_type_number, 6); else return binary_op_t(); case lex_equal: return binary_op_t(ast_op_equal, xpath_type_boolean, 3); case lex_not_equal: return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); case lex_less: return binary_op_t(ast_op_less, xpath_type_boolean, 4); case lex_greater: return binary_op_t(ast_op_greater, xpath_type_boolean, 4); case lex_less_or_equal: return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); case lex_greater_or_equal: return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); case lex_plus: return binary_op_t(ast_op_add, xpath_type_number, 5); case lex_minus: return binary_op_t(ast_op_subtract, xpath_type_number, 5); case lex_multiply: return binary_op_t(ast_op_multiply, xpath_type_number, 6); case lex_union: return binary_op_t(ast_op_union, xpath_type_node_set, 7); default: return binary_op_t(); } } }; xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) { binary_op_t op = binary_op_t::parse(_lexer); while (op.asttype != ast_unknown && op.precedence >= limit) { _lexer.next(); xpath_ast_node* rhs = parse_path_or_unary_expression(); binary_op_t nextop = binary_op_t::parse(_lexer); while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) { rhs = parse_expression_rec(rhs, nextop.precedence); nextop = binary_op_t::parse(_lexer); } if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) throw_error("Union operator has to be applied to node sets"); lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); op = binary_op_t::parse(_lexer); } return lhs; } // Expr ::= OrExpr // OrExpr ::= AndExpr | OrExpr 'or' AndExpr // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr // EqualityExpr ::= RelationalExpr // | EqualityExpr '=' RelationalExpr // | EqualityExpr '!=' RelationalExpr // RelationalExpr ::= AdditiveExpr // | RelationalExpr '<' AdditiveExpr // | RelationalExpr '>' AdditiveExpr // | RelationalExpr '<=' AdditiveExpr // | RelationalExpr '>=' AdditiveExpr // AdditiveExpr ::= MultiplicativeExpr // | AdditiveExpr '+' MultiplicativeExpr // | AdditiveExpr '-' MultiplicativeExpr // MultiplicativeExpr ::= UnaryExpr // | MultiplicativeExpr '*' UnaryExpr // | MultiplicativeExpr 'div' UnaryExpr // | MultiplicativeExpr 'mod' UnaryExpr xpath_ast_node* parse_expression() { return parse_expression_rec(parse_path_or_unary_expression(), 0); } xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) { } xpath_ast_node* parse() { xpath_ast_node* result = parse_expression(); if (_lexer.current() != lex_eof) { // there are still unparsed tokens left, error throw_error("Incorrect query"); } return result; } static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) { xpath_parser parser(query, variables, alloc, result); #ifdef PUGIXML_NO_EXCEPTIONS int error = setjmp(parser._error_handler); return (error == 0) ? parser.parse() : 0; #else return parser.parse(); #endif } }; struct xpath_query_impl { static xpath_query_impl* create() { void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); return new (memory) xpath_query_impl(); } static void destroy(void* ptr) { if (!ptr) return; // free all allocated pages static_cast(ptr)->alloc.release(); // free allocator memory (with the first page) xml_memory::deallocate(ptr); } xpath_query_impl(): root(0), alloc(&block) { block.next = 0; } xpath_ast_node* root; xpath_allocator alloc; xpath_memory_block block; }; PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) { if (!impl) return xpath_string(); #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return xpath_string(); #endif xpath_context c(n, 1, 1); return impl->root->eval_string(c, sd.stack); } PUGI__NS_END namespace pugi { #ifndef PUGIXML_NO_EXCEPTIONS PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) { assert(_result.error); } PUGI__FN const char* xpath_exception::what() const throw() { return _result.error; } PUGI__FN const xpath_parse_result& xpath_exception::result() const { return _result; } #endif PUGI__FN xpath_node::xpath_node() { } PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) { } PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) { } PUGI__FN xml_node xpath_node::node() const { return _attribute ? xml_node() : _node; } PUGI__FN xml_attribute xpath_node::attribute() const { return _attribute; } PUGI__FN xml_node xpath_node::parent() const { return _attribute ? _node : _node.parent(); } PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) { } PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const { return (_node || _attribute) ? unspecified_bool_xpath_node : 0; } PUGI__FN bool xpath_node::operator!() const { return !(_node || _attribute); } PUGI__FN bool xpath_node::operator==(const xpath_node& n) const { return _node == n._node && _attribute == n._attribute; } PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const { return _node != n._node || _attribute != n._attribute; } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_) { assert(begin_ <= end_); size_t size_ = static_cast(end_ - begin_); if (size_ <= 1) { // deallocate old buffer if (_begin != &_storage) impl::xml_memory::deallocate(_begin); // use internal buffer if (begin_ != end_) _storage = *begin_; _begin = &_storage; _end = &_storage + size_; } else { // make heap copy xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); if (!storage) { #ifdef PUGIXML_NO_EXCEPTIONS return; #else throw std::bad_alloc(); #endif } memcpy(storage, begin_, size_ * sizeof(xpath_node)); // deallocate old buffer if (_begin != &_storage) impl::xml_memory::deallocate(_begin); // finalize _begin = storage; _end = storage + size_; } } PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) { } PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage) { _assign(begin_, end_); } PUGI__FN xpath_node_set::~xpath_node_set() { if (_begin != &_storage) impl::xml_memory::deallocate(_begin); } PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) { _assign(ns._begin, ns._end); } PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) { if (this == &ns) return *this; _type = ns._type; _assign(ns._begin, ns._end); return *this; } PUGI__FN xpath_node_set::type_t xpath_node_set::type() const { return _type; } PUGI__FN size_t xpath_node_set::size() const { return _end - _begin; } PUGI__FN bool xpath_node_set::empty() const { return _begin == _end; } PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const { assert(index < size()); return _begin[index]; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const { return _begin; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const { return _end; } PUGI__FN void xpath_node_set::sort(bool reverse) { _type = impl::xpath_sort(_begin, _end, _type, reverse); } PUGI__FN xpath_node xpath_node_set::first() const { return impl::xpath_first(_begin, _end, _type); } PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) { } PUGI__FN xpath_parse_result::operator bool() const { return error == 0; } PUGI__FN const char* xpath_parse_result::description() const { return error ? error : "No error"; } PUGI__FN xpath_variable::xpath_variable(): _type(xpath_type_none), _next(0) { } PUGI__FN const char_t* xpath_variable::name() const { switch (_type) { case xpath_type_node_set: return static_cast(this)->name; case xpath_type_number: return static_cast(this)->name; case xpath_type_string: return static_cast(this)->name; case xpath_type_boolean: return static_cast(this)->name; default: assert(!"Invalid variable type"); return 0; } } PUGI__FN xpath_value_type xpath_variable::type() const { return _type; } PUGI__FN bool xpath_variable::get_boolean() const { return (_type == xpath_type_boolean) ? static_cast(this)->value : false; } PUGI__FN double xpath_variable::get_number() const { return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); } PUGI__FN const char_t* xpath_variable::get_string() const { const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; return value ? value : PUGIXML_TEXT(""); } PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const { return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; } PUGI__FN bool xpath_variable::set(bool value) { if (_type != xpath_type_boolean) return false; static_cast(this)->value = value; return true; } PUGI__FN bool xpath_variable::set(double value) { if (_type != xpath_type_number) return false; static_cast(this)->value = value; return true; } PUGI__FN bool xpath_variable::set(const char_t* value) { if (_type != xpath_type_string) return false; impl::xpath_variable_string* var = static_cast(this); // duplicate string size_t size = (impl::strlength(value) + 1) * sizeof(char_t); char_t* copy = static_cast(impl::xml_memory::allocate(size)); if (!copy) return false; memcpy(copy, value, size); // replace old string if (var->value) impl::xml_memory::deallocate(var->value); var->value = copy; return true; } PUGI__FN bool xpath_variable::set(const xpath_node_set& value) { if (_type != xpath_type_node_set) return false; static_cast(this)->value = value; return true; } PUGI__FN xpath_variable_set::xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; } PUGI__FN xpath_variable_set::~xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { xpath_variable* var = _data[i]; while (var) { xpath_variable* next = var->_next; impl::delete_xpath_variable(var->_type, var); var = next; } } } PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var; return 0; } PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var->type() == type ? var : 0; // add new variable xpath_variable* result = impl::new_xpath_variable(type, name); if (result) { result->_type = type; result->_next = _data[hash]; _data[hash] = result; } return result; } PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) { xpath_variable* var = add(name, xpath_type_boolean); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) { xpath_variable* var = add(name, xpath_type_number); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) { xpath_variable* var = add(name, xpath_type_string); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) { xpath_variable* var = add(name, xpath_type_node_set); return var ? var->set(value) : false; } PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) { return find(name); } PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { return find(name); } PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) { impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); if (!qimpl) { #ifdef PUGIXML_NO_EXCEPTIONS _result.error = "Out of memory"; #else throw std::bad_alloc(); #endif } else { impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy); qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); if (qimpl->root) { _impl = static_cast(impl_holder.release()); _result.error = 0; } } } PUGI__FN xpath_query::~xpath_query() { impl::xpath_query_impl::destroy(_impl); } PUGI__FN xpath_value_type xpath_query::return_type() const { if (!_impl) return xpath_type_none; return static_cast(_impl)->root->rettype(); } PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const { if (!_impl) return false; impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return false; #endif return static_cast(_impl)->root->eval_boolean(c, sd.stack); } PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const { if (!_impl) return impl::gen_nan(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return impl::gen_nan(); #endif return static_cast(_impl)->root->eval_number(c, sd.stack); } #ifndef PUGIXML_NO_STL PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const { impl::xpath_stack_data sd; return impl::evaluate_string_impl(static_cast(_impl), n, sd).c_str(); } #endif PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const { impl::xpath_stack_data sd; impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); size_t full_size = r.length() + 1; if (capacity > 0) { size_t size = (full_size < capacity) ? full_size : capacity; assert(size > 0); memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); buffer[size - 1] = 0; } return full_size; } PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const { if (!_impl) return xpath_node_set(); impl::xpath_ast_node* root = static_cast(_impl)->root; if (root->rettype() != xpath_type_node_set) { #ifdef PUGIXML_NO_EXCEPTIONS return xpath_node_set(); #else xpath_parse_result res; res.error = "Expression does not evaluate to node set"; throw xpath_exception(res); #endif } impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return xpath_node_set(); #endif impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack); return xpath_node_set(r.begin(), r.end(), r.type()); } PUGI__FN const xpath_parse_result& xpath_query::result() const { return _result; } PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) { } PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const { return _impl ? unspecified_bool_xpath_query : 0; } PUGI__FN bool xpath_query::operator!() const { return !_impl; } PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return select_single_node(q); } PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const { xpath_node_set s = query.evaluate_node_set(*this); return s.empty() ? xpath_node() : s.first(); } PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return select_nodes(q); } PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const { return query.evaluate_node_set(*this); } } #endif #ifdef __BORLANDC__ # pragma option pop #endif // Intel C++ does not properly keep warning state for function templates, // so popping warning state at the end of translation unit leads to warnings in the middle. #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # pragma warning(pop) #endif // Undefine all local macros (makes sure we're not leaking macros in header-only mode) #undef PUGI__NO_INLINE #undef PUGI__STATIC_ASSERT #undef PUGI__DMC_VOLATILE #undef PUGI__MSVC_CRT_VERSION #undef PUGI__NS_BEGIN #undef PUGI__NS_END #undef PUGI__FN #undef PUGI__FN_NO_INLINE #undef PUGI__IS_CHARTYPE_IMPL #undef PUGI__IS_CHARTYPE #undef PUGI__IS_CHARTYPEX #undef PUGI__SKIPWS #undef PUGI__OPTSET #undef PUGI__PUSHNODE #undef PUGI__POPNODE #undef PUGI__SCANFOR #undef PUGI__SCANWHILE #undef PUGI__ENDSEG #undef PUGI__THROW_ERROR #undef PUGI__CHECK_ERROR #endif /** * Copyright (c) 2006-2014 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ pvpgn-1.99.7.2.1+dfsg/src/common/pugixml.h000066400000000000000000001334341462640461400201250ustar00rootroot00000000000000/** * pugixml parser - version 1.4 * -------------------------------------------------------- * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at http://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef PUGIXML_VERSION // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons # define PUGIXML_VERSION 140 #endif // Include user configuration file (this can define various configuration macros) #include "pugiconfig.h" #ifndef HEADER_PUGIXML_HPP #define HEADER_PUGIXML_HPP // Include stddef.h for size_t and ptrdiff_t #include // Include exception header for XPath #if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) # include #endif // Include STL headers #ifndef PUGIXML_NO_STL # include # include # include #endif // Macro for deprecated features #ifndef PUGIXML_DEPRECATED # if defined(__GNUC__) # define PUGIXML_DEPRECATED __attribute__((deprecated)) # elif defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGIXML_DEPRECATED __declspec(deprecated) # else # define PUGIXML_DEPRECATED # endif #endif // If no API is defined, assume default #ifndef PUGIXML_API # define PUGIXML_API #endif // If no API for classes is defined, assume default #ifndef PUGIXML_CLASS # define PUGIXML_CLASS PUGIXML_API #endif // If no API for functions is defined, assume default #ifndef PUGIXML_FUNCTION # define PUGIXML_FUNCTION PUGIXML_API #endif // If the platform is known to have long long support, enable long long functions #ifndef PUGIXML_HAS_LONG_LONG # if defined(__cplusplus) && __cplusplus >= 201103 # define PUGIXML_HAS_LONG_LONG # elif defined(_MSC_VER) && _MSC_VER >= 1400 # define PUGIXML_HAS_LONG_LONG # endif #endif // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t # define PUGIXML_CHAR wchar_t #else # define PUGIXML_TEXT(t) t # define PUGIXML_CHAR char #endif namespace pugi { // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE typedef PUGIXML_CHAR char_t; #ifndef PUGIXML_NO_STL // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE typedef std::basic_string, std::allocator > string_t; #endif } // The PugiXML namespace namespace pugi { // Tree node types enum xml_node_type { node_null, // Empty (null) node handle node_document, // A document tree's absolute root node_element, // Element tag, i.e. '' node_pcdata, // Plain character data, i.e. 'text' node_cdata, // Character data, i.e. '' node_comment, // Comment tag, i.e. '' node_pi, // Processing instruction, i.e. '' node_declaration, // Document declaration, i.e. '' node_doctype // Document type declaration, i.e. '' }; // Parsing options // Minimal parsing mode (equivalent to turning all other flags off). // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. const unsigned int parse_minimal = 0x0000; // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. const unsigned int parse_pi = 0x0001; // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. const unsigned int parse_comments = 0x0002; // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. const unsigned int parse_cdata = 0x0004; // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata = 0x0008; // This flag determines if character and entity references are expanded during parsing. This flag is on by default. const unsigned int parse_escapes = 0x0010; // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. const unsigned int parse_eol = 0x0020; // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. const unsigned int parse_wconv_attribute = 0x0040; // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. const unsigned int parse_wnorm_attribute = 0x0080; // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. const unsigned int parse_declaration = 0x0100; // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. const unsigned int parse_doctype = 0x0200; // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only // of whitespace is added to the DOM tree. // This flag is off by default; turning it on may result in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata_single = 0x0400; // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. const unsigned int parse_trim_pcdata = 0x0800; // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document // is a valid document. This flag is off by default. const unsigned int parse_fragment = 0x1000; // The default parsing mode. // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; // The full parsing mode. // Nodes of all types are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; // These flags determine the encoding of input data for XML document enum xml_encoding { encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range { public: typedef It const_iterator; typedef It iterator; xml_object_range(It b, It e): _begin(b), _end(e) { } It begin() const { return _begin; } It end() const { return _end; } private: It _begin, _end; }; // Writer interface for node printing (see xml_node::print) class PUGIXML_CLASS xml_writer { public: virtual ~xml_writer() {} // Write memory chunk into stream/file/whatever virtual void write(const void* data, size_t size) = 0; }; // xml_writer implementation for FILE* class PUGIXML_CLASS xml_writer_file: public xml_writer { public: // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio xml_writer_file(void* file); virtual void write(const void* data, size_t size); private: void* file; }; #ifndef PUGIXML_NO_STL // xml_writer implementation for streams class PUGIXML_CLASS xml_writer_stream: public xml_writer { public: // Construct writer from an output stream object xml_writer_stream(std::basic_ostream >& stream); xml_writer_stream(std::basic_ostream >& stream); virtual void write(const void* data, size_t size); private: std::basic_ostream >* narrow_stream; std::basic_ostream >* wide_stream; }; #endif // A light-weight handle for manipulating attributes in DOM tree class PUGIXML_CLASS xml_attribute { friend class xml_attribute_iterator; friend class xml_node; private: xml_attribute_struct* _attr; typedef void (*unspecified_bool_type)(xml_attribute***); public: // Default constructor. Constructs an empty attribute. xml_attribute(); // Constructs attribute from internal pointer explicit xml_attribute(xml_attribute_struct* attr); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped attribute pointers) bool operator==(const xml_attribute& r) const; bool operator!=(const xml_attribute& r) const; bool operator<(const xml_attribute& r) const; bool operator>(const xml_attribute& r) const; bool operator<=(const xml_attribute& r) const; bool operator>=(const xml_attribute& r) const; // Check if attribute is empty bool empty() const; // Get attribute name/value, or "" if attribute is empty const char_t* name() const; const char_t* value() const; // Get attribute value, or the default value if attribute is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; #ifdef PUGIXML_HAS_LONG_LONG long long as_llong(long long def = 0) const; unsigned long long as_ullong(unsigned long long def = 0) const; #endif // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty bool as_bool(bool def = false) const; // Set attribute name/value (returns false if attribute is empty or there is not enough memory) bool set_name(const char_t* rhs); bool set_value(const char_t* rhs); // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set_value(int rhs); bool set_value(unsigned int rhs); bool set_value(double rhs); bool set_value(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG bool set_value(long long rhs); bool set_value(unsigned long long rhs); #endif // Set attribute value (equivalent to set_value without error checking) xml_attribute& operator=(const char_t* rhs); xml_attribute& operator=(int rhs); xml_attribute& operator=(unsigned int rhs); xml_attribute& operator=(double rhs); xml_attribute& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG xml_attribute& operator=(long long rhs); xml_attribute& operator=(unsigned long long rhs); #endif // Get next/previous attribute in the attribute list of the parent node xml_attribute next_attribute() const; xml_attribute previous_attribute() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_attribute_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); #endif // A light-weight handle for manipulating nodes in DOM tree class PUGIXML_CLASS xml_node { friend class xml_attribute_iterator; friend class xml_node_iterator; friend class xml_named_node_iterator; protected: xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_node***); public: // Default constructor. Constructs an empty node. xml_node(); // Constructs node from internal pointer explicit xml_node(xml_node_struct* p); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped node pointers) bool operator==(const xml_node& r) const; bool operator!=(const xml_node& r) const; bool operator<(const xml_node& r) const; bool operator>(const xml_node& r) const; bool operator<=(const xml_node& r) const; bool operator>=(const xml_node& r) const; // Check if node is empty. bool empty() const; // Get node type xml_node_type type() const; // Get node name, or "" if node is empty or it has no name const char_t* name() const; // Get node value, or "" if node is empty or it has no value // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. const char_t* value() const; // Get attribute list xml_attribute first_attribute() const; xml_attribute last_attribute() const; // Get children list xml_node first_child() const; xml_node last_child() const; // Get next/previous sibling in the children list of the parent node xml_node next_sibling() const; xml_node previous_sibling() const; // Get parent node xml_node parent() const; // Get root of DOM tree this node belongs to xml_node root() const; // Get text object for the current node xml_text text() const; // Get child, attribute or next/previous sibling with the specified name xml_node child(const char_t* name) const; xml_attribute attribute(const char_t* name) const; xml_node next_sibling(const char_t* name) const; xml_node previous_sibling(const char_t* name) const; // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA const char_t* child_value() const; // Get child value of child with specified name. Equivalent to child(name).child_value(). const char_t* child_value(const char_t* name) const; // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) bool set_name(const char_t* rhs); bool set_value(const char_t* rhs); // Add attribute with specified name. Returns added attribute, or empty attribute on errors. xml_attribute append_attribute(const char_t* name); xml_attribute prepend_attribute(const char_t* name); xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. xml_attribute append_copy(const xml_attribute& proto); xml_attribute prepend_copy(const xml_attribute& proto); xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); // Add child node with specified type. Returns added node, or empty node on errors. xml_node append_child(xml_node_type type = node_element); xml_node prepend_child(xml_node_type type = node_element); xml_node insert_child_after(xml_node_type type, const xml_node& node); xml_node insert_child_before(xml_node_type type, const xml_node& node); // Add child element with specified name. Returns added node, or empty node on errors. xml_node append_child(const char_t* name); xml_node prepend_child(const char_t* name); xml_node insert_child_after(const char_t* name, const xml_node& node); xml_node insert_child_before(const char_t* name, const xml_node& node); // Add a copy of the specified node as a child. Returns added node, or empty node on errors. xml_node append_copy(const xml_node& proto); xml_node prepend_copy(const xml_node& proto); xml_node insert_copy_after(const xml_node& proto, const xml_node& node); xml_node insert_copy_before(const xml_node& proto, const xml_node& node); // Remove specified attribute bool remove_attribute(const xml_attribute& a); bool remove_attribute(const char_t* name); // Remove specified child bool remove_child(const xml_node& n); bool remove_child(const char_t* name); // Parses buffer as an XML document fragment and appends all nodes as children of the current node. // Copies/converts the buffer, so it may be deleted or changed after the function returns. // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Find attribute using predicate. Returns first attribute for which predicate returned true. template xml_attribute find_attribute(Predicate pred) const { if (!_root) return xml_attribute(); for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) if (pred(attrib)) return attrib; return xml_attribute(); } // Find child node using predicate. Returns first child for which predicate returned true. template xml_node find_child(Predicate pred) const { if (!_root) return xml_node(); for (xml_node node = first_child(); node; node = node.next_sibling()) if (pred(node)) return node; return xml_node(); } // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. template xml_node find_node(Predicate pred) const { if (!_root) return xml_node(); xml_node cur = first_child(); while (cur._root && cur._root != _root) { if (pred(cur)) return cur; if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); if (cur._root != _root) cur = cur.next_sibling(); } } return xml_node(); } // Find child node by attribute name/value xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; #ifndef PUGIXML_NO_STL // Get the absolute node path from root as a text string. string_t path(char_t delimiter = '/') const; #endif // Search for a node by path consisting of node names and . or .. elements. xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; // Recursively traverse subtree with xml_tree_walker bool traverse(xml_tree_walker& walker); #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; xpath_node select_single_node(const xpath_query& query) const; // Select node set by evaluating XPath query xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; xpath_node_set select_nodes(const xpath_query& query) const; #endif // Print subtree using a writer object void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; #ifndef PUGIXML_NO_STL // Print subtree to stream void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; #endif // Child nodes iterators typedef xml_node_iterator iterator; iterator begin() const; iterator end() const; // Attribute iterators typedef xml_attribute_iterator attribute_iterator; attribute_iterator attributes_begin() const; attribute_iterator attributes_end() const; // Range-based for support xml_object_range children() const; xml_object_range children(const char_t* name) const; xml_object_range attributes() const; // Get node offset in parsed file/string (in char_t units) for debugging purposes ptrdiff_t offset_debug() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_node_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); #endif // A helper for working with text inside PCDATA nodes class PUGIXML_CLASS xml_text { friend class xml_node; xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_text***); explicit xml_text(xml_node_struct* root); xml_node_struct* _data_new(); xml_node_struct* _data() const; public: // Default constructor. Constructs an empty object. xml_text(); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Check if text object is empty bool empty() const; // Get text, or "" if object is empty const char_t* get() const; // Get text, or the default value if object is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get text as a number, or the default value if conversion did not succeed or object is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; #ifdef PUGIXML_HAS_LONG_LONG long long as_llong(long long def = 0) const; unsigned long long as_ullong(unsigned long long def = 0) const; #endif // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty bool as_bool(bool def = false) const; // Set text (returns false if object is empty or there is not enough memory) bool set(const char_t* rhs); // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set(int rhs); bool set(unsigned int rhs); bool set(double rhs); bool set(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG bool set(long long rhs); bool set(unsigned long long rhs); #endif // Set text (equivalent to set without error checking) xml_text& operator=(const char_t* rhs); xml_text& operator=(int rhs); xml_text& operator=(unsigned int rhs); xml_text& operator=(double rhs); xml_text& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG xml_text& operator=(long long rhs); xml_text& operator=(unsigned long long rhs); #endif // Get the data node (node_pcdata or node_cdata) for this object xml_node data() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); #endif // Child node iterator (a bidirectional iterator over a collection of xml_node) class PUGIXML_CLASS xml_node_iterator { friend class xml_node; private: mutable xml_node _wrap; xml_node _parent; xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_node_iterator(); // Construct an iterator which points to the specified node xml_node_iterator(const xml_node& node); // Iterator operators bool operator==(const xml_node_iterator& rhs) const; bool operator!=(const xml_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; const xml_node_iterator& operator++(); xml_node_iterator operator++(int); const xml_node_iterator& operator--(); xml_node_iterator operator--(int); }; // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) class PUGIXML_CLASS xml_attribute_iterator { friend class xml_node; private: mutable xml_attribute _wrap; xml_node _parent; xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_attribute value_type; typedef xml_attribute* pointer; typedef xml_attribute& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_attribute_iterator(); // Construct an iterator which points to the specified attribute xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); // Iterator operators bool operator==(const xml_attribute_iterator& rhs) const; bool operator!=(const xml_attribute_iterator& rhs) const; xml_attribute& operator*() const; xml_attribute* operator->() const; const xml_attribute_iterator& operator++(); xml_attribute_iterator operator++(int); const xml_attribute_iterator& operator--(); xml_attribute_iterator operator--(int); }; // Named node range helper class PUGIXML_CLASS xml_named_node_iterator { friend class xml_node; public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_named_node_iterator(); // Construct an iterator which points to the specified node xml_named_node_iterator(const xml_node& node, const char_t* name); // Iterator operators bool operator==(const xml_named_node_iterator& rhs) const; bool operator!=(const xml_named_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; const xml_named_node_iterator& operator++(); xml_named_node_iterator operator++(int); const xml_named_node_iterator& operator--(); xml_named_node_iterator operator--(int); private: mutable xml_node _wrap; xml_node _parent; const char_t* _name; xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); }; // Abstract tree walker class (see xml_node::traverse) class PUGIXML_CLASS xml_tree_walker { friend class xml_node; private: int _depth; protected: // Get current traversal depth int depth() const; public: xml_tree_walker(); virtual ~xml_tree_walker(); // Callback that is called when traversal begins virtual bool begin(xml_node& node); // Callback that is called for each node traversed virtual bool for_each(xml_node& node) = 0; // Callback that is called when traversal ends virtual bool end(xml_node& node); }; // Parsing status, returned as part of xml_parse_result object enum xml_parse_status { status_ok = 0, // No error status_file_not_found, // File was not found during load_file() status_io_error, // Error reading from file/stream status_out_of_memory, // Could not allocate memory status_internal_error, // Internal error occurred status_unrecognized_tag, // Parser could not determine tag type status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction status_bad_comment, // Parsing error occurred while parsing comment status_bad_cdata, // Parsing error occurred while parsing CDATA section status_bad_doctype, // Parsing error occurred while parsing document type declaration status_bad_pcdata, // Parsing error occurred while parsing PCDATA section status_bad_start_element, // Parsing error occurred while parsing start element tag status_bad_attribute, // Parsing error occurred while parsing element attribute status_bad_end_element, // Parsing error occurred while parsing end element tag status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) status_no_document_element // Parsing resulted in a document without element nodes }; // Parsing result struct PUGIXML_CLASS xml_parse_result { // Parsing status (see xml_parse_status) xml_parse_status status; // Last parsed offset (in char_t units from start of input data) ptrdiff_t offset; // Source document encoding xml_encoding encoding; // Default constructor, initializes object to failed state xml_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // Document class (DOM tree root) class PUGIXML_CLASS xml_document: public xml_node { private: char_t* _buffer; char _memory[192]; // Non-copyable semantics xml_document(const xml_document&); const xml_document& operator=(const xml_document&); void create(); void destroy(); public: // Default constructor, makes empty document xml_document(); // Destructor, invalidates all node/attribute handles to this document ~xml_document(); // Removes all nodes, leaving the empty document void reset(); // Removes all nodes, then copies the entire contents of the specified document void reset(const xml_document& proto); #ifndef PUGIXML_NO_STL // Load document from stream. xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); #endif // Load document from zero-terminated string. No encoding conversions are applied. xml_parse_result load(const char_t* contents, unsigned int options = parse_default); // Load document from file xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; #ifndef PUGIXML_NO_STL // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; #endif // Save XML to file bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; // Get document element xml_node document_element() const; }; #ifndef PUGIXML_NO_XPATH // XPath query return type enum xpath_value_type { xpath_type_none, // Unknown type (query failed to compile) xpath_type_node_set, // Node set (xpath_node_set) xpath_type_number, // Number xpath_type_string, // String xpath_type_boolean // Boolean }; // XPath parsing result struct PUGIXML_CLASS xpath_parse_result { // Error message (0 if no error) const char* error; // Last parsed offset (in char_t units from string start) ptrdiff_t offset; // Default constructor, initializes object to failed state xpath_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // A single XPath variable class PUGIXML_CLASS xpath_variable { friend class xpath_variable_set; protected: xpath_value_type _type; xpath_variable* _next; xpath_variable(); // Non-copyable semantics xpath_variable(const xpath_variable&); xpath_variable& operator=(const xpath_variable&); public: // Get variable name const char_t* name() const; // Get variable type xpath_value_type type() const; // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error bool get_boolean() const; double get_number() const; const char_t* get_string() const; const xpath_node_set& get_node_set() const; // Set variable value; no type conversion is performed, false is returned on type mismatch error bool set(bool value); bool set(double value); bool set(const char_t* value); bool set(const xpath_node_set& value); }; // A set of XPath variables class PUGIXML_CLASS xpath_variable_set { private: xpath_variable* _data[64]; // Non-copyable semantics xpath_variable_set(const xpath_variable_set&); xpath_variable_set& operator=(const xpath_variable_set&); xpath_variable* find(const char_t* name) const; public: // Default constructor/destructor xpath_variable_set(); ~xpath_variable_set(); // Add a new variable or get the existing one, if the types match xpath_variable* add(const char_t* name, xpath_value_type type); // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch bool set(const char_t* name, bool value); bool set(const char_t* name, double value); bool set(const char_t* name, const char_t* value); bool set(const char_t* name, const xpath_node_set& value); // Get existing variable by name xpath_variable* get(const char_t* name); const xpath_variable* get(const char_t* name) const; }; // A compiled XPath query object class PUGIXML_CLASS xpath_query { private: void* _impl; xpath_parse_result _result; typedef void (*unspecified_bool_type)(xpath_query***); // Non-copyable semantics xpath_query(const xpath_query&); xpath_query& operator=(const xpath_query&); public: // Construct a compiled object from XPath expression. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); // Destructor ~xpath_query(); // Get query expression return type xpath_value_type return_type() const; // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. bool evaluate_boolean(const xpath_node& n) const; // Evaluate expression as double value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. double evaluate_number(const xpath_node& n) const; #ifndef PUGIXML_NO_STL // Evaluate expression as string value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. string_t evaluate_string(const xpath_node& n) const; #endif // Evaluate expression as string value in the specified context; performs type conversion if necessary. // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; // Evaluate expression as node set in the specified context. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. xpath_node_set evaluate_node_set(const xpath_node& n) const; // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) const xpath_parse_result& result() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; }; #ifndef PUGIXML_NO_EXCEPTIONS // XPath exception class class PUGIXML_CLASS xpath_exception: public std::exception { private: xpath_parse_result _result; public: // Construct exception from parse result explicit xpath_exception(const xpath_parse_result& result); // Get error message virtual const char* what() const throw(); // Get parse result const xpath_parse_result& result() const; }; #endif // XPath node class (either xml_node or xml_attribute) class PUGIXML_CLASS xpath_node { private: xml_node _node; xml_attribute _attribute; typedef void (*unspecified_bool_type)(xpath_node***); public: // Default constructor; constructs empty XPath node xpath_node(); // Construct XPath node from XML node/attribute xpath_node(const xml_node& node); xpath_node(const xml_attribute& attribute, const xml_node& parent); // Get node/attribute, if any xml_node node() const; xml_attribute attribute() const; // Get parent of contained node/attribute xml_node parent() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators bool operator==(const xpath_node& n) const; bool operator!=(const xpath_node& n) const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); #endif // A fixed-size collection of XPath nodes class PUGIXML_CLASS xpath_node_set { public: // Collection type enum type_t { type_unsorted, // Not ordered type_sorted, // Sorted by document order (ascending) type_sorted_reverse // Sorted by document order (descending) }; // Constant iterator type typedef const xpath_node* const_iterator; // Default constructor. Constructs empty set. xpath_node_set(); // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); // Destructor ~xpath_node_set(); // Copy constructor/assignment operator xpath_node_set(const xpath_node_set& ns); xpath_node_set& operator=(const xpath_node_set& ns); // Get collection type type_t type() const; // Get collection size size_t size() const; // Indexing operator const xpath_node& operator[](size_t index) const; // Collection iterators const_iterator begin() const; const_iterator end() const; // Sort the collection in ascending/descending order by document order void sort(bool reverse = false); // Get first node in the collection by document order xpath_node first() const; // Check if collection is empty bool empty() const; private: type_t _type; xpath_node _storage; xpath_node* _begin; xpath_node* _end; void _assign(const_iterator begin, const_iterator end); }; #endif #ifndef PUGIXML_NO_STL // Convert wide string to UTF8 std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); // Convert UTF8 to wide string std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); #endif // Memory allocation function interface; returns pointer to allocated memory or NULL on failure typedef void* (*allocation_function)(size_t size); // Memory deallocation function interface typedef void (*deallocation_function)(void* ptr); // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); // Get current memory management functions allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); } #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); } #endif #endif /** * Copyright (c) 2006-2014 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ pvpgn-1.99.7.2.1+dfsg/src/common/queue.cpp000066400000000000000000000125311462640461400201110ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #define QUEUE_INTERNAL_ACCESS #include "common/queue.h" #include #include "common/packet.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/setup_after.h" #define QUEUE_QUANTUM 10 /* allocate ring buffer slots for 10 packets at once */ namespace pvpgn { extern t_packet * queue_pull_packet(t_queue * * queue) { t_queue * temp; t_packet * packet; // eventlog(eventlog_level_debug, __FUNCTION__, "entered: queue {:p}", queue); if (!queue) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL queue pointer"); return NULL; } temp = *queue; if (!temp || !temp->ulen) return NULL; // eventlog(eventlog_level_debug, __FUNCTION__, "getting element from tail ({}/{} head/tail {}/{})", temp->alen, temp->ulen, temp->head, temp->tail); /* getting entry from tail and updating queue */ packet = temp->ring[temp->tail]; temp->tail = (temp->tail + 1) % temp->alen; temp->ulen--; // eventlog(eventlog_level_debug, __FUNCTION__, "read {:p} element from tail ({}/{} head/tail {}/{})", packet, temp->alen, temp->ulen, temp->head, temp->tail); if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "NULL packet in queue"); return NULL; } return packet; } extern t_packet * queue_peek_packet(t_queue const * const * queue) { t_packet * packet; // eventlog(eventlog_level_debug, __FUNCTION__, "entered: queue {:p}", queue); if (!queue) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL queue pointer"); return NULL; } if (!*queue || !(*queue)->ulen) return NULL; packet = (*queue)->ring[(*queue)->tail]; if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "NULL packet in queue"); return NULL; } return packet; } extern void queue_push_packet(t_queue * * queue, t_packet * packet) { t_queue * temp; void *ptr; // eventlog(eventlog_level_debug, __FUNCTION__, "entered: queue {:p} packet {:p}", queue, packet); if (!queue) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL queue pointer"); return; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return; } temp = *queue; if (!temp) { // eventlog(eventlog_level_debug, __FUNCTION__, "queue is NULL , initilizing"); temp = (t_queue*)xmalloc(sizeof(t_queue)); temp->alen = temp->ulen = 0; temp->ring = NULL; temp->head = temp->tail = 0; *queue = temp; } if (temp->ulen == temp->alen) { /* ring queue is full, need to allocate some memory */ /* FIXME: find a solution if (temp->alen) eventlog(eventlog_level_error, __FUNCTION__, "queue is full (resizing) (oldsize: {})", temp->alen); */ ptr = xrealloc(temp->ring, sizeof(t_packet *)* (temp->alen + QUEUE_QUANTUM)); temp->ring = (t_packet **)ptr; temp->alen += QUEUE_QUANTUM; // eventlog(eventlog_level_debug, __FUNCTION__, "queue new size {}/{} head/tail {}/{}", temp->alen, temp->ulen, temp->head, temp->tail); if (temp->head) { unsigned moved; moved = (QUEUE_QUANTUM <= temp->head) ? QUEUE_QUANTUM : temp->head; std::memmove(temp->ring + temp->ulen, temp->ring, sizeof(t_packet *)* moved); if (temp->head > QUEUE_QUANTUM) { std::memmove(temp->ring, temp->ring + moved, sizeof(t_packet *)* (temp->head - moved)); temp->head -= moved; } else if (temp->head < QUEUE_QUANTUM) temp->head = temp->ulen + moved; else temp->head = 0; } else temp->head = temp->ulen; } temp->ring[temp->head] = packet_add_ref(packet); temp->head = (temp->head + 1) % temp->alen; temp->ulen++; // eventlog(eventlog_level_debug, __FUNCTION__, "packet added ({}/{} head/tail {}/{})", temp->alen, temp->ulen, temp->head, temp->tail); } extern int queue_get_length(t_queue const * const * queue) { // eventlog(eventlog_level_debug, __FUNCTION__, "entered: queue {:p}", queue); if (!queue) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL queue pointer"); return 0; } if (*queue == NULL) return 0; return (*queue)->ulen; } extern void queue_clear(t_queue * * queue) { t_packet * temp; // eventlog(eventlog_level_debug, __FUNCTION__, "entered: queue {:p}", queue); if (!queue) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL queue pointer"); return; } if (*queue) { while ((temp = queue_pull_packet(queue))) packet_del_ref(temp); if ((*queue)->ring) xfree((void*)((*queue)->ring)); xfree((void*)(*queue)); /* poison the queue, this should make invalid * accessed queues crash earlier */ *queue = 0; } } } pvpgn-1.99.7.2.1+dfsg/src/common/queue.h000066400000000000000000000032451462640461400175600ustar00rootroot00000000000000/* * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_QUEUE_TYPES #define INCLUDED_QUEUE_TYPES #ifdef QUEUE_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES # include "common/packet.h" #else # define JUST_NEED_TYPES # include "common/packet.h" # undef JUST_NEED_TYPES #endif #endif namespace pvpgn { typedef struct queue #ifdef QUEUE_INTERNAL_ACCESS { unsigned ulen, alen; t_packet ** ring; unsigned head, tail; } #endif t_queue; } #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_QUEUE_PROTOS #define INCLUDED_QUEUE_PROTOS #define JUST_NEED_TYPES #include "common/packet.h" #undef JUST_NEED_TYPES namespace pvpgn { extern t_packet * queue_pull_packet(t_queue * * queue); extern t_packet * queue_peek_packet(t_queue const * const * queue); extern void queue_push_packet(t_queue * * queue, t_packet * packet); extern int queue_get_length(t_queue const * const * queue); extern void queue_clear(t_queue * * queue); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/rcm.cpp000066400000000000000000000035471462640461400175550ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/rcm.h" #include #include "common/xalloc.h" #include "common/elist.h" #include "common/setup_after.h" namespace pvpgn { extern void rcm_init(t_rcm *rcm) { assert(rcm); elist_init(&rcm->refs); rcm->count = 0; } extern void rcm_regref_init(t_rcm_regref *regref, t_chref_cb cb, void *data) { assert(regref); elist_init(®ref->refs_link); regref->chref = cb; regref->data = data; } extern void rcm_get(t_rcm *rcm, t_rcm_regref *regref) { assert(rcm); assert(regref); rcm->count++; elist_add_tail(&rcm->refs, ®ref->refs_link); } extern void rcm_put(t_rcm *rcm, t_rcm_regref *regref) { assert(rcm); assert(regref); assert(rcm->count); /* might use eventlog but I want this stopped fast */ rcm->count--; elist_del(®ref->refs_link); } extern void rcm_chref(t_rcm *rcm, void *newref) { t_elist *curr, *save; t_rcm_regref *regref; assert(rcm); elist_for_each_safe(curr, &rcm->refs, save) { regref = elist_entry(curr, t_rcm_regref, refs_link); if (regref->chref) regref->chref(regref->data, newref); } } } pvpgn-1.99.7.2.1+dfsg/src/common/rcm.h000066400000000000000000000042041462640461400172110ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Reference Change Mechanism * an abstract interface to implement safe management of references of objects * which do not share any specific dependencies */ #ifndef __RCM_H_TYPES__ #define __RCM_H_TYPES__ #include "common/elist.h" namespace pvpgn { /* reference change mechanism main object * an object which wishes to be referenced using RCM has to include this */ typedef struct { unsigned count; t_elist refs; /* list of registered references */ } t_rcm; /* callback called when the object referenced is moved/deleted */ typedef int(*t_chref_cb)(void *data, void *newref); /* registered reference object * an object which wants to register it's references to an rcm enabled object * will have to include one of this for every rcm enabled referenced object */ typedef struct { t_chref_cb chref; void *data; t_elist refs_link; } t_rcm_regref; } #endif /* __RCM_H_TYPES__ */ #ifndef __RCM_H_PROTOS__ #define __RCM_H_PROTOS__ namespace pvpgn { extern void rcm_init(t_rcm *rcm); extern void rcm_regref_init(t_rcm_regref *regref, t_chref_cb cb, void *data); extern void rcm_get(t_rcm *rcm, t_rcm_regref *regref); extern void rcm_put(t_rcm *rcm, t_rcm_regref *regref); /* the main function, cycles through the registered references and calls the * registered callback with the new reference */ extern void rcm_chref(t_rcm *rcm, void *newref); } #endif /* __RCM_H_PROTOS__ */ pvpgn-1.99.7.2.1+dfsg/src/common/rlimit.cpp000066400000000000000000000035741462640461400202740ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "rlimit.h" #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_RESOURCE_H # include #endif #include "common/eventlog.h" #include "common/bn_type.h" #include "common/setup_after.h" /* file descriptor limit has different name on BSD and Linux */ #ifdef RLIMIT_NOFILE # define RLIM_NUMFILES RLIMIT_NOFILE #else # ifdef RLIMIT_OFILE # define RLIM_NUMFILES RLIMIT_OFILE # endif #endif namespace pvpgn { extern int get_socket_limit(void) { int socklimit = 0; #ifdef HAVE_GETRLIMIT struct rlimit rlim; if (getrlimit(RLIM_NUMFILES, &rlim) < 0) eventlog(eventlog_level_error, __FUNCTION__, "getrlimit returned error: {}", std::strerror(errno)); socklimit = rlim.rlim_cur; #else /* FIXME: WIN32: somehow get WSAData win32 socket limit here */ #endif #if !(defined HAVE_POLL || defined HAVE_KQUEUE || defined HAVE_EPOLL) if (!socklimit || FD_SETSIZE < socklimit) socklimit = FD_SETSIZE; #endif /* make socket limit smaller than file limit to make sure log files, db connections and save files will still work */ socklimit -= 64; return socklimit; } } pvpgn-1.99.7.2.1+dfsg/src/common/rlimit.h000066400000000000000000000015401462640461400177300ustar00rootroot00000000000000#ifndef COMMON_RLIMIT_H_INCLUDED #define COMMON_RLIMIT_H_INCLUDED /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ namespace pvpgn { extern int get_socket_limit(void); } #endif pvpgn-1.99.7.2.1+dfsg/src/common/scoped_array.h000066400000000000000000000042371462640461400211110ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef PVPGN_SCOPED_ARRAY_H #define PVPGN_SCOPED_ARRAY_H namespace pvpgn { template class scoped_array { public: /** initilize the object aquiring ownership of the given parameter (0 for no onwership) */ explicit scoped_array(T* ptr_ = 0) :ptr(ptr_) {} /** initilize the object from a blind pointer supporting implicit conversions */ template explicit scoped_array(V* ptr_) :ptr(ptr_) {} /** release memory if aquired ownershipt */ ~scoped_array() throw() { cleanup(); } /** get the wrapped array pointer */ T* get() const { return ptr; } /** release ownership of the array */ T* release() { T* tmp = ptr; ptr = 0; return tmp; } /** reinitilize object, release owned resource first if any */ void reset(T* ptr_ = 0) { cleanup(); ptr = ptr_; } /** reinitilize object, supports implicit conversions */ template void reset(V* ptr_) { cleanup(); ptr = ptr_; } /** allow indexed const dereferencing access to the wrapped array */ const T& operator[](unsigned idx) const { return ptr[idx]; } /** allow indexed dereferencing access to the wrapped array */ T& operator[](unsigned idx) { return ptr[idx]; } private: T* ptr; /* do not allow to copy a scoped_array */ scoped_array(const scoped_array&); scoped_array& operator=(const scoped_array&); void cleanup() { if (ptr) delete[] ptr; } }; } #endif pvpgn-1.99.7.2.1+dfsg/src/common/scoped_ptr.h000066400000000000000000000047141462640461400206000ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef PVPGN_SCOPED_PTR_H #define PVPGN_SCOPED_PTR_H #include namespace pvpgn { template class scoped_ptr { public: /** initilize the object aquiring ownership of the given parameter (0 for no onwership) */ explicit scoped_ptr(T* ptr_ = 0) :ptr(ptr_) {} /** initilize from a blind pointer supporting implicit conversion */ template explicit scoped_ptr(V* ptr_) :ptr(ptr_) {} /** initilize the object transfering ownership from an std::unique_ptr<> */ template explicit scoped_ptr(std::unique_ptr& aptr) :ptr(aptr.release()) {} /** release memory if aquired ownershipt */ ~scoped_ptr() throw() { cleanup(); } /** get the wrapped pointer */ T* get() const { return ptr; } /** release ownership of the memory */ T* release() { T* tmp = ptr; ptr = 0; return tmp; } /** reinitilize object, release owned resource first if any */ void reset(T* ptr_ = 0) { cleanup(); ptr = ptr_; } /** reinitilize object, supports implicit conversions */ template void reset(V* ptr_) { cleanup(); ptr = ptr_; } /** const-dereference of the wrapped pointer */ const T& operator*() const { return *ptr; } /** dereference of the wrapped pointer */ T& operator*() { return *ptr; } /** const-member access to the wrapped pointer */ const T* operator->() const { return ptr; } /** member access to the wrapped pointer */ T* operator->() { return ptr; } private: T* ptr; /* do not allow to copy a scoped_ptr (use std::unique_ptr<> if you need that) */ scoped_ptr(const scoped_ptr&); scoped_ptr& operator=(const scoped_ptr&); void cleanup() { delete ptr; } }; } #endif pvpgn-1.99.7.2.1+dfsg/src/common/setup_after.h000066400000000000000000000016741462640461400207610ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SETUP_BEFORE_H # error "This file must be included after all other header files" #endif #ifndef INCLUDED_SETUP_AFTER_H #define INCLUDED_SETUP_AFTER_H #endif pvpgn-1.99.7.2.1+dfsg/src/common/setup_before.h000066400000000000000000000373431462640461400211240ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef INCLUDED_SETUP_AFTER_H # error "This file must be included before all other header files" #endif #ifndef INCLUDED_SETUP_BEFORE_H #define INCLUDED_SETUP_BEFORE_H /* get autoconf defines */ #ifdef HAVE_CONFIG_H # include "config.h" #else # ifdef WIN32 # include "win32/configwin.h" # else # error "No config.h but not building on WIN32, how to configure the system?" # endif #endif /* This file contains compile-time configuration parameters including * debugging options, default configuration values, and format strings. */ #include "version.h" /***************************************************************/ /* Debugging options */ /* print bad calling locations of account functions */ #define DEBUG_ACCOUNT /* make bnchat, etc. print debug messages */ #define CLIENTDEBUG /* make the routines call xxx_check() and notice xxx_purge() calls during list traversal */ #undef LIST_DEBUG #undef HASHTABLE_DEBUG /* this will use GCC evensions to verify that all module arguments to eventlog() are correct. */ #undef DEBUGMODSTRINGS /* After you compile, use this script: strings bnetd | egrep '@\([^@]*@@[a-z_]*\)@' | sed -e 's/@(\([^:]*\):\([a-z_]*\)@@\([a-z_]*\))@/\1 \2 \3/g' | awk '{ if ( $2 != $3 ) printf("%s: %s->%s\n",$1,$2,$3); }' */ /* this will test get/unget memory management in account.c */ #undef TESTUNGET /* check ladders for impossible combinations when returning them */ #define LADDER_DEBUG /***************************************************************/ /* compile-time options */ /* how long do we wait for the UDP test reply? */ #define CLIENT_MAX_UDPTEST_WAIT 5 /* length of listen socket queue */ const int LISTEN_QUEUE = 10; /* the format for account numbers */ #define UID_FORMAT "#{:08}" #define UID_FORMATF "#%08u" /* the format for game ids */ #define GAMEID_FORMAT "#{:06}" #define GAMEID_FORMATF "#%06u" /* the format of timestamps in the userlogfile */ #define USEREVENT_TIME_FORMAT "%b %d %H:%M" const int USEREVENT_TIME_MAXLEN = 16; /* the format of timestamps in the logfile */ #define EVENT_TIME_FORMAT "%b %d %H:%M:%S" const int EVENT_TIME_MAXLEN = 32; /* the format of the stat times in bnstat */ #define STAT_TIME_FORMAT "%Y %b %d %H:%M:%S" const int STAT_TIME_MAXLEN = 32; /* the format of the file modification time in bnftp */ #define FILE_TIME_FORMAT "%Y %b %d %H:%M:%S" const int FILE_TIME_MAXLEN = 32; /* the format of the dates in the game report and for /gameinfo */ #define GAME_TIME_FORMAT "%a %b %d %H:%M:%S %Z" const int GAME_TIME_MAXLEN = 32; /* the format of the timestamps for the start/end of channel log files */ #define CHANLOG_TIME_FORMAT "%Y %b %d %H:%M:%S %Z" const int CHANLOG_TIME_MAXLEN = 32; /* the format of the timestamps for lines in the channel log files */ #define CHANLOGLINE_TIME_FORMAT "%b %d %H:%M:%S" /* adjustable constants */ #define BNETD_LADDER_DEFAULT_TIME "19764578 0" /* 0:00 1 Jan 1970 GMT */ /* for clients if ioctl(TIOCGWINSZ) fails and $LINES and $COLUMNS aren't set */ const int DEF_SCREEN_WIDTH = 80; const int DEF_SCREEN_HEIGHT = 24; /***************************************************************/ /* default values for bnetd.conf */ /* default Boolean setup values */ const bool BNETD_CHANLOG = false; /* default path configuration values */ #ifndef BNETD_DEFAULT_CONF_FILE # define BNETD_DEFAULT_CONF_FILE "conf/bnetd.conf" #endif const char * const BNETD_FILE_DIR = "files"; const char * const BNETD_SCRIPT_DIR = "lua"; const char * const BNETD_STORAGE_PATH = ""; const char * const BNETD_REPORT_DIR = "reports"; const char * const BNETD_I18N_DIR = "conf/i18n"; // ------ i18n files -------- const char * const BNETD_LOCALIZE_FILE = "common.xml"; const char * const BNETD_MOTD_FILE = "bnmotd.txt"; const char * const BNETD_MOTDW3_FILE = "w3motd.txt"; const char * const BNETD_NEWS_FILE = "news.txt"; const char * const BNETD_HELP_FILE = "bnhelp.conf"; const char * const BNETD_TOS_FILE = "newaccount.txt"; // -------------------------- const char * const BNETD_LOG_FILE = "logs/bnetd.log"; const char * const BNETD_AD_FILE = "conf/ad.conf"; const char * const BNETD_CHANNEL_FILE = "conf/channel.conf"; const char * const BNETD_PID_FILE = ""; /* this means "none" */ const char * const BNETD_ACCOUNT_TMP = ".bnetd_acct_temp"; const char * const BNETD_IPBAN_FILE = "conf/bnban.conf"; const char * const BNETD_TRANS_FILE = "conf/address_translation.conf"; const char * const BNETD_CHANLOG_DIR = "var/chanlogs"; const char * const BNETD_USERLOG_DIR = "var/userlogs"; const char * const BNETD_REALM_FILE = "conf/realm.conf"; const char * const BNETD_ISSUE_FILE = "conf/bnissue.txt"; const char * const BNETD_MAIL_DIR = "var/bnmail"; const char * const PVPGN_VERSIONCHECK = "conf/versioncheck.conf"; const char * const BNETD_LADDER_DIR = "var/ladders"; const char * const BNETD_STATUS_DIR = "var/status"; const char * const BNETD_TOPIC_FILE = "var/topics"; const char * const BNETD_DBLAYOUT_FILE = "conf/sql_DB_layout.conf"; const char * const BNETD_SUPPORT_FILE = "conf/supportfile.conf"; const char * const BNETD_COMMAND_GROUPS_FILE = "conf/command_groups.conf"; const char * const BNETD_TOURNAMENT_FILE = "conf/tournament.conf"; const char * const BNETD_CUSTOMICONS_FILE = "conf/icons.conf"; const char * const BNETD_ALIASFILE = "conf/bnalias.conf"; /* time limit for new member as newer(whom cannot be promoted) in clan, (hrs) */ const unsigned CLAN_NEWER_TIME = 168; const unsigned CLAN_DEFAULT_MAX_MEMBERS = 50; /* hardcoded limits in the client */ const unsigned CLAN_MIN_MEMBERS = 10; const unsigned CLAN_MAX_MEMBERS = 100; const unsigned CLAN_DEFAULT_MIN_INVITES = 2; const unsigned MAX_FRIENDS = 20; /* maximum ammount of bytes sent in a single server.c/sd_tcpoutput call */ const unsigned BNETD_MAX_OUTBURST = 16384; /* default files relative to FILE_DIR */ const char * const BNETD_ICON_FILE = "icons.bni"; const char * const BNETD_WAR3_ICON_FILE = "icons-WAR3.bni"; const char * const BNETD_STAR_ICON_FILE = "icons_STAR.bni"; const char * const BNETD_MPQ_FILE = "autoupdate"; /* Westwood Online default configuration values */ const char * const BNETD_APIREG_ADDRS = ""; const int BNETD_APIREG_PORT = 5400; const char * const BNETD_WOLV1_ADDRS = ""; const int BNETD_WOLV1_PORT = 4000; const char * const BNETD_WOLV2_ADDRS = ""; const int BNETD_WOLV2_PORT = 4005; const char * const BNETD_WGAMERES_ADDRS = ""; const int BNETD_WGAMERES_PORT = 4807; /* other default configuration values */ const char * const BNETD_LOG_LEVELS = "warn,error"; const char * const BNETD_SERV_ADDRS = ""; /* this means none */ const int BNETD_SERV_PORT = 6112; const char * const BNETD_W3ROUTE_ADDR = "0.0.0.0"; const int BNETD_W3ROUTE_PORT = 6200; const char * const BNETD_SERVERNAME = PVPGN_SOFTWARE " Realm"; const char * const BNETD_IRC_ADDRS = ""; /* this means none */ const int BNETD_IRC_PORT = 6667; /* used if port not specified */ const char * const BNETD_IRC_NETWORK_NAME = PVPGN_SOFTWARE; const char * const BNETD_TRACK_ADDRS = "track.pvpgn.org"; const int BNETD_TRACK_PORT = 6114; /* use this port if not specified */ const int BNETD_DEF_TEST_PORT = 6112; /* default guess for UDP test port */ const int BNETD_MIN_TEST_PORT = 6112; const int BNETD_MAX_TEST_PORT = 6500; const unsigned BNETD_USERSYNC = 300; /* s */ const unsigned BNETD_USERFLUSH = 1000; const unsigned BNETD_USERSTEP = 100; /* check 100 users per call in accountlist_save() */ const unsigned BNETD_PACKET_LIMIT = 1000; /* maximum of 1000 packets in packet queue until connections is dropped */ const unsigned BNETD_LATENCY = 600; /* s */ const unsigned BNETD_IRC_LATENCY = 180; /* s */ /* Ping timeout for IRC connections */ const unsigned BNETD_DEF_NULLMSG = 120; /* s */ const unsigned BNETD_TRACK_TIME = 0; const int BNETD_POLL_INTERVAL = 20; /* 20 ms */ const int BNETD_JIFFIES = 50; /* 50 ms jiffies time quantum */ const unsigned BNETD_SHUTDELAY = 300; /* s */ const unsigned BNETD_SHUTDECR = 60; /* s */ const char * const BNETD_DEFAULT_OWNER = "PvPGN"; const char * const BNETD_DEFAULT_KEY = "3310541526205"; const char * const BNETD_DEFAULT_HOST = "localhost"; const unsigned BNETD_QUOTA_DOBAE = 7; /* lines */ const unsigned BNETD_QUOTA_LINES = 5; /* lines */ const unsigned BNETD_QUOTA_TIME = 5; /* s */ const unsigned BNETD_QUOTA_WLINE = 40; /* chars */ const unsigned BNETD_QUOTA_MLINE = 200; /* chars */ const unsigned BNETD_LADDER_INIT_RAT = 1000; const unsigned BNETD_MAIL_SUPPORT = 0; const unsigned BNETD_MAIL_QUOTA = 5; const char * const BNETD_LOG_NOTICE = "*** Please note this channel is logged! ***"; const unsigned BNETD_HASHTABLE_SIZE = 61; const int BNETD_REALM_PORT = 6113; /* where D2CS listens */ const char * const BNETD_TELNET_ADDRS = ""; /* this means none */ const int BNETD_TELNET_PORT = 23; /* used if port not specified */ const char * const BNETD_EXEINFO_MATCH = "true"; const unsigned PVPGN_VERSION_TIMEDIV = 0; /* no timediff check by default */ const int PVPGN_CACHE_MEMLIMIT = 5000000; /* bytes */ const char * const PVPGN_DEFAULT_SYMB = "-_[]"; const char * const BNETD_LOG_COMMAND_GROUPS = "2345678"; const char * const BNETD_LOG_COMMAND_LIST = ""; #ifdef WIN32 // reserved filenames on Windows const char* const ILLEGALFILENAMES[] = { "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "con", "nul", "prn", "aux" }; #endif /***************************************************************/ /* default values for the tracking server */ const int BNTRACKD_EXPIRE = 600; const int BNTRACKD_UPDATE = 150; const int BNTRACKD_GRANULARITY = 5; const int BNTRACKD_SERVER_PORT = 6114; const char * const BNTRACKD_PIDFILE = ""; /* this means "none" */ const char * const BNTRACKD_OUTFILE = "pvpgnlist.txt"; #ifdef WIN32 const char * const BNTRACKD_PROCESS = "process.pl"; const char * const BNTRACKD_LOGFILE = "bntrackd.log"; #else const char * const BNTRACKD_PROCESS = "scripts/process.pl"; const char * const BNTRACKD_LOGFILE = "logs/bntrackd.log"; #endif /***************************************************************/ /* default values for W3XP anongameinfo packet */ const char * const PVPGN_DEFAULT_URL = "www.pvpgn.pro"; const char * const PVPGN_PG_1V1_DESC = "Solo Games"; const char * const PVPGN_AT_2V2_DESC = "2 player team"; const char * const PVPGN_AT_3V3_DESC = "3 player team"; const char * const PVPGN_AT_4V4_DESC = "4 player team"; const char * const PVPGN_PG_TEAM_DESC = "Team Games"; const char * const PVPGN_PG_FFA_DESC = "Free for All Games"; const char * const PVPGN_CLAN_1V1_DESC = "Solo Games"; const char * const PVPGN_CLAN_2V2_DESC = "2 player team"; const char * const PVPGN_CLAN_3V3_DESC = "3 player team"; const char * const PVPGN_CLAN_4V4_DESC = "4 player team"; const char * const PVPGN_1V1_GT_DESC = "One vs. One"; const char * const PVPGN_1V1_GT_LONG = "Two players fight to the death"; const char * const PVPGN_2V2_GT_DESC = "Two vs. Two"; const char * const PVPGN_2V2_GT_LONG = "Two teams of two vie for dominance"; const char * const PVPGN_3V3_GT_DESC = "Three vs. Three"; const char * const PVPGN_3V3_GT_LONG = "Two teams of three face off on the battlefield"; const char * const PVPGN_4V4_GT_DESC = "Four vs. Four"; const char * const PVPGN_4V4_GT_LONG = "Two teams of four head to battle"; const char * const PVPGN_5V5_GT_DESC = "Five vs. Five"; const char * const PVPGN_5V5_GT_LONG = "Two teams of five - who will prevail?"; const char * const PVPGN_6V6_GT_DESC = "Six vs. Six"; const char * const PVPGN_6V6_GT_LONG = "Two teams of six - get ready to rumble!"; const char * const PVPGN_SFFA_GT_DESC = "Small Free for All"; const char * const PVPGN_SFFA_GT_LONG = "Can you defeat 3-5 opponents alone?"; const char * const PVPGN_TFFA_GT_DESC = "Team Free for All"; const char * const PVPGN_TFFA_GT_LONG = "Can your team defeat 1-2 others?"; const char * const PVPGN_2V2V2_GT_DESC = "Two vs. Two vs. Two"; const char * const PVPGN_2V2V2_GT_LONG = "Three teams of two, can you handle it?"; const char * const PVPGN_3V3V3_GT_DESC = "Three vs. Three vs. Three"; const char * const PVPGN_3V3V3_GT_LONG = "Three teams of three battle each other "; const char * const PVPGN_4V4V4_GT_DESC = "Four vs. Four vs. Four"; const char * const PVPGN_4V4V4_GT_LONG = "Three teams of four - things getting crowded?"; const char * const PVPGN_2V2V2V2_GT_DESC = "Two vs. Two vs. Two vs. Two"; const char * const PVPGN_2V2V2V2_GT_LONG = "Four teams of two, is this a challenge?"; const char * const PVPGN_3V3V3V3_GT_DESC = "Three vs. Three vs. Three vs. Three"; const char * const PVPGN_3V3V3V3_GT_LONG = "Four teams of three, the ultimate challenge!"; const char * const PVPGN_AINFO_FILE = "conf/anongame_infos.conf"; /* max number of players in an anongame match [Omega] */ const int ANONGAME_MAX_GAMECOUNT = 12; /* max level of players*/ const int ANONGAME_MAX_LEVEL = 100; /***************************************************************/ /* platform dependent features */ /* conditionally enabled features */ #if defined(HAVE_SIGACTION) && defined(HAVE_SIGPROCMASK) && defined(HAVE_SIGADDSET) # define DO_POSIXSIG #endif #if defined(HAVE_FORK) && defined(HAVE_PIPE) # define DO_SUBPROC #endif #if defined(HAVE_FORK) && defined(HAVE_CHDIR) && (defined(HAVE_SETPGID) || defined(HAVE_SETPGRP)) # define DO_DAEMONIZE #endif /* GCC attributes */ /* enable format mismatch warnings from gcc */ #if defined(__GNUC__) && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2) # if __GNUC__ == 2 && __GNUC_MINOR__ < 7 /* namespace clean versions were available starting in 2.6.4 */ # define __format__ format # define __printf__ printf # endif # define PRINTF_ATTR(FMTARG,VARG) __attribute__((__format__(printf,FMTARG,VARG))) #else # define PRINTF_ATTR(FMTARG,VARG) #endif /* type attributes */ /* set GCC machine storage mode */ #if defined(__GNUC__) && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2) # define MODE_ATTR(M) __attribute__((__mode__(M))) # define HAVE_MODE_ATTR #else # define MODE_ATTR(M) #endif /* avoid using padding on GCC, for other compilers you need alternate solutions */ #if defined(__GNUC__) # define PACKED_ATTR() __attribute__((__packed__)) #else # define PACKED_ATTR() #endif /* default maxim number of sockets in the fdwatch pool */ const int BNETD_MAX_SOCKETS = 1000; /* Used for FDSETSIZE redefine (only on WIN32 so so far) */ const int BNETD_MAX_SOCKVAL = 8192; /* * select() hackery... works most places, need to add autoconf checks * because some systems may redefine FD_SETSIZE, have it as a variable, * or not have the concept of such a value. * dizzy: this is a total hack. only WIN32 so far specifies this as beeing * "legal"; in UNIX in general it should be NOT because the kernel interface * of select will never notice your userland changes to the fd_sets */ /* Win32 defaults to 64, BSD and Linux default to 1024 */ /* FIXME: how big can this be before things break? */ #ifdef WIN32 # define FD_SETSIZE BNETD_MAX_SOCKVAL #endif #ifdef HAVE_EPOLL_CREATE # define HAVE_EPOLL 1 #endif #if defined(WITH_LUA) #define WITH_LUA 1 #endif #if defined(WITH_SQL_MYSQL) || defined(WITH_SQL_PGSQL) || defined(WITH_SQL_SQLITE3) || defined(WITH_SQL_ODBC) #define WITH_SQL 1 #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/systemerror.cpp000066400000000000000000000020761462640461400213660ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "systemerror.h" #include #include "compat/strerror.h" #include "common/setup_after.h" namespace pvpgn { SystemError::SystemError(const std::string& prefix, int err) :std::runtime_error((prefix + ": ") + pstrerror(err)) { } SystemError::~SystemError() throw() { } } pvpgn-1.99.7.2.1+dfsg/src/common/systemerror.h000066400000000000000000000020071462640461400210250ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef PVPGN_SYSTEMERROR_H #define PVPGN_SYSTEMERROR_H #include #include namespace pvpgn { class SystemError : public std::runtime_error { public: SystemError(const std::string& prefix, int err = errno); ~SystemError() throw(); }; } #endif pvpgn-1.99.7.2.1+dfsg/src/common/tag.cpp000066400000000000000000000411571462640461400175460ustar00rootroot00000000000000/* * Copyright (C) 2004 Aaron * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * Copyright (C) 2007,2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/tag.h" #include #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/xstring.h" #include "common/setup_after.h" namespace pvpgn { /* fixme: have all functions call tag_str_to_uint() */ extern t_clienttag clienttag_str_to_uint(char const * clienttag) { if (!clienttag) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL clienttag"); return CLIENTTAG_UNKNOWN_UINT; } return tag_str_to_uint(clienttag); } /* fixme: have all fuctions call tag_uint_to_str() */ extern char const * clienttag_uint_to_str(t_clienttag clienttag) { switch (clienttag) { case CLIENTTAG_BNCHATBOT_UINT: return CLIENTTAG_BNCHATBOT; case CLIENTTAG_STARCRAFT_UINT: return CLIENTTAG_STARCRAFT; case CLIENTTAG_BROODWARS_UINT: return CLIENTTAG_BROODWARS; case CLIENTTAG_SHAREWARE_UINT: return CLIENTTAG_SHAREWARE; case CLIENTTAG_DIABLORTL_UINT: return CLIENTTAG_DIABLORTL; case CLIENTTAG_DIABLOSHR_UINT: return CLIENTTAG_DIABLOSHR; case CLIENTTAG_WARCIIBNE_UINT: return CLIENTTAG_WARCIIBNE; case CLIENTTAG_DIABLO2DV_UINT: return CLIENTTAG_DIABLO2DV; case CLIENTTAG_STARJAPAN_UINT: return CLIENTTAG_STARJAPAN; case CLIENTTAG_DIABLO2ST_UINT: return CLIENTTAG_DIABLO2ST; case CLIENTTAG_DIABLO2XP_UINT: return CLIENTTAG_DIABLO2XP; case CLIENTTAG_WARCRAFT3_UINT: return CLIENTTAG_WARCRAFT3; case CLIENTTAG_WAR3XP_UINT: return CLIENTTAG_WAR3XP; case CLIENTTAG_IIRC_UINT: return CLIENTTAG_IIRC; case CLIENTTAG_WCHAT_UINT: return CLIENTTAG_WCHAT; case CLIENTTAG_TIBERNSUN_UINT: return CLIENTTAG_TIBERNSUN; case CLIENTTAG_TIBSUNXP_UINT: return CLIENTTAG_TIBSUNXP; case CLIENTTAG_REDALERT_UINT: return CLIENTTAG_REDALERT; case CLIENTTAG_REDALERT2_UINT: return CLIENTTAG_REDALERT2; case CLIENTTAG_DUNE2000_UINT: return CLIENTTAG_DUNE2000; case CLIENTTAG_NOX_UINT: return CLIENTTAG_NOX; case CLIENTTAG_NOXQUEST_UINT: return CLIENTTAG_NOXQUEST; case CLIENTTAG_RENEGADE_UINT: return CLIENTTAG_RENEGADE; case CLIENTTAG_RENGDFDS_UINT: return CLIENTTAG_RENGDFDS; case CLIENTTAG_YURISREV_UINT: return CLIENTTAG_YURISREV; case CLIENTTAG_EMPERORBD_UINT: return CLIENTTAG_EMPERORBD; case CLIENTTAG_LOFLORE3_UINT: return CLIENTTAG_LOFLORE3; case CLIENTTAG_WWOL_UINT: return CLIENTTAG_WWOL; default: return CLIENTTAG_UNKNOWN; } } /*****/ /* make all letters in string upper case - used in command.cpp*/ extern t_tag tag_case_str_to_uint(char const * tag_str) { unsigned int i, len; char temp_str[5]; len = std::strlen(tag_str); if (len != 4) eventlog(eventlog_level_warn, __FUNCTION__, "got unusual sized clienttag '{}'", tag_str); for (i = 0; i < len && i < 4; i++) temp_str[i] = safe_toupper(tag_str[i]); temp_str[4] = '\0'; return tag_str_to_uint(temp_str); } extern t_tag tag_str_to_uint(char const * tag_str) { t_tag tag_uint; if (!tag_str) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL tag"); return 0; /* unknown */ } tag_uint = tag_str[0] << 24; tag_uint |= tag_str[1] << 16; tag_uint |= tag_str[2] << 8; tag_uint |= tag_str[3]; return tag_uint; } /* tag_uint_to_str() * * from calling function: * * char tag_str[5]; // define first, then send into fuction * tag_uint_to_str(tag_str, tag_uint); // returns pointer to tag_str * * Nothing to malloc, nothing to free */ extern const char * tag_uint_to_str(char * tag_str, t_tag tag_uint) { if (!tag_uint) /* return "UNKN" if tag_uint = 0 */ return TAG_UNKNOWN; tag_str[0] = ((unsigned char)(tag_uint >> 24)); tag_str[1] = ((unsigned char)(tag_uint >> 16) & 0xff); tag_str[2] = ((unsigned char)(tag_uint >> 8) & 0xff); tag_str[3] = ((unsigned char)(tag_uint)& 0xff); tag_str[4] = '\0'; return tag_str; } extern std::string tag_uint_to_str2(t_tag tag_uint) { if (!tag_uint) /* return "UNKN" if tag_uint = 0 */ return std::string(TAG_UNKNOWN); char tag_str[5] = {}; tag_str[0] = ((unsigned char)(tag_uint >> 24)); tag_str[1] = ((unsigned char)(tag_uint >> 16) & 0xff); tag_str[2] = ((unsigned char)(tag_uint >> 8) & 0xff); tag_str[3] = ((unsigned char)(tag_uint)& 0xff); tag_str[4] = '\0'; return std::string(tag_str); } extern const char * tag_uint_to_revstr(char * tag_str, t_tag tag_uint) { if (!tag_uint) /* return "UNKN" if tag_uint = 0 */ return TAG_UNKNOWN; tag_str[0] = ((unsigned char)(tag_uint)& 0xff); tag_str[1] = ((unsigned char)(tag_uint >> 8) & 0xff); tag_str[2] = ((unsigned char)(tag_uint >> 16) & 0xff); tag_str[3] = ((unsigned char)(tag_uint >> 24)); tag_str[4] = '\0'; return tag_str; } extern int tag_check_arch(t_tag tag_uint) { switch (tag_uint) { case ARCHTAG_WINX86_UINT: case ARCHTAG_MACPPC_UINT: case ARCHTAG_OSXPPC_UINT: return 1; default: return 0; } } extern int tag_check_client(t_tag tag_uint) { switch (tag_uint) { case CLIENTTAG_BNCHATBOT_UINT: case CLIENTTAG_STARCRAFT_UINT: case CLIENTTAG_BROODWARS_UINT: case CLIENTTAG_SHAREWARE_UINT: case CLIENTTAG_DIABLORTL_UINT: case CLIENTTAG_DIABLOSHR_UINT: case CLIENTTAG_WARCIIBNE_UINT: case CLIENTTAG_DIABLO2DV_UINT: case CLIENTTAG_STARJAPAN_UINT: case CLIENTTAG_DIABLO2ST_UINT: case CLIENTTAG_DIABLO2XP_UINT: case CLIENTTAG_WARCRAFT3_UINT: case CLIENTTAG_WAR3XP_UINT: case CLIENTTAG_IIRC_UINT: case CLIENTTAG_WCHAT_UINT: case CLIENTTAG_TIBERNSUN_UINT: case CLIENTTAG_TIBSUNXP_UINT: case CLIENTTAG_REDALERT_UINT: case CLIENTTAG_REDALERT2_UINT: case CLIENTTAG_DUNE2000_UINT: case CLIENTTAG_NOX_UINT: case CLIENTTAG_NOXQUEST_UINT: case CLIENTTAG_RENEGADE_UINT: case CLIENTTAG_RENGDFDS_UINT: case CLIENTTAG_YURISREV_UINT: case CLIENTTAG_EMPERORBD_UINT: case CLIENTTAG_LOFLORE3_UINT: case CLIENTTAG_WWOL_UINT: return 1; default: return 0; } } extern int tag_check_wolv1(t_tag tag_uint) { switch (tag_uint) { case CLIENTTAG_WCHAT_UINT: return 1; default: return 0; } } extern int tag_check_wolv2(t_tag tag_uint) { switch (tag_uint) { case CLIENTTAG_TIBERNSUN_UINT: case CLIENTTAG_TIBSUNXP_UINT: case CLIENTTAG_REDALERT_UINT: case CLIENTTAG_REDALERT2_UINT: case CLIENTTAG_DUNE2000_UINT: case CLIENTTAG_NOX_UINT: case CLIENTTAG_NOXQUEST_UINT: case CLIENTTAG_RENEGADE_UINT: case CLIENTTAG_RENGDFDS_UINT: case CLIENTTAG_YURISREV_UINT: case CLIENTTAG_EMPERORBD_UINT: case CLIENTTAG_LOFLORE3_UINT: case CLIENTTAG_WWOL_UINT: return 1; default: return 0; } } extern char const * clienttag_get_title(t_clienttag clienttag) { switch (clienttag) { case CLIENTTAG_WAR3XP_UINT: return "Warcraft III Frozen Throne"; case CLIENTTAG_WARCRAFT3_UINT: return "Warcraft III"; case CLIENTTAG_DIABLO2XP_UINT: return "Diablo II Lord of Destruction"; case CLIENTTAG_DIABLO2DV_UINT: return "Diablo II"; case CLIENTTAG_STARJAPAN_UINT: return "Starcraft (Japan)"; case CLIENTTAG_WARCIIBNE_UINT: return "Warcraft II"; case CLIENTTAG_DIABLOSHR_UINT: return "Diablo I (Shareware)"; case CLIENTTAG_DIABLORTL_UINT: return "Diablo I"; case CLIENTTAG_SHAREWARE_UINT: return "Starcraft (Shareware)"; case CLIENTTAG_BROODWARS_UINT: return "Starcraft: Brood War"; case CLIENTTAG_STARCRAFT_UINT: return "Starcraft"; case CLIENTTAG_BNCHATBOT_UINT: return "Chat"; case CLIENTTAG_IIRC_UINT: return "Internet Relay Chat"; case CLIENTTAG_WCHAT_UINT: return "Westwood Chat"; case CLIENTTAG_TIBERNSUN_UINT: return "Tiberian Sun"; case CLIENTTAG_TIBSUNXP_UINT: return "Tiberian Sun: Firestorm"; case CLIENTTAG_REDALERT_UINT: return "Red Alert"; case CLIENTTAG_REDALERT2_UINT: return "Red Alert 2"; case CLIENTTAG_DUNE2000_UINT: return "Dune 2000"; case CLIENTTAG_NOX_UINT: return "Nox"; case CLIENTTAG_NOXQUEST_UINT: return "Nox Quest"; case CLIENTTAG_RENEGADE_UINT: return "Renegade"; case CLIENTTAG_RENGDFDS_UINT: return "Renegade Free Dedicated Server"; case CLIENTTAG_YURISREV_UINT: return "Yuri's Revenge"; case CLIENTTAG_EMPERORBD_UINT: return "Emepror: Battle for Dune"; case CLIENTTAG_LOFLORE3_UINT: return "Lands of Lore 3"; case CLIENTTAG_WWOL_UINT: return "Westwood Online"; default: return "Unknown"; } } extern int tag_check_in_list(t_clienttag clienttag, char const * list) { /* checks if a clienttag is in the list * @clienttag : clienttag integer to check * if it's allowed returns 0 * if it's not allowed returns -1 */ char *tmp, *p, *q; /* by default allow all */ if (!list) return 0; /* this shortcut check should make server as fast as before if * the configuration is left in default mode */ if (!strcasecmp(list, "all")) return 0; tmp = p = xstrdup(list); do { q = std::strchr(p, ','); if (q) *q = '\0'; if (!strcasecmp(p, "all")) goto ok; if (std::strlen(p) != 4) continue; if (clienttag == tag_case_str_to_uint(p)) goto ok; /* client is in list */ if (q) p = q + 1; } while (q); xfree((void *)tmp); return -1; /* client is NOT in list */ ok: xfree((void *)tmp); return 0; } extern t_clienttag tag_sku_to_uint(int sku) { /** * Here is table of Westwood Online SKUs and by this SKU returning CLIENTTAG * SKUs are from Autoupdate FTP and from windows registry */ if (!sku) { ERROR0("got NULL sku"); return CLIENTTAG_UNKNOWN_UINT; } switch (sku) { case 1000: /* Westwood Chat */ return CLIENTTAG_WCHAT_UINT; /* case 1002: Internet Registration */ case 1003: /* Command & Conquer */ return CLIENTTAG_WCHAT_UINT; // return CLIENTTAG_CNCONQUER_UINT; case 1005: /* Red Alert 1 v2.00 (Westwood Chat Version) */ case 1006: case 1007: case 1008: return CLIENTTAG_WCHAT_UINT; // return CLIENTTAG_REDALERT_UINT; case 1040: /* C&C Sole Survivor */ return CLIENTTAG_WCHAT_UINT; // return CLIENTTAG_CNCSOLES_UINT; case 3072: /* C&C Renegade */ case 3074: case 3075: case 3078: case 3081: case 3082: // case 16780288: /* renegade ladder players */ return CLIENTTAG_RENEGADE_UINT; case 3584: /* Dune 2000 */ case 3586: case 3587: case 3589: case 3591: return CLIENTTAG_DUNE2000_UINT; case 4096: /* Nox */ case 4098: case 4099: case 4101: case 4102: case 4105: return CLIENTTAG_NOX_UINT; case 4608: /* Tiberian Sun */ case 4610: case 4611: case 4615: return CLIENTTAG_TIBERNSUN_UINT; case 5376: /* Red Alert 1 v3.03 (4 Players Internet Version) */ case 5378: case 5379: return CLIENTTAG_REDALERT_UINT; case 6400: /* Lands of Lore 3 */ case 6401: case 6402: case 6403: case 6405: return CLIENTTAG_LOFLORE3_UINT; case 7168: /* Tiberian Sun: Firestorm */ case 7170: case 7171: case 7175: case 7424: case 7426: case 7427: case 7431: return CLIENTTAG_TIBSUNXP_UINT; case 7936: /* Emperor: Battle for Dune */ case 7938: case 7939: case 7945: case 7946: return CLIENTTAG_EMPERORBD_UINT; case 8448: /* Red Alert 2 */ case 8450: case 8451: case 8457: case 8458: case 8960: case 8962: case 8963: case 8969: case 8970: return CLIENTTAG_REDALERT2_UINT; case 9472: /* Nox Quest */ case 9474: case 9475: case 9477: case 9478: case 9481: return CLIENTTAG_NOXQUEST_UINT; case 10496: /* Yuri's Revenge */ case 10498: case 10499: case 10505: case 10506: return CLIENTTAG_YURISREV_UINT; case 12288: /* C&C Renegade Free Dedicated Server */ return CLIENTTAG_RENGDFDS_UINT; case 32512: /* Westwood Online API */ return CLIENTTAG_WWOL_UINT; default: /* Unknown Westwood Online game -> is anyone SKU that we havent??? */ return CLIENTTAG_WWOL_UINT; } } extern t_clienttag tag_channeltype_to_uint(int channeltype) { /** * This is used to convert WOL channel type to t_clienttag */ if (!channeltype) { ERROR0("got NULL channeltype"); return CLIENTTAG_UNKNOWN_UINT; } switch (channeltype) { case 0: /* Westwood Chat channels */ case 1: /* Command & Conquer Win95 channels */ case 2: /* Red Alert Win95 channels */ case 3: /* Red Alert Win95 Counterstrike channels */ case 4: /* Red Alert Win95 The Aftermath channels */ case 5: /* Command & Conquer: Sole Survivor channels */ return CLIENTTAG_WCHAT_UINT; case 12: /* Command & Conquer: Renegade channels */ return CLIENTTAG_RENEGADE_UINT; case 14: /* Dune 2000 channels */ return CLIENTTAG_DUNE2000_UINT; case 16: /* Nox channels */ return CLIENTTAG_NOX_UINT; case 18: /* Tiberian Sun channels */ return CLIENTTAG_TIBERNSUN_UINT; case 21: /* Red Alert 1 v 3.03 channels */ return CLIENTTAG_REDALERT_UINT; case 31: /* Emperor: Battle for Dune channels */ return CLIENTTAG_EMPERORBD_UINT; case 33: /* Red Alert 2 channels */ return CLIENTTAG_REDALERT2_UINT; case 37: /* Nox Quest channels */ return CLIENTTAG_NOXQUEST_UINT; case 38: /* QuickMatch channels */ case 39: case 40: return CLIENTTAG_WWOL_UINT; case 41: /* Yuri's Revenge channels */ return CLIENTTAG_YURISREV_UINT; default: return CLIENTTAG_WWOL_UINT; } } extern t_tag tag_wol_locale_to_uint(int locale) { switch (locale) { case tag_wol_locale_unknown: return GAMELANG_ENGLISH_UINT; case tag_wol_locale_other: return GAMELANG_ENGLISH_UINT; case tag_wol_locale_usa: return GAMELANG_ENGLISH_UINT; /* case tag_wol_locale_canada: return enCA case tag_wol_locale_uk: return enGB */ case tag_wol_locale_germany: return GAMELANG_GERMAN_UINT; case tag_wol_locale_france: return GAMELANG_FRENCH_UINT; case tag_wol_locale_spain: return GAMELANG_SPANISH_UINT; /* case tag_wol_locale_netherlands: return nlNL case tag_wol_locale_belgium: return nlBE case tag_wol_locale_austria: return deAT case tag_wol_locale_switzerland: return deCH */ case tag_wol_locale_italy: return GAMELANG_ITALIAN_UINT; /* case tag_wol_locale_denmark: return daDK case tag_wol_locale_sweden: return svSE case tag_wol_locale_norway: return noNO case tag_wol_locale_finland: return fiFI case tag_wol_locale_israel: return arIL case tag_wol_locale_south_africa: return enZA*/ case tag_wol_locale_japan: return GAMELANG_JAPANESE_UINT; case tag_wol_locale_south_korea: return GAMELANG_KOREAN_UINT; case tag_wol_locale_china: return GAMELANG_CHINESE_S_UINT; /* case tag_wol_locale_singapore: return enSG case tag_wol_locale_taiwan: return zhTW case tag_wol_locale_malaysia: return msMY case tag_wol_locale_australia: return enAU case tag_wol_locale_new_zealand: return enNZ case tag_wol_locale_brazil: return ptBR case tag_wol_locale_thailand: return thTH case tag_wol_locale_argentina: return esAR case tag_wol_locale_philippines: return enPH case tag_wol_locale_greece: return elGR case tag_wol_locale_ireland: return enIE*/ case tag_wol_locale_poland: return GAMELANG_POLISH_UINT; /* case tag_wol_locale_portugal: return ptPT case tag_wol_locale_mexico: return esMX*/ case tag_wol_locale_russia: return GAMELANG_RUSSIAN_UINT; /* case tag_wol_locale_turkey: return trTR*/ default: WARN1("{} is not defined", locale); return GAMELANG_ENGLISH_UINT; } } /* Convert clienttag to uppercase and check it in valid client list * Return NULL of tag not found */ extern t_clienttag tag_validate_client(char const * client) { t_clienttag clienttag; if (!client || strlen(client) != 4) return 0; // toupper clienttag = tag_case_str_to_uint(client); if (!tag_check_client(clienttag)) return 0; return clienttag; } } pvpgn-1.99.7.2.1+dfsg/src/common/tag.h000066400000000000000000000200201462640461400171750ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_TAG_TYPES #define INCLUDED_TAG_TYPES #include #include namespace pvpgn { using t_tag = std::uint32_t; using t_archtag = t_tag; using t_clienttag = t_tag; using t_gamelang = t_tag; } typedef enum { tag_wol_locale_unknown = 0, tag_wol_locale_other = 1, tag_wol_locale_usa = 2, tag_wol_locale_canada = 3, tag_wol_locale_uk = 4, tag_wol_locale_germany = 5, tag_wol_locale_france = 6, tag_wol_locale_spain = 7, tag_wol_locale_netherlands = 8, tag_wol_locale_belgium = 9, tag_wol_locale_austria = 10, tag_wol_locale_switzerland = 11, tag_wol_locale_italy = 12, tag_wol_locale_denmark = 13, tag_wol_locale_sweden = 14, tag_wol_locale_norway = 15, tag_wol_locale_finland = 16, tag_wol_locale_israel = 17, tag_wol_locale_south_africa = 18, tag_wol_locale_japan = 19, tag_wol_locale_south_korea = 20, tag_wol_locale_china = 21, tag_wol_locale_singapore = 22, tag_wol_locale_taiwan = 23, tag_wol_locale_malaysia = 24, tag_wol_locale_australia = 25, tag_wol_locale_new_zealand = 26, tag_wol_locale_brazil = 27, tag_wol_locale_thailand = 28, tag_wol_locale_argentina = 29, tag_wol_locale_philippines = 30, tag_wol_locale_greece = 31, tag_wol_locale_ireland = 32, tag_wol_locale_poland = 33, tag_wol_locale_portugal = 34, tag_wol_locale_mexico = 35, tag_wol_locale_russia = 36, tag_wol_locale_turkey = 37 } t_tag_wol_locale; #endif /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TAG_PROTOS #define INCLUDED_TAG_PROTOS /* Software tags */ #define CLIENTTAG_BNCHATBOT "CHAT" /* CHAT bot */ #define CLIENTTAG_BNCHATBOT_UINT 0x43484154 #define CLIENTTAG_STARCRAFT "STAR" /* Starcraft (original) */ #define CLIENTTAG_STARCRAFT_UINT 0x53544152 #define CLIENTTAG_BROODWARS "SEXP" /* Starcraft EXpansion Pack */ #define CLIENTTAG_BROODWARS_UINT 0x53455850 #define CLIENTTAG_SHAREWARE "SSHR" /* Starcraft Shareware */ #define CLIENTTAG_SHAREWARE_UINT 0x53534852 #define CLIENTTAG_DIABLORTL "DRTL" /* Diablo ReTaiL */ #define CLIENTTAG_DIABLORTL_UINT 0x4452544C #define CLIENTTAG_DIABLOSHR "DSHR" /* Diablo SHaReware */ #define CLIENTTAG_DIABLOSHR_UINT 0x44534852 #define CLIENTTAG_WARCIIBNE "W2BN" /* WarCraft II Battle.net Edition */ #define CLIENTTAG_WARCIIBNE_UINT 0x5732424E #define CLIENTTAG_DIABLO2DV "D2DV" /* Diablo II Diablo's Victory */ #define CLIENTTAG_DIABLO2DV_UINT 0x44324456 #define CLIENTTAG_STARJAPAN "JSTR" /* Starcraft (Japan) */ #define CLIENTTAG_STARJAPAN_UINT 0x4A535452 #define CLIENTTAG_DIABLO2ST "D2ST" /* Diablo II Stress Test */ #define CLIENTTAG_DIABLO2ST_UINT 0x44325354 #define CLIENTTAG_DIABLO2XP "D2XP" /* Diablo II Extension Pack */ #define CLIENTTAG_DIABLO2XP_UINT 0x44325850 /* FIXME: according to FSGS: SJPN==Starcraft (Japanese) SSJP==Starcraft (Japanese,Spawn) */ #define CLIENTTAG_WARCRAFT3 "WAR3" /* WarCraft III */ #define CLIENTTAG_WARCRAFT3_UINT 0x57415233 #define CLIENTTAG_WAR3XP "W3XP" /* WarCraft III Expansion */ #define CLIENTTAG_WAR3XP_UINT 0x57335850 #define CLIENTTAG_IIRC "IIRC"/* IRC */ #define CLIENTTAG_IIRC_UINT 0x49495243 /* Westwood Online tags: */ #define CLIENTTAG_WCHAT "WCHT" /* Westwood Chat */ #define CLIENTTAG_WCHAT_UINT 0x57434854 #define CLIENTTAG_TIBERNSUN "TSUN" /* Tiberian Sun */ #define CLIENTTAG_TIBERNSUN_UINT 0x5453554E #define CLIENTTAG_TIBSUNXP "TSXP" /* Tiberian Sun Extension Pack */ #define CLIENTTAG_TIBSUNXP_UINT 0x54535850 #define CLIENTTAG_REDALERT "RALT" /* Red Alert 1 */ #define CLIENTTAG_REDALERT_UINT 0x52414C54 #define CLIENTTAG_REDALERT2 "RAL2" /* Red Alert 2 */ #define CLIENTTAG_REDALERT2_UINT 0x52414C32 #define CLIENTTAG_DUNE2000 "DN2K" /* Dune 2000 */ #define CLIENTTAG_DUNE2000_UINT 0x444E324B #define CLIENTTAG_NOX "NOXX" /* NOX */ #define CLIENTTAG_NOX_UINT 0x4E4F5858 #define CLIENTTAG_NOXQUEST "NOXQ" /* NOX Quest*/ #define CLIENTTAG_NOXQUEST_UINT 0x4E4F5851 #define CLIENTTAG_RENEGADE "RNGD" /* Renegade */ #define CLIENTTAG_RENEGADE_UINT 0x524E4744 #define CLIENTTAG_RENGDFDS "RFDS" /* Renegade Free Dedicated Server */ #define CLIENTTAG_RENGDFDS_UINT 0x52464453 #define CLIENTTAG_YURISREV "YURI" /* Yuri's Revenge */ #define CLIENTTAG_YURISREV_UINT 0x59555249 #define CLIENTTAG_EMPERORBD "EBFD" /* Emperor: Battle for Dune */ #define CLIENTTAG_EMPERORBD_UINT 0x45424644 #define CLIENTTAG_LOFLORE3 "LOR3" /* Lands of Lore 3 */ #define CLIENTTAG_LOFLORE3_UINT 0x4C4F5233 #define CLIENTTAG_WWOL "WWOL" /* Other Westwood Online games */ #define CLIENTTAG_WWOL_UINT 0x57574F4C /* BNETD-specific software tags - we try to use lowercase to avoid collisions */ #define CLIENTTAG_FREECRAFT "free" /* FreeCraft http://www.freecraft.com/ */ #define CLIENTTAG_UNKNOWN "UNKN" #define CLIENTTAG_UNKNOWN_UINT 0x554E4B4E /* Architecture tags */ #define ARCHTAG_WINX86 "IX86" /* MS Windows on Intel x86 */ #define ARCHTAG_MACPPC "PMAC" /* MacOS on PowerPC */ #define ARCHTAG_OSXPPC "XMAC" /* MacOS X on PowerPC */ #define ARCHTAG_WINX86_UINT 0x49583836 /* IX86 */ #define ARCHTAG_MACPPC_UINT 0x504D4143 /* PMAC */ #define ARCHTAG_OSXPPC_UINT 0x584D4143 /* XMAC */ /* game languages */ #define GAMELANG_ENGLISH_UINT 0x656E5553 /* enUS */ #define GAMELANG_GERMAN_UINT 0x64654445 /* deDE */ #define GAMELANG_CZECH_UINT 0x6373435A /* csCZ */ #define GAMELANG_SPANISH_UINT 0x65734553 /* esES */ #define GAMELANG_FRENCH_UINT 0x66724652 /* frFR */ #define GAMELANG_ITALIAN_UINT 0x69744954 /* itIT */ #define GAMELANG_JAPANESE_UINT 0x6A614A41 /* jaJA */ #define GAMELANG_KOREAN_UINT 0x6B6F4B52 /* koKR */ #define GAMELANG_POLISH_UINT 0x706C504C /* plPL */ #define GAMELANG_RUSSIAN_UINT 0x72755255 /* ruRU */ #define GAMELANG_CHINESE_S_UINT 0x7A68434E /* zhCN */ #define GAMELANG_CHINESE_T_UINT 0x7A685457 /* zhTW */ #define TAG_UNKNOWN_UINT 0x554E4B4E /* UNKN */ #define TAG_UNKNOWN "UNKN" /* Server tag */ #define BNETTAG "bnet" /* Battle.net */ /* Filetype tags (note these are "backwards") */ #define EXTENSIONTAG_PCX "xcp." #define EXTENSIONTAG_SMK "kms." #define EXTENSIONTAG_MNG "gnm." namespace pvpgn { extern t_clienttag clienttag_str_to_uint(char const * clienttag); extern char const * clienttag_uint_to_str(t_clienttag clienttag); extern char const * clienttag_get_title(t_clienttag clienttag); extern t_tag tag_str_to_uint(char const * tag_str); extern t_tag tag_case_str_to_uint(char const * tag_str); extern const char * tag_uint_to_str(char * tag_str, t_tag tag_uint); extern std::string tag_uint_to_str2(t_tag tag_uint); extern const char * tag_uint_to_revstr(char * tag_str, t_tag tag_uint); extern int tag_check_arch(t_tag tag_uint); extern int tag_check_client(t_tag tag_uint); extern int tag_check_wolv1(t_tag tag_uint); extern int tag_check_wolv2(t_tag tag_uint); extern int tag_check_in_list(t_clienttag clienttag, const char * list); extern t_clienttag tag_sku_to_uint(int sku); extern t_clienttag tag_channeltype_to_uint(int channeltype); extern t_tag tag_wol_locale_to_uint(int locale); extern t_clienttag tag_validate_client(char const * client); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/token.cpp000066400000000000000000000040401462640461400201010ustar00rootroot00000000000000/* * Copyright (C) 2001 Rob Crittenden (rcrit@greyoak.com) * Copyright (C) 2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //#define PREFS_INTERNAL_ACCESS #include "common/setup_before.h" #include "token.h" #include #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { /* * Given a string and an integer pointer skip past pos characters and return * the next white-space delimited string, setting pos to the new position. */ extern char * next_token(char * ptr, unsigned int * pos) { unsigned int i; unsigned int start; int quoted; if (!ptr || !pos) return NULL; /* skip leading whitespace */ for (i = *pos; std::isspace((int)ptr[i]); i++); if (ptr[i] == '\0') return NULL; /* if after whitespace, we're done */ if (ptr[i] == '"') { quoted = 1; i++; } else quoted = 0; start = i; for (;;) { if (ptr[i] == '\0') break; if (quoted) /* FIXME: add handling of escape chars so quotes can be in tokens */ { if (ptr[i] == '"') break; } else if (std::isspace((int)ptr[i])) break; i++; } if (ptr[i] != '\0') { ptr[i] = '\0'; /* terminate the string */ *pos = i + 1; /* remember the position of the next char */ } else *pos = i; /* this was the last token, just remember the NUL */ return &ptr[start]; } } pvpgn-1.99.7.2.1+dfsg/src/common/token.h000066400000000000000000000017021462640461400175500ustar00rootroot00000000000000/* * Copyright (C) 2001 Rob Crittenden (rcrit@greyoak.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TOKEN_PROTOS #define INCLUDED_TOKEN_PROTOS namespace pvpgn { extern char * next_token(char * ptr, unsigned int * pos); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/tracker.h000066400000000000000000000054671462640461400200770ustar00rootroot00000000000000/* * Copyright (C) 1999 Mark Baysinger (mbaysing@ucsd.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_TRACKER_TYPES #define INCLUDED_TRACKER_TYPES #ifdef TRACKER_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES #include "common/bn_type.h" #else #define JUST_NEED_TYPES #include "common/bn_type.h" #undef JUST_NEED_TYPES #endif #endif namespace pvpgn { /* * * Here's how this works: * * Bnetd sends a UDP ClientPacket to each track server port on a regular basis. * Simple. * */ #ifdef TRACKER_INTERNAL_ACCESS typedef struct { bn_short packet_version; /* set to TRACK_VERSION, network byte order */ bn_short port; /* port server is listening on, network byte order */ bn_int flags; /* see below, network byte order */ bn_byte software[32]; /* example: Bnetd, NUL terminated */ bn_byte version[16]; /* example: 0.4, NUL terminated */ bn_byte platform[32]; /* Windows, Linux, etc., NUL terminated */ bn_byte server_desc[64]; /* description, NUL terminated */ bn_byte server_location[64]; /* geographical location, NUL terminated */ bn_byte server_url[96]; /* web address: http://..., NUL terminated */ bn_byte contact_name[64]; /* name of operator, NUL terminated */ bn_byte contact_email[64]; /* e-mail address of operator, NUL terminated */ bn_int users; /* current number of users, network byte order */ bn_int channels; /* current number of channels, network byte order */ bn_int games; /* current number of games, network byte order */ bn_int uptime; /* daemon uptime in seconds, network byte order */ bn_int total_games; /* total number of games served */ bn_int total_logins; /* total number of client logins */ /* new versions will add fields to end of packet */ } t_trackpacket; /* packet_version */ #define TRACK_VERSION 2 /* flags */ #define TF_SHUTDOWN 0x1 /* send packet with this flag set when * shutting down (Currently ignored) */ #define TF_PRIVATE 0x2 /* server is private and should not be * listed (Currently ignored) */ #endif } #endif pvpgn-1.99.7.2.1+dfsg/src/common/trans.cpp000066400000000000000000000240511462640461400201140ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define TRANS_INTERNAL_ACCESS #include "trans.h" #include #include #include "common/setup_before.h" #include "common/eventlog.h" #include "common/list.h" #include "common/addr.h" #include "common/util.h" #include "common/xalloc.h" #include "common/setup_after.h" #define DEBUG_TRANS namespace pvpgn { static t_list * trans_head = NULL; extern int trans_load(char const * filename, int program) { std::FILE *fp; unsigned int line; unsigned int pos; char *buff; char *temp; char *input; char const *output; char const *exclude; char const *include; unsigned int npos; char *network; char *tmp; char tmp1[32]; char tmp2[32]; char tmp3[32]; t_trans *entry; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } if (!(fp = std::fopen(filename, "r"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } trans_head = list_create(); for (line = 1; (buff = file_get_line(fp)); line++) { for (pos = 0; buff[pos] == '\t' || buff[pos] == ' '; pos++); if (buff[pos] == '\0' || buff[pos] == '#') { continue; } if ((temp = std::strrchr(buff, '#'))) { unsigned int len; unsigned int endpos; *temp = '\0'; len = std::strlen(buff) + 1; for (endpos = len - 1; buff[endpos] == '\t' || buff[endpos] == ' '; endpos--); buff[endpos + 1] = '\0'; } if (!(input = std::strtok(buff, " \t"))) { /* std::strtok modifies the string it is passed */ eventlog(eventlog_level_error, __FUNCTION__, "missing input line {} of file \"{}\"", line, filename); continue; } /* check for port number - this tells us what programs will use this entry */ if (!(temp = std::strrchr(input, ':'))) { eventlog(eventlog_level_error, __FUNCTION__, "missing port # on input line {} of file \"{}\"", line, filename); continue; } temp++; /* bnetd doesn't want the port 4000 entries */ if (program == TRANS_BNETD && std::strcmp(temp, "4000") == 0) { #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "d2gs input (ignoring) \"{}\"", input); #endif continue; } /* d2cs only wants the port 4000 entries */ if (program == TRANS_D2CS && std::strcmp(temp, "4000") != 0) { #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "non d2gs input (ignoring) \"{}\"", input); #endif continue; } if (!(output = std::strtok(NULL, " \t"))) { eventlog(eventlog_level_error, __FUNCTION__, "missing output on line {} of file \"{}\"", line, filename); continue; } if (!(exclude = std::strtok(NULL, " \t"))) { eventlog(eventlog_level_error, __FUNCTION__, "missing exclude on line {} of file \"{}\"", line, filename); continue; } if (!(include = std::strtok(NULL, " \t"))) { eventlog(eventlog_level_error, __FUNCTION__, "missing include on line {} of file \"{}\"", line, filename); continue; } /* add exlude networks */ tmp = xstrdup(exclude); npos = 0; while (tmp[npos]) { network = &tmp[npos]; for (; tmp[npos] != ',' && tmp[npos] != '\0'; npos++); if (tmp[npos] == '\0') npos--; else tmp[npos] = '\0'; if (std::strcmp(network, "NONE") == 0) { npos++; continue; } entry = (t_trans*)xmalloc(sizeof(t_trans)); if (!(entry->input = addr_create_str(input, 0, 0))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for input address"); xfree(entry); npos++; continue; } if (!(entry->output = addr_create_str(input, 0, 0))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for output address"); addr_destroy(entry->input); xfree(entry); npos++; continue; } if (std::strcmp(network, "ANY") == 0) { if (!(entry->network = netaddr_create_str("0.0.0.0/0"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for network address"); addr_destroy(entry->output); addr_destroy(entry->input); xfree(entry); npos++; continue; } } else { if (!(entry->network = netaddr_create_str(network))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for network address"); addr_destroy(entry->output); addr_destroy(entry->input); xfree(entry); npos++; continue; } } #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "Adding Host -> {}, Output -> {}, Network {} - (exclude)", addr_get_addr_str(entry->input, tmp1, sizeof(tmp1)), addr_get_addr_str(entry->output, tmp2, sizeof(tmp2)), netaddr_get_addr_str(entry->network, tmp3, sizeof(tmp3))); #endif list_append_data(trans_head, entry); npos++; } xfree(tmp); /* add include networks */ tmp = xstrdup(include); npos = 0; while (tmp[npos]) { network = &tmp[npos]; for (; tmp[npos] != ',' && tmp[npos] != '\0'; npos++); if (tmp[npos] == '\0') npos--; else tmp[npos] = '\0'; if (std::strcmp(network, "NONE") == 0) { npos++; continue; } entry = (t_trans*)xmalloc(sizeof(t_trans)); if (!(entry->input = addr_create_str(input, 0, 0))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for input address"); xfree(entry); npos++; continue; } if (!(entry->output = addr_create_str(output, 0, 0))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for output address"); addr_destroy(entry->input); xfree(entry); npos++; continue; } if (std::strcmp(network, "ANY") == 0) { if (!(entry->network = netaddr_create_str("0.0.0.0/0"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for network address"); addr_destroy(entry->output); addr_destroy(entry->input); xfree(entry); npos++; continue; } } else { if (!(entry->network = netaddr_create_str(network))) { eventlog(eventlog_level_error, __FUNCTION__, "could not allocate memory for network address"); addr_destroy(entry->output); addr_destroy(entry->input); xfree(entry); npos++; continue; } } #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "Adding Host -> {}, Output -> {}, Network {} - (include)", addr_get_addr_str(entry->input, tmp1, sizeof(tmp1)), addr_get_addr_str(entry->output, tmp2, sizeof(tmp2)), netaddr_get_addr_str(entry->network, tmp3, sizeof(tmp3))); #endif list_append_data(trans_head, entry); npos++; } xfree(tmp); } file_get_line(NULL); // clear file_get_line buffer std::fclose(fp); eventlog(eventlog_level_info, __FUNCTION__, "trans file loaded"); return 0; } extern int trans_unload(void) { t_elem *curr; t_trans *entry; if (trans_head) { LIST_TRAVERSE(trans_head, curr) { if (!(entry = (t_trans*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); } else { netaddr_destroy(entry->network); addr_destroy(entry->output); addr_destroy(entry->input); xfree(entry); } list_remove_elem(trans_head, &curr); } list_destroy(trans_head); trans_head = NULL; } return 0; } extern int trans_reload(char const * filename, int program) { trans_unload(); if (trans_load(filename, program) < 0) return -1; return 0; } extern int trans_net(unsigned int clientaddr, unsigned int *addr, unsigned short *port) { t_elem const *curr; t_trans *entry; char temp1[32]; char temp2[32]; char temp3[32]; char temp4[32]; #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "checking {} for client {} ...", addr_num_to_addr_str(*addr, *port), addr_num_to_ip_str(clientaddr)); #endif if (trans_head) { LIST_TRAVERSE_CONST(trans_head, curr) { if (!(entry = (t_trans*)elem_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL entry in list"); continue; } #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "against entry -> {} output {} network {}", addr_get_addr_str(entry->input, temp1, sizeof(temp1)), addr_get_addr_str(entry->output, temp2, sizeof(temp2)), netaddr_get_addr_str(entry->network, temp3, sizeof(temp3))); #endif if (addr_get_ip(entry->input) != *addr || addr_get_port(entry->input) != *port) { #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "entry does match input address"); #endif continue; } if (netaddr_contains_addr_num(entry->network, clientaddr) == 0) { #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "client is not in the correct network"); #endif continue; } #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "{} translated to {}", addr_num_to_addr_str(*addr, *port), addr_get_addr_str(entry->output, temp4, sizeof(temp4))); #endif *addr = addr_get_ip(entry->output); *port = addr_get_port(entry->output); return 1; /* match found in list */ } } #ifdef DEBUG_TRANS eventlog(eventlog_level_debug, __FUNCTION__, "no match found for {} (not translated)", addr_num_to_addr_str(*addr, *port)); #endif return 0; /* no match found in list */ } } pvpgn-1.99.7.2.1+dfsg/src/common/trans.h000066400000000000000000000030111462640461400175520ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_TRANS_TYPES #define INCLUDED_TRANS_TYPES #ifdef TRANS_INTERNAL_ACCESS #ifdef JUST_NEED_TYPES # include "common/addr.h" #else # define JUST_NEED_TYPES # include "common/addr.h" # undef JUST_NEED_TYPES #endif namespace pvpgn { typedef struct { t_addr *input; t_addr *output; t_netaddr *network; } t_trans; } #endif #endif #ifndef JUST_NEED_TYPES #ifndef INCLUDED_TRANS_PROTOS #define INCLUDED_TRANS_PROTOS #define TRANS_BNETD 1 #define TRANS_D2CS 2 namespace pvpgn { extern int trans_load(char const * filename, int program); extern int trans_unload(void); extern int trans_reload(char const * filename, int program); extern int trans_net(unsigned int clientaddr, unsigned int *addr, unsigned short *port); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/udp_protocol.h000066400000000000000000000060301462640461400211400ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_UDP_PROTOCOL_TYPES #define INCLUDED_UDP_PROTOCOL_TYPES namespace pvpgn { /* * This file describes the UDP chat packets. The game packets aren't * in here and would probably go in another file. The first 4 bytes * seem to be a little-endian 32 bit integer id for the packet type. * FIXME: add more UDP packet types */ /******************************************************/ typedef struct { bn_int type; } PACKED_ATTR() t_udp_header; /******************************************************/ /******************************************************/ typedef struct { t_udp_header h; } PACKED_ATTR() t_udp_generic; /******************************************************/ /******************************************************/ /* client echos back with UDPOK packet */ /* 05 00 00 00 74 65 6E 62 ....tenb */ #define SERVER_UDPTEST 0x00000005 typedef struct { t_udp_header h; bn_int bnettag; /* 74 65 6E 62 */ } PACKED_ATTR() t_server_udptest; /******************************************************/ /******************************************************/ /* 07 00 00 00 49 41 19 00 ....IA.. 07 00 00 00 11 60 1D 00 .....`.. */ #define CLIENT_UDPPING 0x00000007 typedef struct { t_udp_header h; bn_int unknown1; /* time? */ } PACKED_ATTR() t_client_udpping; /******************************************************/ /******************************************************/ /* From Brood War 1.04 08 00 00 00 75 EC A0 28 ....u..( */ #define CLIENT_SESSIONADDR1 0x00000008 typedef struct { t_udp_header h; bn_int sessionkey; } PACKED_ATTR() t_client_sessionaddr1; /******************************************************/ /******************************************************/ /* From Brood War 1.07 09 00 00 00 7A 11 07 ED 9D DF 01 00 ....z....... */ #define CLIENT_SESSIONADDR2 0x00000009 typedef struct { t_udp_header h; bn_int sessionkey; bn_int sessionnum; } PACKED_ATTR() t_client_sessionaddr2; /******************************************************/ } #endif pvpgn-1.99.7.2.1+dfsg/src/common/util.cpp000066400000000000000000000324421462640461400177450ustar00rootroot00000000000000/* * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu) * Copyright (C) 1998,1999,2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/util.h" #include #include #include #include #include #include "compat/strcasecmp.h" #include "compat/strncasecmp.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { extern int strstart(char const * full, char const * part) { std::size_t strlen_part; int compare_result; if (!full || !part) return 1; strlen_part = std::strlen(part); compare_result = strncasecmp(full, part, strlen_part); /* If there is more than the command, make sure it is separated */ if (compare_result != 0) return compare_result; else if (full[strlen_part] != ' ' && full[strlen_part] != '\0') return 1; else return compare_result; } #define DEF_LEN 64 #define INC_LEN 16 extern char * file_get_line(std::FILE * fp) { static char * line = NULL; static unsigned int len = 0; unsigned int pos = 0; int prev_char, curr_char; // use file_get_line with NULL argument to clear the buffer if (!(fp)) { len = 0; if ((line)) xfree((void *)line); line = NULL; return NULL; } if (!(line)) { line = (char*)xmalloc(DEF_LEN); len = DEF_LEN; } prev_char = '\0'; while ((curr_char = std::fgetc(fp)) != EOF) { if (((char)curr_char) == '\r') continue; /* make DOS line endings look Unix-like */ if (((char)curr_char) == '\n') { if (pos < 1 || ((char)prev_char) != '\\') break; pos--; /* throw away the backslash */ prev_char = '\0'; continue; } prev_char = curr_char; line[pos++] = (char)curr_char; if ((pos + 1) >= len) { len += INC_LEN; line = (char*)xrealloc(line, len); } } if (curr_char == EOF && pos < 1) /* not even an empty line */ { return NULL; } line[pos] = '\0'; return line; } extern char * strreverse(char * str) { std::reverse(str, str + std::strlen(str)); return str; } extern int str_to_uint(char const * str, unsigned int * num) { unsigned int val; unsigned int pval; char * pos; if (!str || !num) return -1; for (pos = (char *)str; *pos == ' ' || *pos == '\t'; pos++); if (*pos == '+') pos++; val = 0; for (; *pos != '\0'; pos++) { pval = val; val *= 10; if (val / 10 != pval) /* check for overflow */ return -1; pval = val; if (std::isdigit(*pos)) val += *pos - '0'; else return -1; if (val < pval) /* check for overflow */ return -1; } *num = val; return 0; } extern int str_to_ushort(char const * str, unsigned short * num) { unsigned short val; unsigned short pval; char * pos; if (!str || !num) return -1; for (pos = (char *)str; *pos == ' ' || *pos == '\t'; pos++); if (*pos == '+') pos++; val = 0; for (; *pos != '\0'; pos++) { pval = val; val *= 10; if (val / 10 != pval) /* check for overflow */ return -1; pval = val; if (std::isdigit(*pos)) val += *pos - '0'; else return -1; if (val < pval) /* check for overflow */ return -1; } *num = val; return 0; } /* This routine assumes ASCII like control chars. If len is zero, it will print all characters up to the first NUL, otherwise it will print exactly that many characters. */ int str_print_term(std::FILE * fp, char const * str, unsigned int len, int allow_nl) { unsigned int i; if (!fp) return -1; if (!str) return -1; if (len == 0) len = std::strlen(str); for (i = 0; i < len; i++) if ((str[i] == '\177' || (str[i] >= '\000' && str[i] < '\040')) && (!allow_nl || (str[i] != '\r' && str[i] != '\n'))) std::fprintf(fp, "^%c", str[i] + 64); else std::fputc((int)str[i], fp); return 0; } extern int str_get_bool(char const * str) { if (!str) return -1; if (strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 || std::strcmp(str, "1") == 0) return 1; if (strcasecmp(str, "false") == 0 || strcasecmp(str, "no") == 0 || strcasecmp(str, "off") == 0 || std::strcmp(str, "0") == 0) return 0; return -1; } extern char const * seconds_to_timestr(unsigned int totsecs) { static char temp[256]; int days; int hours; int minutes; int seconds; days = totsecs / (24 * 60 * 60); hours = (totsecs / (60 * 60)) % 24; minutes = (totsecs / 60) % 60; seconds = totsecs % 60; if (days > 0) std::sprintf(temp, "%d day%s %d hour%s %d minute%s %d second%s", days, days == 1 ? "" : "s", hours, hours == 1 ? "" : "s", minutes, minutes == 1 ? "" : "s", seconds, seconds == 1 ? "" : "s"); else if (hours > 0) std::sprintf(temp, "%d hour%s %d minute%s %d second%s", hours, hours == 1 ? "" : "s", minutes, minutes == 1 ? "" : "s", seconds, seconds == 1 ? "" : "s"); else if (minutes > 0) std::sprintf(temp, "%d minute%s %d second%s", minutes, minutes == 1 ? "" : "s", seconds, seconds == 1 ? "" : "s"); else std::sprintf(temp, "%d second%s.", seconds, seconds == 1 ? "" : "s"); return temp; } extern int clockstr_to_seconds(char const * clockstr, unsigned int * totsecs) { unsigned int i, j; unsigned int temp; if (!clockstr) return -1; if (!totsecs) return -1; for (i = j = temp = 0; j < std::strlen(clockstr); j++) { switch (clockstr[j]) { case ':': temp *= 60; temp += std::strtoul(&clockstr[i], NULL, 10); i = j + 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; default: return -1; } } if (i < j) { temp *= 60; temp += std::strtoul(&clockstr[i], NULL, 10); } *totsecs = temp; return 0; } extern char * escape_fs_chars(char const * in, unsigned int len) { char * out; unsigned int inpos; unsigned int outpos; if (!in) return NULL; out = (char*)xmalloc(len * 3 + 1); /* if all turn into %XX */ for (inpos = 0, outpos = 0; inpos < len; inpos++) { if (in[inpos] == '\0' || in[inpos] == '%' || in[inpos] == '/' || in[inpos] == '\\' || in[inpos] == ':') /* FIXME: what other characters does Windows not allow? */ { out[outpos++] = '%'; /* always 00 through FF hex */ std::sprintf(&out[outpos], "%02X", (unsigned int)(unsigned char)in[inpos]); outpos += 2; } else out[outpos++] = in[inpos]; } /* if outpos >= len*3+1 then the buffer was overflowed */ out[outpos] = '\0'; return out; } extern char * escape_chars(char const * in, unsigned int len) { char * out; unsigned int inpos; unsigned int outpos; if (!in) return NULL; out = (char*)xmalloc(len * 4 + 1); /* if all turn into \xxx */ for (inpos = 0, outpos = 0; inpos < len; inpos++) { if (in[inpos] == '\\') { out[outpos++] = '\\'; out[outpos++] = '\\'; } else if (in[inpos] == '"') { out[outpos++] = '\\'; out[outpos++] = '"'; } else if (std::isprint((unsigned char)in[inpos])) out[outpos++] = in[inpos]; else if (in[inpos] == '\a') { out[outpos++] = '\\'; out[outpos++] = 'a'; } else if (in[inpos] == '\b') { out[outpos++] = '\\'; out[outpos++] = 'b'; } else if (in[inpos] == '\t') { out[outpos++] = '\\'; out[outpos++] = 't'; } else if (in[inpos] == '\n') { out[outpos++] = '\\'; out[outpos++] = 'n'; } else if (in[inpos] == '\v') { out[outpos++] = '\\'; out[outpos++] = 'v'; } else if (in[inpos] == '\f') { out[outpos++] = '\\'; out[outpos++] = 'f'; } else if (in[inpos] == '\r') { out[outpos++] = '\\'; out[outpos++] = 'r'; } else { out[outpos++] = '\\'; /* always 001 through 377 octal */ std::sprintf(&out[outpos], "%03o", (unsigned int)(unsigned char)in[inpos]); outpos += 3; } } /* if outpos >= len*4+1 then the buffer was overflowed */ out[outpos] = '\0'; return out; } extern char * unescape_chars(char const * in) { char * out; unsigned int inpos; unsigned int outpos; unsigned int inlen; if (!in) return NULL; inlen = std::strlen(in); out = (char*)xmalloc(inlen + 1); for (inpos = 0, outpos = 0; inpos < inlen; inpos++) { if (in[inpos] != '\\') out[outpos++] = in[inpos]; else switch (in[++inpos]) { case '\\': out[outpos++] = '\\'; break; case '"': out[outpos++] = '"'; break; case 'a': out[outpos++] = '\a'; break; case 'b': out[outpos++] = '\b'; break; case 't': out[outpos++] = '\t'; break; case 'n': out[outpos++] = '\n'; break; case 'v': out[outpos++] = '\v'; break; case 'f': out[outpos++] = '\f'; break; case 'r': out[outpos++] = '\r'; break; default: { char temp[4]; unsigned int i; unsigned int num; for (i = 0; i < 3; i++) { if (in[inpos] != '0' && in[inpos] != '1' && in[inpos] != '2' && in[inpos] != '3' && in[inpos] != '4' && in[inpos] != '5' && in[inpos] != '6' && in[inpos] != '7') break; temp[i] = in[inpos++]; } temp[i] = '\0'; inpos--; num = std::strtoul(temp, NULL, 8); if (i < 3 || num<1 || num>255) /* bad escape (including \000), leave it as-is */ { out[outpos++] = '\\'; std::strcpy(&out[outpos], temp); outpos += std::strlen(temp); } else out[outpos++] = (unsigned char)num; } } } out[outpos] = '\0'; return out; } extern void str_to_hex(char * target, char const * data, int datalen) { unsigned char c; int i; for (i = 0; i < datalen; i++) { c = (data[i]) & 0xff; std::sprintf(target + i * 3, "%02X ", c); target[i * 3 + 3] = '\0'; /* std::fprintf(stderr, "str_to_hex %d | '%02x' '%s'\n", i, c, target); */ } } extern int hex_to_str(char const * source, char * data, int datalen) { /* * TODO: We really need a more robust function here, * for now, I'll just use this hack for a quick evaluation */ char byte; char c; int i; for (i = 0; i < datalen; i++) { byte = 0; /* std::fprintf(stderr, "hex_to_str %d | '%02x'", i, byte); */ c = source[i * 3 + 0]; byte += 16 * (c > '9' ? (c - 'A' + 10) : (c - '0')); /* std::fprintf(stderr, " | '%c' '%02x'", c, byte); */ c = source[i * 3 + 1]; byte += 1 * (c > '9' ? (c - 'A' + 10) : (c - '0')); /* std::fprintf(stderr, " | '%c' '%02x'", c, byte); */ /* std::fprintf(stderr, "\n"); */ data[i] = byte; } /* std::fprintf(stderr, "finished, returning %d from '%s'\n", i, source); */ return i; } /* convert a time string to time_t time string format is: yyyy/mm/dd or yyyy-mm-dd or yyyy.mm.dd hh:mm:ss */ extern int timestr_to_time(char const * timestr, std::time_t* ptime) { char const * p; char ch; struct std::tm when; int day_s, time_s, last; if (!timestr) return -1; if (!timestr[0]) { *ptime = 0; return 0; } p = timestr; day_s = time_s = 0; last = 0; std::memset(&when, 0, sizeof(when)); when.tm_mday = 1; when.tm_isdst = -1; while (1) { ch = *timestr; timestr++; switch (ch) { case '/': case '-': case '.': if (day_s == 0) { when.tm_year = std::atoi(p) - 1900; } else if (day_s == 1) { when.tm_mon = std::atoi(p) - 1; } else if (day_s == 2) { when.tm_mday = std::atoi(p); } time_s = 0; day_s++; p = timestr; last = 1; break; case ':': if (time_s == 0) { when.tm_hour = std::atoi(p); } else if (time_s == 1) { when.tm_min = std::atoi(p); } else if (time_s == 2) { when.tm_sec = std::atoi(p); } day_s = 0; time_s++; p = timestr; last = 2; break; case ' ': case '\t': case '\x0': if (last == 1) { if (day_s == 0) { when.tm_year = std::atoi(p) - 1900; } else if (day_s == 1) { when.tm_mon = std::atoi(p) - 1; } else if (day_s == 2) { when.tm_mday = std::atoi(p); } } else if (last == 2) { if (time_s == 0) { when.tm_hour = std::atoi(p); } else if (time_s == 1) { when.tm_min = std::atoi(p); } else if (time_s == 2) { when.tm_sec = std::atoi(p); } } time_s = day_s = 0; p = timestr; last = 0; break; default: break; } if (!ch) break; } *ptime = std::mktime(&when); return 0; } } pvpgn-1.99.7.2.1+dfsg/src/common/util.h000066400000000000000000000042051462640461400174060ustar00rootroot00000000000000/* * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*****/ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_UTIL_PROTOS #define INCLUDED_UTIL_PROTOS #include #include namespace pvpgn { extern int strstart(char const * full, char const * part); extern char * file_get_line(std::FILE * fp); extern char * strreverse(char * str); extern int str_to_uint(char const * str, unsigned int * num); extern int str_to_ushort(char const * str, unsigned short * num); extern int str_print_term(std::FILE * fp, char const * str, unsigned int len, int allow_nl); extern int str_get_bool(char const * str); extern char const * seconds_to_timestr(unsigned int totsecs); /* FIXME: can this be marked pure? */ extern int clockstr_to_seconds(char const * clockstr, unsigned int * totsecs); extern char * escape_fs_chars(char const * in, unsigned int len); extern char * escape_chars(char const * in, unsigned int len); extern char * unescape_chars(char const * in); extern void str_to_hex(char * target, char const * data, int datalen); extern int hex_to_str(char const * source, char * data, int datalen); extern int timestr_to_time(char const * timestr, std::time_t* ptime); static inline char * str_skip_space(char *str) { for (; *str == ' ' || *str == '\t'; str++); return str; } static inline char * str_skip_word(char *str) { for (; *str != ' ' && *str != '\t' && *str; str++); return str; } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/version.h000066400000000000000000000020761462640461400201220ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_COMMON_VERSION_H #define INCLUDED_COMMON_VERSION_H #define WIDEN(quote) WIDEN2(quote) #define WIDEN2(quote) L##quote #ifndef PVPGN_VERSION #define PVPGN_VERSION "1.99.7.2.1-PRO" #define PVPGN_VERSIONW WIDEN(PVPGN_VERSION) #endif #ifndef PVPGN_SOFTWARE #define PVPGN_SOFTWARE "PvPGN" #define PVPGN_SOFTWAREW WIDEN(PVPGN_SOFTWARE) #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/wol_gameres_protocol.h000066400000000000000000000310371462640461400226610ustar00rootroot00000000000000/* * Copyright (C) 2008 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_WOL_GAMERES_PROTOCOL_TYPES #define INCLUDED_WOL_GAMERES_PROTOCOL_TYPES #ifdef JUST_NEED_TYPES # include "common/bn_type.h" # include "common/tag.h" #else # define JUST_NEED_TYPES # include "common/bn_type.h" # include "common/tag.h" # undef JUST_NEED_TYPES #endif /* * The "wol gamreres" protocol is used for sending Game reports to server * for ladders support. */ namespace pvpgn { /******************************************************/ typedef struct { bn_short size; bn_short rngd_size; } PACKED_ATTR() t_wolgameres_header; /******************************************************/ /******************************************************/ typedef struct { t_wolgameres_header h; } PACKED_ATTR() t_wolgameres_generic; /******************************************************/ /* PELISH: not used now... typedef struct { bn_short type; bn_short length; // data; } PACKED_ATTR() t_client_wolgameres_generic; */ /* DATA TYPES */ const int DATA_TYPE_BYTE = 1; const int DATA_TYPE_BOOL = 2; const int DATA_TYPE_TIME = 5; const int DATA_TYPE_INT = 6; const int DATA_TYPE_STRING = 7; const int DATA_TYPE_BIGINT = 20; /* Game resolution tags */ const t_tag CLIENT_SERN_UINT = 0x53455223; const t_tag CLIENT_SDFX_UINT = 0x53444658; const t_tag CLIENT_IDNO_UINT = 0x49444E4F; const t_tag CLIENT_GSKU_UINT = 0x47534B55; const t_tag CLIENT_DCON_UINT = 0x44434F4E; const t_tag CLIENT_LCON_UINT = 0x4C434F4E; const t_tag CLIENT_TYPE_UINT = 0x54595045; const t_tag CLIENT_TRNY_UINT = 0x54524E59; const t_tag CLIENT_OOSY_UINT = 0x4F4F5359; const t_tag CLIENT_FINI_UINT = 0x46494E49; const t_tag CLIENT_DURA_UINT = 0x44555241; const t_tag CLIENT_CRED_UINT = 0x43524544; const t_tag CLIENT_SHRT_UINT = 0x53485254; const t_tag CLIENT_SUPR_UINT = 0x53555052; const t_tag CLIENT_MODE_UINT = 0x4D4F4445; const t_tag CLIENT_BAMR_UINT = 0x42414D52; const t_tag CLIENT_CRAT_UINT = 0x43524154; const t_tag CLIENT_AIPL_UINT = 0x4149504C; const t_tag CLIENT_UNIT_UINT = 0x554E4954; const t_tag CLIENT_SCEN_UINT = 0x5343454E; const t_tag CLIENT_CMPL_UINT = 0x434D504C; const t_tag CLIENT_PNGS_UINT = 0x504E4753; const t_tag CLIENT_PNGR_UINT = 0x504E4752; const t_tag CLIENT_PLRS_UINT = 0x504C5253; const t_tag CLIENT_SPID_UINT = 0x53504944; const t_tag CLIENT_TIME_UINT = 0x54494D45; const t_tag CLIENT_AFPS_UINT = 0x41465053; const t_tag CLIENT_PROC_UINT = 0x50524F43; const t_tag CLIENT_MEMO_UINT = 0x4D454D4F; const t_tag CLIENT_VIDM_UINT = 0x5649444D; const t_tag CLIENT_SPED_UINT = 0x53504544; const t_tag CLIENT_VERS_UINT = 0x56455253; const t_tag CLIENT_DATE_UINT = 0x44415445; const t_tag CLIENT_BASE_UINT = 0x42415345; const t_tag CLIENT_TIBR_UINT = 0x54494252; const t_tag CLIENT_SHAD_UINT = 0x53484144; const t_tag CLIENT_FLAG_UINT = 0x464C4147; const t_tag CLIENT_TECH_UINT = 0x54454348; const t_tag CLIENT_BROK_UINT = 0x42524f4b; const t_tag CLIENT_ACCO_UINT = 0x4143434f; const t_tag CLIENT_ETIM_UINT = 0x4554494d; const t_tag CLIENT_PSPD_UINT = 0x50535044; const t_tag CLIENT_SMEM_UINT = 0x534d454d; const t_tag CLIENT_SVID_UINT = 0x53564944; const t_tag CLIENT_SNAM_UINT = 0x534e414d; const t_tag CLIENT_GMAP_UINT = 0x474d4150; const t_tag CLIENT_DSVR_UINT = 0x44535652; /* RNDG Player tags */ const t_tag CLIENT_PNAM_UINT = 0x504e414d; const t_tag CLIENT_PLOC_UINT = 0x504c4f43; const t_tag CLIENT_TEAM_UINT = 0x5445414d; const t_tag CLIENT_PSCR_UINT = 0x50534352; const t_tag CLIENT_PPTS_UINT = 0x50505453; const t_tag CLIENT_PTIM_UINT = 0x5054494d; const t_tag CLIENT_PHLT_UINT = 0x50484c54; const t_tag CLIENT_PKIL_UINT = 0x504b494c; const t_tag CLIENT_EKIL_UINT = 0x454b494c; const t_tag CLIENT_AKIL_UINT = 0x414b494c; const t_tag CLIENT_SHOT_UINT = 0x53484f54; const t_tag CLIENT_HEDF_UINT = 0x48454446; const t_tag CLIENT_TORF_UINT = 0x544f5246; const t_tag CLIENT_ARMF_UINT = 0x41524d46; const t_tag CLIENT_LEGF_UINT = 0x4c454746; const t_tag CLIENT_CRTF_UINT = 0x43525446; const t_tag CLIENT_PUPS_UINT = 0x50555053; const t_tag CLIENT_VKIL_UINT = 0x564b494c; const t_tag CLIENT_VTIM_UINT = 0x5654494d; const t_tag CLIENT_NKFV_UINT = 0x4e4b4656; const t_tag CLIENT_SQUI_UINT = 0x53515549; const t_tag CLIENT_PCRD_UINT = 0x50435244; const t_tag CLIENT_BKIL_UINT = 0x424b494c; const t_tag CLIENT_HEDR_UINT = 0x48454452; const t_tag CLIENT_TORR_UINT = 0x544f5252; const t_tag CLIENT_ARMR_UINT = 0x41524d52; const t_tag CLIENT_LEGR_UINT = 0x4c454752; const t_tag CLIENT_CRTR_UINT = 0x43525452; const t_tag CLIENT_FLGC_UINT = 0x464c4743; /* Player tags */ const t_tag CLIENT_NAM0_UINT = 0x4E414D30; const t_tag CLIENT_NAM1_UINT = 0x4E414D31; const t_tag CLIENT_NAM2_UINT = 0x4E414D32; const t_tag CLIENT_NAM3_UINT = 0x4E414D33; const t_tag CLIENT_NAM4_UINT = 0x4E414D34; const t_tag CLIENT_NAM5_UINT = 0x4E414D35; const t_tag CLIENT_NAM6_UINT = 0x4E414D36; const t_tag CLIENT_NAM7_UINT = 0x4E414D37; const t_tag CLIENT_IPA0_UINT = 0x49504130; const t_tag CLIENT_IPA1_UINT = 0x49504131; const t_tag CLIENT_IPA2_UINT = 0x49504132; const t_tag CLIENT_IPA3_UINT = 0x49504133; const t_tag CLIENT_IPA4_UINT = 0x49504134; const t_tag CLIENT_IPA5_UINT = 0x49504135; const t_tag CLIENT_IPA6_UINT = 0x49504136; const t_tag CLIENT_IPA7_UINT = 0x49504137; const t_tag CLIENT_CID0_UINT = 0x43494430; const t_tag CLIENT_CID1_UINT = 0x43494431; const t_tag CLIENT_CID2_UINT = 0x43494432; const t_tag CLIENT_CID3_UINT = 0x43494433; const t_tag CLIENT_CID4_UINT = 0x43494434; const t_tag CLIENT_CID5_UINT = 0x43494435; const t_tag CLIENT_CID6_UINT = 0x43494436; const t_tag CLIENT_CID7_UINT = 0x43494437; const t_tag CLIENT_SID0_UINT = 0x53494430; const t_tag CLIENT_SID1_UINT = 0x53494431; const t_tag CLIENT_SID2_UINT = 0x53494432; const t_tag CLIENT_SID3_UINT = 0x53494433; const t_tag CLIENT_SID4_UINT = 0x53494434; const t_tag CLIENT_SID5_UINT = 0x53494435; const t_tag CLIENT_SID6_UINT = 0x53494436; const t_tag CLIENT_SID7_UINT = 0x53494437; const t_tag CLIENT_TID0_UINT = 0x54494430; const t_tag CLIENT_TID1_UINT = 0x54494431; const t_tag CLIENT_TID2_UINT = 0x54494432; const t_tag CLIENT_TID3_UINT = 0x54494433; const t_tag CLIENT_TID4_UINT = 0x54494434; const t_tag CLIENT_TID5_UINT = 0x54494435; const t_tag CLIENT_TID6_UINT = 0x54494436; const t_tag CLIENT_TID7_UINT = 0x54494437; const t_tag CLIENT_CMP0_UINT = 0x434D5030; const t_tag CLIENT_CMP1_UINT = 0x434D5031; const t_tag CLIENT_CMP2_UINT = 0x434D5032; const t_tag CLIENT_CMP3_UINT = 0x434D5033; const t_tag CLIENT_CMP4_UINT = 0x434D5034; const t_tag CLIENT_CMP5_UINT = 0x434D5035; const t_tag CLIENT_CMP6_UINT = 0x434D5036; const t_tag CLIENT_CMP7_UINT = 0x434D5037; const t_tag CLIENT_COL0_UINT = 0x434f4c30; const t_tag CLIENT_COL1_UINT = 0x434f4c31; const t_tag CLIENT_COL2_UINT = 0x434f4c32; const t_tag CLIENT_COL3_UINT = 0x434f4c33; const t_tag CLIENT_COL4_UINT = 0x434f4c34; const t_tag CLIENT_COL5_UINT = 0x434f4c35; const t_tag CLIENT_COL6_UINT = 0x434f4c36; const t_tag CLIENT_COL7_UINT = 0x434f4c37; const t_tag CLIENT_CRD0_UINT = 0x43524430; const t_tag CLIENT_CRD1_UINT = 0x43524431; const t_tag CLIENT_CRD2_UINT = 0x43524432; const t_tag CLIENT_CRD3_UINT = 0x43524433; const t_tag CLIENT_CRD4_UINT = 0x43524434; const t_tag CLIENT_CRD5_UINT = 0x43524435; const t_tag CLIENT_CRD6_UINT = 0x43524436; const t_tag CLIENT_CRD7_UINT = 0x43524437; const t_tag CLIENT_INB0_UINT = 0x494e4230; const t_tag CLIENT_INB1_UINT = 0x494e4231; const t_tag CLIENT_INB2_UINT = 0x494e4232; const t_tag CLIENT_INB3_UINT = 0x494e4233; const t_tag CLIENT_INB4_UINT = 0x494e4234; const t_tag CLIENT_INB5_UINT = 0x494e4235; const t_tag CLIENT_INB6_UINT = 0x494e4236; const t_tag CLIENT_INB7_UINT = 0x494e4237; const t_tag CLIENT_UNB0_UINT = 0x554e4230; const t_tag CLIENT_UNB1_UINT = 0x554e4231; const t_tag CLIENT_UNB2_UINT = 0x554e4232; const t_tag CLIENT_UNB3_UINT = 0x554e4233; const t_tag CLIENT_UNB4_UINT = 0x554e4234; const t_tag CLIENT_UNB5_UINT = 0x554e4235; const t_tag CLIENT_UNB6_UINT = 0x554e4236; const t_tag CLIENT_UNB7_UINT = 0x554e4237; const t_tag CLIENT_PLB0_UINT = 0x504c4230; const t_tag CLIENT_PLB1_UINT = 0x504c4231; const t_tag CLIENT_PLB2_UINT = 0x504c4232; const t_tag CLIENT_PLB3_UINT = 0x504c4233; const t_tag CLIENT_PLB4_UINT = 0x504c4234; const t_tag CLIENT_PLB5_UINT = 0x504c4235; const t_tag CLIENT_PLB6_UINT = 0x504c4236; const t_tag CLIENT_PLB7_UINT = 0x504c4237; const t_tag CLIENT_BLB0_UINT = 0x424c4230; const t_tag CLIENT_BLB1_UINT = 0x424c4231; const t_tag CLIENT_BLB2_UINT = 0x424c4232; const t_tag CLIENT_BLB3_UINT = 0x424c4233; const t_tag CLIENT_BLB4_UINT = 0x424c4234; const t_tag CLIENT_BLB5_UINT = 0x424c4235; const t_tag CLIENT_BLB6_UINT = 0x424c4236; const t_tag CLIENT_BLB7_UINT = 0x424c4237; const t_tag CLIENT_INL0_UINT = 0x494e4c30; const t_tag CLIENT_INL1_UINT = 0x494e4c31; const t_tag CLIENT_INL2_UINT = 0x494e4c32; const t_tag CLIENT_INL3_UINT = 0x494e4c33; const t_tag CLIENT_INL4_UINT = 0x494e4c34; const t_tag CLIENT_INL5_UINT = 0x494e4c35; const t_tag CLIENT_INL6_UINT = 0x494e4c36; const t_tag CLIENT_INL7_UINT = 0x494e4c37; const t_tag CLIENT_UNL0_UINT = 0x554e4c30; const t_tag CLIENT_UNL1_UINT = 0x554e4c31; const t_tag CLIENT_UNL2_UINT = 0x554e4c32; const t_tag CLIENT_UNL3_UINT = 0x554e4c33; const t_tag CLIENT_UNL4_UINT = 0x554e4c34; const t_tag CLIENT_UNL5_UINT = 0x554e4c35; const t_tag CLIENT_UNL6_UINT = 0x554e4c36; const t_tag CLIENT_UNL7_UINT = 0x554e4c37; const t_tag CLIENT_PLL0_UINT = 0x504c4c30; const t_tag CLIENT_PLL1_UINT = 0x504c4c31; const t_tag CLIENT_PLL2_UINT = 0x504c4c32; const t_tag CLIENT_PLL3_UINT = 0x504c4c33; const t_tag CLIENT_PLL4_UINT = 0x504c4c34; const t_tag CLIENT_PLL5_UINT = 0x504c4c35; const t_tag CLIENT_PLL6_UINT = 0x504c4c36; const t_tag CLIENT_PLL7_UINT = 0x504c4c37; const t_tag CLIENT_BLL0_UINT = 0x424c4c30; const t_tag CLIENT_BLL1_UINT = 0x424c4c31; const t_tag CLIENT_BLL2_UINT = 0x424c4c32; const t_tag CLIENT_BLL3_UINT = 0x424c4c33; const t_tag CLIENT_BLL4_UINT = 0x424c4c34; const t_tag CLIENT_BLL5_UINT = 0x424c4c35; const t_tag CLIENT_BLL6_UINT = 0x424c4c36; const t_tag CLIENT_BLL7_UINT = 0x424c4c37; const t_tag CLIENT_INK0_UINT = 0x494e4b30; const t_tag CLIENT_INK1_UINT = 0x494e4b31; const t_tag CLIENT_INK2_UINT = 0x494e4b32; const t_tag CLIENT_INK3_UINT = 0x494e4b33; const t_tag CLIENT_INK4_UINT = 0x494e4b34; const t_tag CLIENT_INK5_UINT = 0x494e4b35; const t_tag CLIENT_INK6_UINT = 0x494e4b36; const t_tag CLIENT_INK7_UINT = 0x494e4b37; const t_tag CLIENT_UNK0_UINT = 0x554e4b30; const t_tag CLIENT_UNK1_UINT = 0x554e4b31; const t_tag CLIENT_UNK2_UINT = 0x554e4b32; const t_tag CLIENT_UNK3_UINT = 0x554e4b33; const t_tag CLIENT_UNK4_UINT = 0x554e4b34; const t_tag CLIENT_UNK5_UINT = 0x554e4b35; const t_tag CLIENT_UNK6_UINT = 0x554e4b36; const t_tag CLIENT_UNK7_UINT = 0x554e4b37; const t_tag CLIENT_PLK0_UINT = 0x504c4b30; const t_tag CLIENT_PLK1_UINT = 0x504c4b31; const t_tag CLIENT_PLK2_UINT = 0x504c4b32; const t_tag CLIENT_PLK3_UINT = 0x504c4b33; const t_tag CLIENT_PLK4_UINT = 0x504c4b34; const t_tag CLIENT_PLK5_UINT = 0x504c4b35; const t_tag CLIENT_PLK6_UINT = 0x504c4b36; const t_tag CLIENT_PLK7_UINT = 0x504c4b37; const t_tag CLIENT_BLK0_UINT = 0x424c4b30; const t_tag CLIENT_BLK1_UINT = 0x424c4b31; const t_tag CLIENT_BLK2_UINT = 0x424c4b32; const t_tag CLIENT_BLK3_UINT = 0x424c4b33; const t_tag CLIENT_BLK4_UINT = 0x424c4b34; const t_tag CLIENT_BLK5_UINT = 0x424c4b35; const t_tag CLIENT_BLK6_UINT = 0x424c4b36; const t_tag CLIENT_BLK7_UINT = 0x424c4b37; const t_tag CLIENT_BLC0_UINT = 0x424c4330; const t_tag CLIENT_BLC1_UINT = 0x424c4331; const t_tag CLIENT_BLC2_UINT = 0x424c4332; const t_tag CLIENT_BLC3_UINT = 0x424c4333; const t_tag CLIENT_BLC4_UINT = 0x424c4334; const t_tag CLIENT_BLC5_UINT = 0x424c4335; const t_tag CLIENT_BLC6_UINT = 0x424c4336; const t_tag CLIENT_BLC7_UINT = 0x424c4337; } #endif /* INCLUDED_WOL_GAMERES_PROTOCOL_TYPES */ pvpgn-1.99.7.2.1+dfsg/src/common/wolhash.cpp000066400000000000000000000046201462640461400204320ustar00rootroot00000000000000/* * Original hash funcions Copyright (C) Luigi Auriemma (aluigi@autistici.org) * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/wolhash.h" #include #include #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { extern int wol_hash(t_wolhash * hashout, unsigned int size, void const * datain) { unsigned char pwd1[9]; unsigned char pwd2[9]; unsigned char edx; unsigned char esi; unsigned char i; unsigned char *p1; unsigned char *p2; /** * Original NOTE by Luigi Auriemma: * Original algorithm starts at offset 0x0041d440 of wchat.dat of 4.221 US version * The algorithm is one-way, so the encoded password can NOT be completely decoded! */ if (size > 8) { // ERROR1("Westwood passwords are max 8 bytes long: \"%.8s\"",size); return -1; } if (!datain) { ERROR0("got NULL datain"); return -1; } std::memset(pwd1, 0, sizeof(pwd1)); std::memset(pwd2, 0, sizeof(pwd2)); std::memcpy(pwd1, datain, size); esi = size; p1 = pwd1; p2 = pwd2; for (i = 0; i < size; i++) { if (*p1 & 1) { edx = *p1 << 1; edx &= *(pwd1 + esi); } else { edx = *p1 ^ *(pwd1 + esi); } *p2++ = edx; esi--; p1++; } p1 = pwd1; p2 = pwd2; for (i = 0; i < 8; i++) { edx = *p2++ & 0x3f; *p1++ = WOL_HASH_CHAR[edx]; } std::memcpy(hashout, pwd1, sizeof(pwd1)); return 0; } /*extern int wolhash_eq(t_wolhash const h1, t_wolhash const h2) { unsigned int i; if (!h1 || !h2) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL hash"); return -1; } for (i=0; i<5; i++) if (h1[i]!=h2[i]) return 0; return 1; }*/ } pvpgn-1.99.7.2.1+dfsg/src/common/wolhash.h000066400000000000000000000023701462640461400200770ustar00rootroot00000000000000/* * Copyright (C) 2007 Pelish (pelish@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_WOLHASH_TYPES #define INCLUDED_WOLHASH_TYPES namespace pvpgn { using t_wolhash = char[9]; } #endif /*****/ #define WOL_HASH_CHAR "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./" #ifndef JUST_NEED_TYPES #ifndef INCLUDED_WOLHASH_PROTOS #define INCLUDED_WOLHASH_PROTOS namespace pvpgn { extern int wol_hash(t_wolhash * hashout, unsigned int size, void const * datain); //extern int wolhash_eq(t_wolhash const h1, t_wolhash const h2) ; } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/common/xalloc.cpp000066400000000000000000000050611462640461400202470ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifndef XALLOC_SKIP #define XALLOC_INTERNAL_ACCESS #include "xalloc.h" #undef XALLOC_INTERNAL_ACCESS #include "compat/strdup.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { static t_oom_cb oom_cb = NULL; void *xmalloc_real(std::size_t size, const char *fn, unsigned ln) { void *res; res = malloc(size); if (!res) { eventlog(eventlog_level_fatal, __FUNCTION__, "out of memory (from {}:{})", fn, ln); if (oom_cb && oom_cb() && (res = malloc(size))) return res; std::abort(); } return res; } void *xcalloc_real(std::size_t nmemb, std::size_t size, const char *fn, unsigned ln) { void *res; res = calloc(nmemb, size); if (!res) { eventlog(eventlog_level_fatal, __FUNCTION__, "out of memory (from {}:{})", fn, ln); if (oom_cb && oom_cb() && (res = calloc(nmemb, size))) return res; std::abort(); } return res; } void *xrealloc_real(void *ptr, std::size_t size, const char *fn, unsigned ln) { void *res; res = std::realloc(ptr, size); if (!res) { eventlog(eventlog_level_fatal, __FUNCTION__, "out of memory (from {}:{})", fn, ln); if (oom_cb && oom_cb() && (res = std::realloc(ptr, size))) return res; std::abort(); } return res; } char *xstrdup_real(const char *str, const char *fn, unsigned ln) { char *res; res = strdup(str); if (!res) { eventlog(eventlog_level_fatal, __FUNCTION__, "out of memory (from {}:{})", fn, ln); if (oom_cb && oom_cb() && (res = strdup(str))) return res; std::abort(); } return res; } void xfree_real(void *ptr, const char *fn, unsigned ln) { if (!ptr) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL ptr (from {}:{})", fn, ln); return; } free(ptr); } void xalloc_setcb(t_oom_cb cb) { oom_cb = cb; } } #endif /* XALLOC_SKIP */ pvpgn-1.99.7.2.1+dfsg/src/common/xalloc.h000066400000000000000000000040031462640461400177070ustar00rootroot00000000000000/* * Copyright (C) 2004 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_XALLOC_TYPES namespace pvpgn { /* out of memory callback function */ typedef int(*t_oom_cb)(void); } #define INCLUDED_XALLOC_TYPES #endif /* INCLUDED_XALLOC_TYPES */ #ifndef INCLUDED_XALLOC_PROTOS #define INCLUDED_XALLOC_PROTOS #ifndef XALLOC_SKIP #include namespace pvpgn { #define xmalloc(size) xmalloc_real(size,__FILE__,__LINE__) void *xmalloc_real(std::size_t size, const char *fn, unsigned ln); #define xcalloc(no,size) xcalloc_real(no,size,__FILE__,__LINE__) void *xcalloc_real(std::size_t nmemb, std::size_t size, const char *fn, unsigned ln); #define xrealloc(ptr,size) xrealloc_real(ptr,size,__FILE__,__LINE__) void *xrealloc_real(void *ptr, std::size_t size, const char *fn, unsigned ln); #define xstrdup(str) xstrdup_real(str,__FILE__,__LINE__) char *xstrdup_real(const char *str, const char *fn, unsigned ln); #define xfree(ptr) xfree_real(ptr,__FILE__,__LINE__) void xfree_real(void *ptr, const char *fn, unsigned ln); void xalloc_setcb(t_oom_cb cb); } #else /* XALLOC_SKIP */ #define xmalloc(size) malloc(size) #define xcalloc(no,size) calloc(no,size) #define xrealloc(ptr,size) std::realloc(ptr,size) #define xstrdup(str) strdup(str) #define xfree(ptr) free(ptr) #define xalloc_setcb(cb) #endif #endif /* INCLUDED_XALLOC_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/common/xstr.cpp000066400000000000000000000057361462640461400177760ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * lstr is a structure to be used for cached string lengths for speed * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/xstr.h" #include #include #include "common/xalloc.h" #include "common/setup_after.h" /* how many bytes allocate at once on enlarging a xstr */ #define XSTR_INCREMENT 256 namespace pvpgn { /* enlarge "dst" enough so it can hold more "size" characters (not including terminator) */ static void xstr_enlarge(t_xstr* dst, int size) { if (dst->alen < dst->ulen + size + 1) { int nalen = ((dst->ulen + size + 10) / XSTR_INCREMENT + 1) * XSTR_INCREMENT; dst->str = (char*)xrealloc(dst->str, nalen); dst->alen = nalen; } } extern t_xstr* xstr_alloc(void) { t_xstr* xstr = (t_xstr*)xmalloc(sizeof(t_xstr)); xstr_init(xstr); return xstr; } extern void xstr_free(t_xstr* xstr) { assert(xstr); if (xstr->str) xfree(xstr->str); xfree(xstr); } extern t_xstr * xstr_cpy_str(t_xstr * dst, const char * src) { int len; assert(dst); dst->ulen = 0; /* so if we cpy a NULL string we delete the old one :) */ if (!src) return dst; len = std::strlen(src); /* need to enlarge dst ? */ xstr_enlarge(dst, len); std::memcpy(dst->str, src, len + 1); dst->ulen = len; return dst; } extern t_xstr* xstr_cat_xstr(t_xstr* dst, const t_xstr* src) { assert(dst); if (!src || !src->ulen) return dst; /* need to enlarge dst ? */ xstr_enlarge(dst, src->ulen); std::memcpy(dst->str + dst->ulen, src->str, src->ulen + 1); dst->ulen += src->ulen; return dst; } extern t_xstr* xstr_cat_str(t_xstr* dst, const char* src) { int len; assert(dst); if (!src) return dst; len = std::strlen(src); /* need to enlarge dst ? */ xstr_enlarge(dst, len); std::memcpy(dst->str + dst->ulen, src, len + 1); dst->ulen += len; return dst; } extern t_xstr* xstr_ncat_str(t_xstr * dst, const char * src, int len) { const char *p; assert(dst); if (!src || len < 1 || !src[0]) return dst; for (p = src; *p && p - src < len; ++p) xstr_cat_char(dst, *p); return dst; } extern t_xstr* xstr_cat_char(t_xstr * dst, const char ch) { assert(dst); xstr_enlarge(dst, 1); dst->str[dst->ulen++] = ch; dst->str[dst->ulen] = '\0'; return dst; } } pvpgn-1.99.7.2.1+dfsg/src/common/xstr.h000066400000000000000000000040371462640461400174340ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * * xstr is a module trying to offer some high-level string functionality * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_XSTR_TYPES #define INCLUDED_XSTR_TYPES namespace pvpgn { typedef struct { unsigned alen, ulen; char *str; } t_xstr; } #define DECLARE_XSTR(var) \ t_xstr var = { 0, 0, NULL }; #endif /* INCLUDED_XSTR_TYPES */ #ifndef INCLUDED_XSTR_PROTOS #define INCLUDED_XSTR_PROTOS /* for NULL */ #include namespace pvpgn { extern t_xstr* xstr_alloc(void); extern void xstr_free(t_xstr*); extern t_xstr* xstr_cpy_str(t_xstr* dst, const char* src); extern t_xstr* xstr_cat_xstr(t_xstr* dst, const t_xstr* src); extern t_xstr* xstr_cat_str(t_xstr* dst, const char* src); extern t_xstr* xstr_ncat_str(t_xstr* dst, const char* src, int len); extern t_xstr* xstr_cat_char(t_xstr* dst, const char ch); static inline void xstr_init(t_xstr* xstr) { xstr->alen = xstr->ulen = 0; xstr->str = NULL; } static inline const char* xstr_get_str(t_xstr* xstr) { if (xstr->alen && xstr->ulen) return xstr->str; return NULL; } static inline unsigned xstr_get_len(t_xstr* xstr) { return xstr->alen ? xstr->ulen : 0; } static inline t_xstr* xstr_clear(t_xstr* xstr) { /* reset the string content, don't touch the allocation space */ xstr->ulen = 0; return xstr; } } #endif /* INCLUDED_STR_PROTOS */ pvpgn-1.99.7.2.1+dfsg/src/common/xstring.cpp000066400000000000000000000172411462640461400204660ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "xstring.h" #include #include #include #include #include #include #include #include #include "compat/strdup.h" #include "compat/strcasecmp.h" #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { extern char * strtolower(char * str) { unsigned int i; unsigned char ch; if (!str) return NULL; for (i = 0; (ch = str[i]); i++) { if ((std::isupper(ch))) str[i] = ch + ('a' - 'A'); } return str; } extern unsigned char xtoi(unsigned char ch) { unsigned char retval; if (std::isalpha(ch)) retval = safe_tolower(ch); else retval = ch; if (retval < 'A') retval -= ('0' - 0); else retval -= ('a' - 0xa); return retval; } extern char * str_strip_affix(char * str, char const * affix) { unsigned int i, j, n; int match; if (!str) return NULL; if (!affix) return str; for (i = 0; str[i]; i++) { match = 0; for (n = 0; affix[n]; n++) { if (str[i] == affix[n]) { match = 1; break; } } if (!match) break; } for (j = std::strlen(str) - 1; j >= i; j--) { match = 0; for (n = 0; affix[n]; n++) { if (str[j] == affix[n]) { match = 1; break; } } if (!match) break; } if (i > j) { str[0] = '\0'; } else { std::memmove(str, str + i, j - i + 1); str[j - i + 1] = '\0'; } return str; } extern char * hexstrdup(unsigned char const * src) { char * dest; int len; if (!src) return NULL; dest = xstrdup((const char*)src); len = hexstrtoraw(src, dest, std::strlen(dest) + 1); dest[len] = '\0'; return dest; } extern unsigned int hexstrtoraw(unsigned char const * src, char * data, unsigned int datalen) { unsigned char ch; unsigned int i, j; for (i = 0, j = 0; j < datalen; i++) { ch = src[i]; if (!ch) break; if (ch == '\\') { i++; ch = src[i]; if (!ch) { break; } else if (ch == '\\') { data[j++] = ch; } else if (ch == 'x') { if (std::isxdigit(src[i + 1])) { if (std::isxdigit(src[i + 2])) { data[j++] = xtoi(src[i + 1]) * 0x10 + xtoi(src[i + 2]); i += 2; } else { data[j++] = xtoi(src[i + 1]); i++; } } else { data[j++] = ch; } } else if (ch == 'n') { data[j++] = '\n'; } else if (ch == 'r') { data[j++] = '\r'; } else if (ch == 'a') { data[j++] = '\a'; } else if (ch == 't') { data[j++] = '\t'; } else if (ch == 'b') { data[j++] = '\b'; } else if (ch == 'f') { data[j++] = '\f'; } else if (ch == 'v') { data[j++] = '\v'; } else { data[j++] = ch; } } else { data[j++] = ch; continue; } } return j; } #define SPLIT_STRING_INIT_COUNT 32 #define SPLIT_STRING_INCREASEMENT 32 extern char * * strtoargv(char const * str, unsigned int * count) { unsigned int n, index_size; char * temp; unsigned int i; int j; int * pindex; void ** ptrindex; char * result; if (!str || !count) return NULL; temp = (char*)xmalloc(std::strlen(str) + 1); n = SPLIT_STRING_INIT_COUNT; pindex = (int*)xmalloc(n * sizeof (int)); i = j = 0; *count = 0; while (str[i]) { while (str[i] == ' ' || str[i] == '\t') i++; if (!str[i]) break; if (*count >= n) { n += SPLIT_STRING_INCREASEMENT; pindex = (int *)xrealloc(pindex, n * sizeof(int)); } pindex[*count] = j; (*count)++; if (str[i] == '"') { i++; while (str[i]) { if (str[i] == '\\') { i++; if (!str[i]) break; } else if (str[i] == '"') { i++; break; } temp[j++] = str[i++]; } } else { while (str[i] && str[i] != ' ' && str[i] != '\t') { temp[j++] = str[i++]; } } temp[j++] = '\0'; } index_size = *count * sizeof(char *); if (!index_size) { xfree(temp); xfree(pindex); return NULL; } result = (char*)xmalloc(j + index_size); std::memcpy(result + index_size, temp, j); ptrindex = (void**)xmalloc(*count * sizeof (char*)); for (i = 0; i < *count; i++) { ptrindex[i] = result + index_size + pindex[i]; } std::memcpy(result, ptrindex, index_size); xfree(temp); xfree(pindex); xfree(ptrindex); return (char * *)result; } #define COMBINE_STRING_INIT_LEN 1024 #define COMBINE_STRING_INCREASEMENT 1024 extern char * arraytostr(char * * array, char const * delim, int count) { int i; unsigned int n; char * result; int need_delim; if (!delim || !array) return NULL; n = COMBINE_STRING_INIT_LEN; result = (char*)xmalloc(n); result[0] = '\0'; need_delim = 0; for (i = 0; i < count; i++) { if (!array[i]) continue; if (std::strlen(result) + std::strlen(array[i]) + std::strlen(delim) >= n) { n += COMBINE_STRING_INCREASEMENT; result = (char*)xrealloc(result, n); } if (need_delim) { std::strcat(result, delim); } std::strcat(result, array[i]); need_delim = 1; } result = (char*)xrealloc(result, std::strlen(result) + 1); return result; } // You must free the result if result is non-NULL. extern char *str_replace(char *orig, char *rep, char *with) { if (!orig) return nullptr; if (!rep) std::strcpy(rep, ""); int len_rep = std::strlen(rep); if (!with) std::strcpy(with, ""); int len_with = std::strlen(with); // number of replacements int count = 0; // next insert point char *ins = orig; char *tmp = nullptr; for (count = 0; tmp = std::strstr(ins, rep); ++count) ins = tmp + len_rep; // the return string char *result = nullptr; // first time through the loop, all the variable are set correctly // from here on, // tmp points to the end of the result string // ins points to the next occurrence of rep in orig // orig points to the remainder of orig after "end of rep" tmp = result = (char*)xmalloc(std::strlen(orig) + (len_with - len_rep) * count + 1); if (!result) return nullptr; // distance between rep and end of last rep int len_front = 0; while (count--) { ins = std::strstr(orig, rep); len_front = ins - orig; tmp = std::strncpy(tmp, orig, len_front) + len_front; tmp = std::strcpy(tmp, with) + len_with; orig += len_front + len_rep; // move to next "end of rep" } std::strcpy(tmp, orig); return result; } /* Replace "\n" in string to a new line character '\n' */ extern std::string str_replace_nl(char const * text) { std::string s(text); size_t pos = 0; while ((pos = s.find("\\n", pos)) != std::string::npos) { s.replace(pos, 2, "\n"); } return s; } // search substring in input string // (case insensitive) extern bool find_substr(char * input, const char * find) { std::string str1(input); std::string str2(find); return std::lexicographical_compare( str1.begin(), str1.end(), str2.begin(), str2.end(), [](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); } ); } } pvpgn-1.99.7.2.1+dfsg/src/common/xstring.h000066400000000000000000000036361462640461400201360ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_XSTRING_H #define INCLUDED_XSTRING_H #include #include #include namespace pvpgn { extern char * strtolower(char * str); extern char * hexstrdup(unsigned char const * src); extern unsigned int hexstrtoraw(unsigned char const * src, char * data, unsigned int datalen); extern unsigned char xtoi(unsigned char ch); extern char * * strtoargv(char const * str, unsigned int * count); extern char * arraytostr(char * * array, char const * delim, int count); extern char * str_strip_affix(char * str, char const * affix); extern char *str_replace(char *orig, char *rep, char *with); extern std::string str_replace_nl(char const * text); extern bool find_substr(char * input, const char * find); #define safe_toupper(X) (std::islower((unsigned char)X)?std::toupper((unsigned char)X):(X)) #define safe_tolower(X) (std::isupper((unsigned char)X)?std::tolower((unsigned char)X):(X)) /* Fix for std::string for some unix compilers http://stackoverflow.com/a/20861692/701779 */ template < typename T > std::string std_to_string(const T& n) { std::ostringstream stm; stm << n; return stm.str(); } } #endif pvpgn-1.99.7.2.1+dfsg/src/compat/000077500000000000000000000000001462640461400162525ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/compat/CMakeLists.txt000066400000000000000000000006171462640461400210160ustar00rootroot00000000000000add_library(compat access.h gethostname.h gettimeofday.cpp gettimeofday.h mkdir.h mmap.cpp mmap.h netinet_in.h pdir.cpp pdir.h pgetopt.cpp pgetopt.h pgetpid.h psock.cpp psock.h read.h recv.h rename.h send.h socket.h statmacros.h stdfileno.h strcasecmp.cpp strcasecmp.h strdup.cpp strdup.h strerror.cpp strerror.h strncasecmp.cpp strncasecmp.h strsep.cpp strsep.h termios.h uname.cpp uname.h)pvpgn-1.99.7.2.1+dfsg/src/compat/access.h000066400000000000000000000022661462640461400176720ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_ACCESS_PROTOS #define INCLUDED_ACCESS_PROTOS #ifdef WIN32 # include /* Values for the second argument to access. These may be OR'd together. */ # define R_OK 4 /* Test for read permission. */ # define W_OK 2 /* Test for write permission. */ # define X_OK 1 /* Test for execute permission. */ # define F_OK 0 /* Test for existence. */ #endif #ifdef HAVE_UNISTD_H # include #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/gethostname.h000066400000000000000000000022421462640461400207410ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_GETHOSTNAME_PROTOS #define INCLUDED_GETHOSTNAME_PROTOS /* * This is from unistd.h on Unix systems, but it's included with * the socket stuff under Windows. This tries to hide that. */ #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_WINSOCK2_H # include #else # ifndef HAVE_GETHOSTNAME # error "This program requires gethostname()" # endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/gettimeofday.cpp000066400000000000000000000033051462640461400214400ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifndef HAVE_GETTIMEOFDAY #include "gettimeofday.h" #include #include #ifdef HAVE_SYS_TIMEB_H # include #endif #include "common/setup_after.h" namespace pvpgn { extern int gettimeofday(struct timeval * tv, struct timezone * tz) { #ifdef HAVE_FTIME struct timeb tb; #endif if (!tv) { errno = EFAULT; return -1; } #ifdef HAVE_FTIME tb.millitm = 0; /* apparently the MS CRT version of this doesn't set this member */ /* FIXME: what would be a more appropriate function for that platform? */ ftime(&tb); /* FIXME: some versions are void return others int */ tv->tv_sec = tb.time; tv->tv_usec = ((long)tb.millitm) * 1000; if (tz) { tz->tz_minuteswest = 0; tz->tz_dsttime = 0; } return 0; #else # error "This program requires either gettimeofday() or ftime()" #endif } } #else typedef int filenotempty; /* make ISO standard happy */ #endif pvpgn-1.99.7.2.1+dfsg/src/compat/gettimeofday.h000066400000000000000000000025351462640461400211110ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_GETTIMEOFDAY_TYPES #define INCLUDED_GETTIMEOFDAY_TYPES #ifdef HAVE_SYS_TIME_H # include #endif #ifndef HAVE_GETTIMEOFDAY namespace pvpgn { /* FIXME: these might already exist even if gettimeofday() doesn't */ struct timeval { long tv_sec; long tv_usec; }; struct timezone { int tz_minuteswest; int tz_dsttime; }; } #endif #endif #ifndef INCLUDED_GETTIMEOFDAY_PROTOS #define INCLUDED_GETTIMEOFDAY_PROTOS #ifndef HAVE_GETTIMEOFDAY namespace pvpgn { extern int gettimeofday(struct timeval * tv, struct timezone * tz); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/mkdir.h000066400000000000000000000032301462640461400175270ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_MKDIR_PROTOS #define INCLUDED_MKDIR_PROTOS /* Unix puts this in unistd.h, Borland/Win32 puts it in dir.h, MSVC++/Win32 puts it in direct.h */ /* Windows and MacOS also typically take only one argument */ #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_DIR_H # include #endif #ifdef HAVE_DIRECT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef MKDIR_TAKES_ONE_ARG # ifdef HAVE_MKDIR # define p_mkdir(A,B) mkdir(A) # else # ifdef HAVE__MKDIR # define p_mkdir(A,B) _mkdir(A) # else # error "This program requires either mkdir() or _mkdir()" # endif # endif #else # ifdef HAVE_MKDIR # define p_mkdir mkdir # else # ifdef HAVE__MKDIR # define p_mkdir _mkdir # else # error "This program requires either mkdir() or _mkdir()" # endif # endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/mmap.cpp000066400000000000000000000041111462640461400177050ustar00rootroot00000000000000/* * Copyright (C) 2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifndef HAVE_MMAP #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "common/xalloc.h" #include "mmap.h" #ifdef HAVE_WINDOWS_H #include #endif #ifdef WIN32 # include #endif #include "common/setup_after.h" namespace pvpgn { extern void * pmmap(void *addr, unsigned len, int prot, int flags, int fd, unsigned offset) { void *mem; #ifdef WIN32 HANDLE hFile, hMapping; /* under win32 we only support readonly mappings, the only ones used in pvpgn now :) */ if (prot != PROT_READ) return NULL; hFile = (HANDLE)_get_osfhandle(fd); if (hFile == (HANDLE)-1) return MAP_FAILED; hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (!hMapping) return MAP_FAILED; mem = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); #else /* systems without mmap or win32 */ unsigned pos; int res; mem = xmalloc(len); pos = 0; while (pos < len) { res = read(fd, (char *)mem + pos, len - pos); if (res < 0) { xfree(mem); return MAP_FAILED; } pos += res; } #endif return mem; } extern int pmunmap(void *addr, unsigned len) { #ifdef WIN32 UnmapViewOfFile(addr); #else xfree(addr); #endif return 0; } } #else typedef int filenotempty; /* make ISO standard happy */ #endif pvpgn-1.99.7.2.1+dfsg/src/compat/mmap.h000066400000000000000000000035531462640461400173630ustar00rootroot00000000000000/* * Copyright (C) 2003 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_PMMAP_PROTOS #define INCLUDED_PMMAP_PROTOS #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_MMAN_H # include #endif #ifdef __BORLANDC__ # include #endif #ifndef PROT_NONE #define PROT_NONE 0x00 /* no permissions */ #endif #ifndef PROT_READ #define PROT_READ 0x01 /* pages can be read */ #endif #ifndef PROT_WRITE #define PROT_WRITE 0x02 /* pages can be written */ #endif #ifndef PROT_EXEC #define PROT_EXEC 0x04 /* pages can be executed */ #endif #ifndef MAP_SHARED #define MAP_SHARED 0x0001 /* share changes */ #endif #ifndef MAP_PRIVATE #define MAP_PRIVATE 0x0002 /* changes are private */ #endif #ifndef MAP_COPY #define MAP_COPY MAP_PRIVATE /* Obsolete */ #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif #ifdef HAVE_MMAP #define pmmap(a,b,c,d,e,f) mmap(a,b,c,d,e,f) #define pmunmap(a,b) munmap(a,b) #else namespace pvpgn { extern void * pmmap(void *addr, unsigned len, int prot, int flags, int fd, unsigned offset); extern int pmunmap(void *addr, unsigned len); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/netinet_in.h000066400000000000000000000017271462640461400205660ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_NETINET_IN_PROTOS #define INCLUDED_NETINET_IN_PROTOS #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 #endif #ifndef INADDR_ANY #define INADDR_ANY 0x00000000 #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/pdir.cpp000066400000000000000000000115271462640461400177220ustar00rootroot00000000000000/* * Copyright (C) 2001,2006 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define PDIR_INTERNAL_ACCESS #include "common/setup_before.h" #include "pdir.h" #include #include "common/eventlog.h" #include "common/setup_after.h" #include "compat/strcasecmp.h" #ifdef WIN32 #include "win32/dirent.h" #else #include #endif namespace pvpgn { Directory::Directory(const std::string& path_, bool lazyread_) { open(path_, lazyread_); } Directory::~Directory() throw() { close(); } void Directory::close() { #ifdef WIN32 if (lFindHandle >= 0) _findclose(lFindHandle); #else /* POSIX */ if (dir) closedir(dir); #endif /* WIN32-POSIX */ } void Directory::open(const std::string& path_, bool lazyread_) { #ifdef WIN32 std::string tmp(path_); if (tmp.size() + 1 + 3 >= _MAX_PATH) throw std::runtime_error("pvpgn::Directory::Directory(): WIN32: path too long"); tmp += "/*.*"; status = 0; std::memset(&fileinfo, 0, sizeof(fileinfo)); lFindHandle = _findfirst(tmp.c_str(), &fileinfo); if (lFindHandle < 0 && !lazyread_) throw OpenError(tmp); path = tmp; #else /* POSIX style */ if (!(dir = opendir(path_.c_str())) && !lazyread_) throw OpenError(path); path = path_; #endif /* WIN32-POSIX */ lazyread = lazyread_; } void Directory::rewind() { #ifdef WIN32 close(); status = 0; std::memset(&fileinfo, 0, sizeof(fileinfo)); lFindHandle = _findfirst(path.c_str(), &fileinfo); if (lFindHandle < 0 && !lazyread) { ERROR0("WIN32: couldn't rewind directory"); status = -1; } #else /* POSIX */ if (dir) rewinddir(dir); #endif } char const * Directory::read() const { const char * result; #ifdef WIN32 switch (status) { default: case -1: /* couldn't rewind */ ERROR0("got status -1"); return 0; case 0: /* freshly opened */ status = 1; if (lFindHandle < 0) return 0; result = fileinfo.name; break; case 1: /* reading */ if (lFindHandle < 0) return 0; if (_findnext(lFindHandle, &fileinfo) < 0) { status = 2; return 0; } else result = fileinfo.name; break; case 2: /* EOF */ return 0; } #else /* POSIX */ struct dirent *dentry = dir ? readdir(dir) : 0; if (!dentry) return 0; result = dentry->d_name; #endif /* WIN32-POSIX */ if (!(strcmp(result, ".") && strcmp(result, ".."))) /* here we presume we don't get an infinite number of "." or ".." ;) */ return read(); return result; } Directory::operator bool() const { #ifdef WIN32 return lFindHandle >= 0; #else return dir != 0; #endif } bool is_directory(const char* pzPath); /* Returns a list of files in a directory (except the ones that begin with a dot) */ extern std::vector dir_getfiles(const char * directory, const char* ext, bool recursive) { std::vector files, dfiles; const char* _ext; DIR *dir; struct dirent* ent; dir = opendir(directory); if (!dir) return files; while ((ent = readdir(dir)) != NULL) { const std::string file_name = ent->d_name; const std::string full_file_name = std::string(directory) + "/" + file_name; if (file_name[0] == '.') continue; if (is_directory(full_file_name.c_str())) { // iterate subdirectories if (recursive) { std::vector subfiles = dir_getfiles(full_file_name.c_str(), ext, recursive); for (std::size_t i = 0; i < subfiles.size(); ++i) dfiles.push_back(subfiles[i]); } continue; } // filter by extension _ext = strrchr(file_name.c_str(), '.'); if (strcmp(ext, "*") != 0) if (!_ext || strcasecmp(_ext, ext) != 0) continue; files.push_back(full_file_name); } closedir(dir); // merge files and files from directories, so we will receive directories at begin, files at the end // (otherwise files and directories are read alphabetically - as is) files.insert(files.begin(), dfiles.begin(), dfiles.end()); return files; } bool is_directory(const char* pzPath) { if (pzPath == NULL) return false; DIR *pDir; bool bExists = false; pDir = opendir(pzPath); if (pDir != NULL) { bExists = true; (void)closedir(pDir); } return bExists; } } pvpgn-1.99.7.2.1+dfsg/src/compat/pdir.h000066400000000000000000000042401462640461400173610ustar00rootroot00000000000000/* * Copyright (C) 2001,2006 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_PVPGN_PDIR #define INCLUDED_PVPGN_PDIR #include #include #ifdef HAVE_DIRENT_H # include #else # ifdef HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif # define dirent direct #endif #ifdef WIN32 # include /* for _findfirst(), _findnext(), etc */ #endif #include namespace pvpgn { class Directory { public: class OpenError : public std::runtime_error { public: OpenError(const std::string& path) :std::runtime_error("Error opening directory: " + path) {} ~OpenError() throw() {} }; explicit Directory(const std::string& fname, bool lazyread = false); ~Directory() throw(); void rewind(); char const * read() const; void open(const std::string& fname, bool lazyread = false); operator bool() const; private: std::string path; bool lazyread; #ifdef WIN32 long lFindHandle; mutable struct _finddata_t fileinfo; mutable int status; /* -1 == failure, 0 == freshly opened, 1 == opened and read, 2 == eof */ #else /* POSIX */ DIR * dir; #endif /* do not allow copying for the time being */ Directory(const Directory&); Directory& operator=(const Directory&); void close(); }; extern std::vector dir_getfiles(const char * directory, const char* ext, bool recursive); } #endif pvpgn-1.99.7.2.1+dfsg/src/compat/pgetopt.cpp000066400000000000000000000635511462640461400204520ustar00rootroot00000000000000/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc. This file is part of the GNU C Library. Its master source is NOT part of the C library, however. The master source lives in /gd/gnu/lib. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #ifndef HAVE_GETOPT #include "pgetopt.h" /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #include #include #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #include #endif /* GNU C library. */ #ifdef VMS #include #if HAVE_STRING_H - 0 #include #endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "common/setup_after.h" namespace pvpgn { /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ static const char *nonoption_flags; static int nonoption_flags_len; static int original_argc; static char *const *original_argv; /* Make sure the environment variable bash 2.0 puts in the environment is valid for the getopt call we must make sure that the ARGV passed to getopt is that one passed to the process. */ static void store_args(int argc, char *const *argv) __attribute__((unused)); static void store_args(int argc, char *const *argv) { /* XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ original_argc = argc; original_argv = argv; } text_set_element(__libc_subinit, store_args); #endif /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange(char **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ static const char * _getopt_initialize(int argc, char *const *argv, const char *optstring) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind = 1; nextchar = NULL; posixly_correct = std::getenv("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #ifdef _LIBC if (posixly_correct == NULL && argc == original_argc && argv == original_argv) { /* Bash 2.0 puts a special variable in the environment for each command it runs, specifying which ARGV elements are the results of file name wildcard expansion and therefore should not be considered as options. */ char var[100]; sprintf(var, "_%d_GNU_nonoption_argv_flags_", getpid()); nonoption_flags = getenv(var); if (nonoption_flags == NULL) nonoption_flags_len = 0; else nonoption_flags_len = strlen(nonoption_flags); } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) { optarg = NULL; if (!__getopt_initialized || optind == 0) { optstring = _getopt_initialize(argc, argv, optstring); optind = 1; /* Don't scan ARGV[0], the program name. */ __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #ifdef _LIBC #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && nonoption_flags[optind] == '1')) #else #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **)argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !std::strcmp(argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **)argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !std::strchr(optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!std::strncmp(p->name, nextchar, nameend - nextchar)) { if ((unsigned int)(nameend - nextchar) == (unsigned int)std::strlen(p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf(stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); nextchar += std::strlen(nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } nextchar += std::strlen(nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += std::strlen(nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += std::strlen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || std::strchr(optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf(stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); else /* +option or -option */ fprintf(stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; const char *temp = std::strchr(optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf(stderr, "%s: illegal option -- %c\n", argv[0], c); else fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!std::strncmp(p->name, nextchar, nameend - nextchar)) { if ((unsigned int)(nameend - nextchar) == std::strlen(p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf(stderr, "%s: option `-W %s' is ambiguous\n", argv[0], argv[optind]); nextchar += std::strlen(nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) fprintf(stderr, "\ %s: option `-W %s' doesn't allow an argument\n", argv[0], pfound->name); nextchar += std::strlen(nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += std::strlen(nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += std::strlen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt(int argc, char * const *argv, const char *optstring) { return _getopt_internal(argc, argv, optstring, (const struct option *) 0, (int *)0, 0); } } #endif /* Not ELIDE_CODE. */ #endif /* !HAVE_GETOPT */ pvpgn-1.99.7.2.1+dfsg/src/compat/pgetopt.h000066400000000000000000000072651462640461400201170ustar00rootroot00000000000000/* Declarations for getopt. Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. This file is part of the GNU C Library. Its master source is NOT part of the C library, however. The master source lives in /gd/gnu/lib. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _PVPGN_GETOPT_H_ #define _PVPGN_GETOPT_H_ 1 #include "common/setup_before.h" #ifdef HAVE_UNISTD_H # include #endif #ifndef HAVE_GETOPT namespace pvpgn { /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { const char *name; /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 extern int getopt(int argc, char *const *argv, const char *shortopts); } #endif /* !HAVE_GETOPT */ #endif /* _PVPGN_GETOPT_H_ */ pvpgn-1.99.7.2.1+dfsg/src/compat/pgetpid.h000066400000000000000000000020301462640461400200520ustar00rootroot00000000000000/* * Copyright (C) 2007 Olaf Freyer aaron@cs.tu-berlin.de * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_PGETPID_PROTOS #define INCLUDED_PGETPID_PROTOS #ifdef HAVE_GETPID # ifdef HAVE_UNISTD_H # include # endif # ifdef HAVE_SYS_TYPES_H # include # endif # ifdef HAVE_PROCESS_H # include # endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/psock.cpp000066400000000000000000000034751462640461400201060ustar00rootroot00000000000000/* * Copyright (C) 1999 Philippe Dubois (pdubois1@hotmail.com) * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifdef WIN32 #include "psock.h" #include "common/setup_after.h" extern int psock_init(void) { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData) != 0) { /* FIXME: Tell the user that we could not find a usable WinSock DLL. */ return -1; } return 0; } extern int psock_deinit(void) { if (WSACleanup() != 0) { return -1; } return 0; } extern int psock_ctl(int sd, int mode) { unsigned long nParam = 1; /* u_long or DWORD or what? */ return ioctlsocket(sd, mode, &nParam); } #else #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_FCNTL_H # include #else # ifdef HAVE_SYS_FILE_H # include # endif #endif #include "psock.h" #include "common/setup_after.h" extern int psock_ctl(int sd, long int mode) { long int oldmode; if ((oldmode = fcntl(sd, F_GETFL)) < 0) oldmode = 0; oldmode |= mode; return fcntl(sd, F_SETFL, mode); } #endif pvpgn-1.99.7.2.1+dfsg/src/compat/psock.h000066400000000000000000000215101462640461400175410ustar00rootroot00000000000000/* * Copyright (C) 1999 Philippe Dubois (pdubois1@hotmail.com) * Copyright (C) 1999,2000,2002 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef JUST_NEED_TYPES #ifndef INCLUDED_PSOCK_PROTOS #define INCLUDED_PSOCK_PROTOS /* * This file is like the other compat header files in that it * tries to hide differences between systems. It is somewhat * different in that it doesn't present a "normal" interface * to the rest of the code, but instead defines its own macros * and functions. This is done since Win32 uses closesocket() * instead of close() for sockets, but it can't be remapped * since it already has a close() for file descriptors. Also, * this allows us to hide the fact that some systems use * fcntl() and some use ioctl(). The select() function is * included here even though it isn't strictly for sockets. * Just call this version if any of the file descriptors might * be sockets. * * This file must be included _after_ any other socket related * headers and any other compat socket fixup headers. */ #ifdef WIN32 /* winsock2 */ #include /* protocol families */ #define PSOCK_PF_INET PF_INET /* address families */ #define PSOCK_AF_INET AF_INET /* socket types */ #define PSOCK_SOCK_STREAM SOCK_STREAM #define PSOCK_SOCK_DGRAM SOCK_DGRAM /* socket protocols */ #ifdef IPPROTO_TCP # define PSOCK_IPPROTO_TCP IPPROTO_TCP #else # define PSOCK_IPPROTO_TCP 0 #endif #ifdef IPPROTO_UDP # define PSOCK_IPPROTO_UDP IPPROTO_UDP #else # define PSOCK_IPPROTO_UDP 0 #endif /* psock_[gs]etsockopt() flags */ #define PSOCK_SOL_SOCKET SOL_SOCKET #define PSOCK_SO_REUSEADDR SO_REUSEADDR #define PSOCK_SO_KEEPALIVE SO_KEEPALIVE #define PSOCK_SO_ERROR SO_ERROR /* psock_ctl() flags */ #define PSOCK_NONBLOCK FIONBIO /* psock_errno() values */ #ifdef WSAEWOULDBLOCK # define PSOCK_EWOULDBLOCK WSAEWOULDBLOCK #endif #ifdef WSAEINPROGRESS # define PSOCK_EINPROGRESS WSAEINPROGRESS #endif #ifdef WSAEINTR # define PSOCK_EINTR WSAEINTR #endif #ifdef WSAECONNABORTED # define PSOCK_ECONNABORTED WSAECONNABORTED #endif #ifdef WSATRY_AGAIN # define PSOCK_EAGAIN WSATRY_AGAIN #endif #ifdef WSAECONNRESET # define PSOCK_ECONNRESET WSAECONNRESET #endif #ifdef WSAENOTCONN # define PSOCK_ENOTCONN WSAENOTCONN #endif #ifdef WSAEPIPE # define PSOCK_EPIPE WSAEPIPE #endif #ifdef WSAEPROTO # define PSOCK_EPROTO WSAEPROTO #endif #ifdef WSAENOBUFS # define PSOCK_ENOBUFS WSAENOBUFS #endif #ifdef WSAENOMEM # define PSOCK_ENOMEM WSAENOMEM #endif /* psock_shutdown() flags */ #define PSOCK_SHUT_RD 0 #define PSOCK_SHUT_WR 1 #define PSOCK_SHUT_RDWR 2 /* psock types */ #define psock_t_socklen int /* psock_select() macros and types */ #define t_psock_fd_set fd_set #define PSOCK_FD_ZERO FD_ZERO #define PSOCK_FD_CLR FD_CLR #define PSOCK_FD_SET FD_SET #define PSOCK_FD_ISSET FD_ISSET /* psock functions */ extern int psock_init(void); /* a real functions in */ extern int psock_deinit(void); /* compat/psock.c */ #define psock_errno() WSAGetLastError() #define psock_socket(pf, t, ps) socket(pf, t, ps) #define psock_getsockopt(s, l, o, v, size) getsockopt(s, l, o, (char *)(v), size) #define psock_setsockopt(s, l, o, v, size) setsockopt(s, l, o, (const char *)(v), size) extern int psock_ctl(int sd, int mode); /* a real function in compat/psock.c */ #define psock_listen(s, b) listen(s, b) #define psock_bind(s, a, l) bind(s, a, l) #define psock_accept(s, a, l) accept(s, a, l) #define psock_connect(s, a, l) connect(s, a, l) #define psock_send(s, b, l, f) send(s, (const char *)(b), l, f) #define psock_sendto(s, b, l, f, a, al) sendto(s, (const char *)(b), l, f, a, al) #define psock_recv(s, b, l, f) recv(s, (char *)(b), l, f) #define psock_recvfrom(s, b, l, f, a, al) recvfrom(s, (char *)(b), l, f, a, al) #define psock_shutdown(s, how) shutdown(s, how) #define psock_close(s) closesocket(s) #define psock_select(s, r, w, e, t) select(s, r, w, e ,t) #define psock_getsockname(s, a, l) getsockname(s, a, l) /* PDubois - 991111 */ /*#define inet_aton(cp, pAdd) (((*pAdd).s_addr = inet_addr(cp)) != INADDR_NONE)*/ /* (this should be done in compat/inet_aton.h and this isn't a 100% correct implementation anyway -Ross) */ #else /* assume POSIX */ #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_NETINET_IP_H # include #endif #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_NETDB_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #include /* protocol families */ #define PSOCK_PF_INET PF_INET /* address families */ #define PSOCK_AF_INET AF_INET /* socket types */ #define PSOCK_SOCK_STREAM SOCK_STREAM #define PSOCK_SOCK_DGRAM SOCK_DGRAM /* socket protocols */ #ifdef IPPROTO_TCP # define PSOCK_IPPROTO_TCP IPPROTO_TCP #else # define PSOCK_IPPROTO_TCP 0 #endif #ifdef IPPROTO_UDP # define PSOCK_IPPROTO_UDP IPPROTO_UDP #else # define PSOCK_IPPROTO_UDP 0 #endif /* psock_[gs]etsockopt() flags */ #define PSOCK_SOL_SOCKET SOL_SOCKET #define PSOCK_SO_REUSEADDR SO_REUSEADDR #define PSOCK_SO_KEEPALIVE SO_KEEPALIVE #define PSOCK_SO_ERROR SO_ERROR /* psock_ctl() flags */ #define PSOCK_NONBLOCK O_NONBLOCK /* psock_errno() values */ #ifdef EWOULDBLOCK # define PSOCK_EWOULDBLOCK EWOULDBLOCK #endif #ifdef EINPROGRESS # define PSOCK_EINPROGRESS EINPROGRESS #endif #ifdef EINTR # define PSOCK_EINTR EINTR #endif #ifdef ECONNABORTED # define PSOCK_ECONNABORTED ECONNABORTED #endif #ifdef EAGAIN # define PSOCK_EAGAIN EAGAIN #endif #ifdef ECONNRESET # define PSOCK_ECONNRESET ECONNRESET #endif #ifdef ENOTCONN # define PSOCK_ENOTCONN ENOTCONN #endif #ifdef EPIPE # define PSOCK_EPIPE EPIPE #endif #ifdef EPROTO # define PSOCK_EPROTO EPROTO #endif #ifdef ENOBUFS # define PSOCK_ENOBUFS ENOBUFS #endif #ifdef ENOMEM # define PSOCK_ENOMEM ENOMEM #endif /* psock_shutdown() flags */ #ifdef SHUT_RD # define PSOCK_SHUT_RD SHUT_RD #else # define PSOCK_SHUT_RD 0 #endif #ifdef SHUT_WR # define PSOCK_SHUT_WR SHUT_WR #else # define PSOCK_SHUT_WR 1 #endif #ifdef SHUT_RDWR # define PSOCK_SHUT_RDWR SHUT_RDWR #else # define PSOCK_SHUT_RDWR 2 #endif /* psock types */ /* FIXME: write configure test to check for this type */ #ifdef HAVE_SOCKLEN_T # define psock_t_socklen socklen_t #else # define psock_t_socklen unsigned int #endif /* psock_select() macros and types */ #define t_psock_fd_set fd_set #define PSOCK_FD_ZERO FD_ZERO #define PSOCK_FD_CLR FD_CLR #define PSOCK_FD_SET FD_SET #define PSOCK_FD_ISSET FD_ISSET /* psock functions */ #define psock_init() (0) #define psock_deinit() (0) #define psock_errno() (errno) #define psock_socket(pf, t, ps) socket(pf, t, ps) #define psock_getsockopt(s, l, o, v, size) getsockopt(s, l, o, (void *)(v), size) #define psock_setsockopt(s, l, o, v, size) setsockopt(s, l, o, (void *)(v), size) extern int psock_ctl(int sd, long int mode); /* a real function in compat/psock.c */ #define psock_listen(s, b) listen(s, b) #define psock_bind(s, a, l) bind(s, a, l) #define psock_accept(s, a, l) accept(s, a, l) #define psock_connect(s, a, l) connect(s, a, l) #define psock_send(s, b, l, f) send(s, (void *)(b), l, f) #define psock_sendto(s, b, l, f, a, al) sendto(s, (void *)(b), l, f, a, al) #define psock_recv(s, b, l, f) recv(s, (void *)(b), l, f) #define psock_recvfrom(s, b, l, f, a, al) recvfrom(s, (void *)(b), l, f, a, al) #define psock_shutdown(s, how) shutdown(s, how) #define psock_close(s) close(s) #define psock_select(s, r, w, e, t) select(s, r, w, e ,t) #define psock_getsockname(s, a, l) getsockname(s, a, l) #endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/read.h000066400000000000000000000016261462640461400173430ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_READ_PROTOS #define INCLUDED_READ_PROTOS /* Unix puts this in unistd.h */ #ifdef WIN32 # include #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/recv.h000066400000000000000000000023241462640461400173630ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_RECV_PROTOS #define INCLUDED_RECV_PROTOS #ifndef HAVE_RECV /* some recvfrom()s don't handle NULL, but recv is called depreciated on BSD */ #ifdef HAVE_RECVFROM # include # define recv(s, b, l, f) recvfrom(s, b, l, f, NULL, NULL) #else # ifdef HAVE_WINSOCK2_H # include # define recv(s, b, l, f) recvfrom(s, b, l, f, NULL, NULL) # else # error "This program requires recvfrom()" # endif #endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/rename.h000066400000000000000000000021601462640461400176710ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_RENAME_PROTOS #define INCLUDED_RENAME_PROTOS #include #include "compat/access.h" namespace pvpgn { static inline int p_rename(const char * oldpath, const char * newpath) { #ifdef WIN32 if (access(newpath, F_OK) == 0) if (std::remove(newpath) < 0) return -1; #endif return std::rename(oldpath, newpath); } } #endif pvpgn-1.99.7.2.1+dfsg/src/compat/runtime_libs.h000066400000000000000000000032361462640461400211230ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_RUNTIME_LIBS_H #define INCLUDED_RUNTIME_LIBS_H #ifdef WIN32 # include /* for GetProcAddress() & LoadLibrary() & FreeLibrary() */ # define OpenLibrary(l) LoadLibrary(l) # define GetFunction(h,f) GetProcAddress((HINSTANCE)h,f) # define CloseLibrary(h) FreeLibrary((HINSTANCE)h) # define MYSQL_LIB "libmysql.dll" # define PGSQL_LIB "libpq.dll" # define SQLITE3_LIB "sqlite3.dll" # define ODBC_LIB "odbc32.dll" #else /* FIXME: will this work on all nix like systems? */ # include /* for dlopen() & dlsym() & dlclose() */ /* link to 'libdl.so' */ # define OpenLibrary(l) dlopen(l, RTLD_LOCAL | RTLD_LAZY) /* is this the correct mode? */ # define GetFunction(h,f) dlsym(h,f) # define CloseLibrary(h) dlclose(h) # define MYSQL_LIB "libmysqlclient.so" # define PGSQL_LIB "libpq.so" # define ODBC_LIB "libodbc.so" # define SQLITE3_LIB "libsqlite3.so" #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/send.h000066400000000000000000000023121462640461400173520ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SEND_PROTOS #define INCLUDED_SEND_PROTOS #ifndef HAVE_SEND /* some sendto()s don't handle NULL, but send is called depreciated on BSD */ #ifdef HAVE_SENDTO # include # define send(s, b, l, f) sendto(s, b, l, f, NULL, NULL) #else # ifdef HAVE_WINSOCK2_H # include # define send(s, b, l, f) sendto(s, b, l, f, NULL, NULL) # else # error "This program requires sendto()" # endif #endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/socket.h000066400000000000000000000023231462640461400177130ustar00rootroot00000000000000/* * Copyright (C) 1999 Philippe Dubois (pdubois1@hotmail.com) * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SOCKET_PROTOS #define INCLUDED_SOCKET_PROTOS #ifdef WIN32 /* assume winsock2 is available */ # include /* * Including that file is basically equivalent to including: * * * * * * * * It should be included once for any file needing one or more * of those headers. */ #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/statmacros.h000066400000000000000000000063511462640461400206100ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STATMACROS_PROTOS #define INCLUDED_STATMACROS_PROTOS #ifdef STAT_MACROS_BROKEN # ifdef S_ISREG # undef S_ISREG # endif # ifdef S_ISDIR # undef S_ISDIR # endif # ifdef S_ISCHR # undef S_ISCHR # endif # ifdef S_ISBLK # undef S_ISBLK # endif # ifdef S_ISFIFO # undef S_ISFIFO # endif # ifdef S_ISSOCK # undef S_ISSOCK # endif # ifdef S_ISLNK # undef S_ISLNK # endif #endif #ifndef S_IFMT # define S_IFMT 0170000 #endif #ifndef S_ISREG # ifdef S_IFREG # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) # endif #endif #ifndef S_ISDIR # ifdef S_IFDIR # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) # endif #endif #ifndef S_ISCHR # ifdef S_IFCHR # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) # endif #endif #ifndef S_ISBLK # ifdef S_IFBLK # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) # endif #endif #ifndef S_ISFIFO # ifdef S_IFIFO # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) # endif #endif #ifndef S_ISLNK # ifdef S_IFLNK # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) # endif #endif #ifndef S_ISSOCK # ifdef S_IFSOCK # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) # endif #endif #ifndef S_ISCDF # ifdef S_CDF # define S_ISCDF(m) (S_ISDIR(m) && ((m) & S_CDF)) # endif #endif #ifndef S_ISMPB # ifdef S_IFMPB /* V7 */ # define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) # define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) # endif #endif #ifndef S_ISNWK # ifdef S_IFNWK /* HP/UX */ # define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) # endif #endif #ifndef S_ISUID # define S_ISUID 01000 /* set user ID */ #endif #ifndef S_ISGID # define S_ISGID 01000 /* set group ID */ #endif #ifndef S_ISVTX # define S_ISVTX 01000 /* sticky bit */ #endif #ifndef S_IRUSR # ifdef S_IREAD # define S_IRUSR S_IREAD # else # define S_IRUSR 00400 # endif #endif #ifndef S_IWUSR # ifdef S_IWRITE # define S_IWUSR S_IWRITE # else # define S_IWUSR 00200 # endif #endif #ifndef S_IXUSR # ifdef S_IEXEC # define S_IXUSR S_IEXEC # else # define S_IXUSR 00100 # endif #endif #ifndef S_IRWXU # define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR) #endif #ifndef S_IRGRP # define S_IRGRP (S_IRUSR >> 3) #endif #ifndef S_IWGRP # define S_IWGRP (S_IWUSR >> 3) #endif #ifndef S_IXGRP # define S_IXGRP (S_IXUSR >> 3) #endif #ifndef S_IRWXG # define S_IRWXG (S_IRGRP|S_IWGRP|S_IXGRP) #endif #ifndef S_IROTH # define S_IROTH (S_IRGRP >> 3) #endif #ifndef S_IWOTH # define S_IWOTH (S_IWGRP >> 3) #endif #ifndef S_IXOTH # define S_IXOTH (S_IXGRP >> 3) #endif #ifndef S_IRWXO # define S_IRWXO (S_IROTH|S_IWOTH|S_IXOTH) #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/stdfileno.h000066400000000000000000000022141462640461400204110ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STDFILENO_PROTOS #define INCLUDED_STDFILENO_PROTOS #ifdef HAVE_UNISTD_H # include #endif #ifdef STDIN_FILENO # define STDINFD STDIN_FILENO #else # define STDINFD 0 #endif #ifdef STDOUT_FILENO # define STDOUTFD STDOUT_FILENO #else # define STDOUTFD 1 #endif #ifdef STDERR_FILENO # define STDERRFD STDERR_FILENO #else # define STDERRFD 2 #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strcasecmp.cpp000066400000000000000000000032021462640461400211170ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifndef HAVE_STRCASECMP #include "strcasecmp.h" #include #include #include "common/setup_after.h" namespace pvpgn { extern int strcasecmp(char const * str1, char const * str2) { unsigned int i; int a; int b; if (!str1 || !str2) return -1; /* some versions of tolower() break when given already lowercase characters */ for (i = 0; str1[i] != '\0' && str2[i] != '\0'; i++) { if (isupper((int)str1[i])) a = (int)tolower((int)str1[i]); else a = (int)str1[i]; if (isupper((int)str2[i])) b = (int)tolower((int)str2[i]); else b = (int)str2[i]; if (ab) return +1; } if (str1[i] != '\0') return -1; if (str2[i] != '\0') return +1; return 0; } } #else typedef int filenotempty; /* make ISO standard happy */ #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strcasecmp.h000066400000000000000000000021151462640461400205660ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STRCASECMP_PROTOS #define INCLUDED_STRCASECMP_PROTOS #include #ifndef HAVE_STRCASECMP #ifdef HAVE_STRICMP # define strcasecmp(s1, s2) stricmp(s1, s2) # define HAVE_STRCASECMP #else namespace pvpgn { extern int strcasecmp(char const * str1, char const * str2); } #endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strdup.cpp000066400000000000000000000023261462640461400203020ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifndef HAVE_STRDUP #include "strdup.h" #include #include #include #include "common/xalloc.h" #include "common/setup_after.h" namespace pvpgn { extern char * strdup(char const * str) { char * out; if (!(out = (char *)xmalloc(std::strlen(str) + 1))) return NULL; std::strcpy(out, str); return out; } } #else typedef int filenotempty; /* make ISO standard happy */ #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strdup.h000066400000000000000000000021031462640461400177400ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STRDUP_PROTOS #define INCLUDED_STRDUP_PROTOS #ifndef HAVE_STRDUP namespace pvpgn { extern char * strdup(char const * str); } #else // dirty hack? but I don't like to include directly when I just want this one... // better solutions welcome # include #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strerror.cpp000066400000000000000000000077271462640461400206550ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifdef WIN32 #include #include namespace pvpgn { extern char const * pstrerror(int errornum) { switch (errornum) { case WSAEINTR: return "Interrupted function call"; case WSAEACCES: return "Permission denied"; case WSAEFAULT: return "Bad address"; case WSAEINVAL: return "Inavlid argument"; case WSAEMFILE: return "Too many open files"; case WSAEWOULDBLOCK: return "Resource temorarily unavailable"; case WSAEINPROGRESS: return "Operation now in progress"; case WSAEALREADY: return "Operation already in progress"; case WSAENOTSOCK: return "Socket operation on nonsocket"; case WSAEDESTADDRREQ: return "Destination address required"; case WSAEMSGSIZE: return "Message too long"; case WSAEPROTOTYPE: return "Protocol wrong type fpr socket"; case WSAENOPROTOOPT: return "Bad protocol option"; case WSAEPROTONOSUPPORT: return "Protocol not supported"; case WSAESOCKTNOSUPPORT: return "Socket type not supported"; case WSAEOPNOTSUPP: return "Operation not supported"; case WSAEPFNOSUPPORT: return "Protocol family not supported"; case WSAEAFNOSUPPORT: return "Address family not supported by protocol family"; case WSAEADDRINUSE: return "Address already in use"; case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; case WSAENETDOWN: return "Network is down"; case WSAENETUNREACH: return "Network is unreachable"; case WSAENETRESET: return "Network dropped connection on reset"; case WSAECONNABORTED: return "Software caused connection abort"; case WSAECONNRESET: return "Connection reset by peer"; case WSAENOBUFS: return "No buffer space available"; case WSAEISCONN: return "Socket is already connected"; case WSAENOTCONN: return "Socket is not connected"; case WSAESHUTDOWN: return "Cannot send after socket shutdown"; case WSAETIMEDOUT: return "Connection timed out"; case WSAECONNREFUSED: return "Connection refused"; case WSAEHOSTDOWN: return "Host is down"; case WSAEHOSTUNREACH: return "No route to host"; case WSAEPROCLIM: return "Too many processes"; case WSASYSNOTREADY: return "Network subsystem is unavailable"; case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; case WSANOTINITIALISED: return "Successful WSAStartup not yet performed"; case WSAEDISCON: return "Graceful shutdown in progress"; case WSATYPE_NOT_FOUND: return "Class type not found"; case WSAHOST_NOT_FOUND: return "Host not found"; case WSATRY_AGAIN: return "Nonauthoriative host not found"; case WSANO_RECOVERY: return "This is a nonrecoverable error"; case WSANO_DATA: return "Valid name, no data record of requested type"; case WSA_INVALID_HANDLE: return "Specified event object handle is invalid"; case WSA_INVALID_PARAMETER: return "One or more parameters are invalid"; case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state"; case WSA_IO_PENDING: return "Overlapped operation will complete later"; case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available"; case WSA_OPERATION_ABORTED: return "Overlapped operation aborted"; case WSASYSCALLFAILURE: return "System call failure"; default: return std::strerror(errornum); } } } #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strerror.h000066400000000000000000000017641462640461400203150ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STRERROR_PROTOS #define INCLUDED_STRERROR_PROTOS #if defined(WIN32) namespace pvpgn { extern char const * pstrerror(int errornum); } #else #include # define pstrerror(e) strerror(e) #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strncasecmp.cpp000066400000000000000000000032701462640461400213020ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifndef HAVE_STRCASECMP #include "strncasecmp.h" #include #include #include "common/setup_after.h" namespace pvpgn { extern int strncasecmp(char const * str1, char const * str2, unsigned int cnt) { unsigned int i; int a; int b; if (!str1 || !str2) return -1; /* some versions of tolower() break when given already lowercase characters */ for (i = 0; i < cnt && str1[i] != '\0' && str2[i] != '\0'; i++) { if (isupper((int)str1[i])) a = (int)tolower((int)str1[i]); else a = (int)str1[i]; if (isupper((int)str2[i])) b = (int)tolower((int)str2[i]); else b = (int)str2[i]; if (ab) return +1; } if (i < cnt && str1[i] != '\0') return -1; if (i < cnt && str2[i] != '\0') return +1; return 0; } } #else typedef int filenotempty; /* make ISO standard happy */ #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strncasecmp.h000066400000000000000000000022261462640461400207470ustar00rootroot00000000000000/* * Copyright (C) 1999 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STRNCASECMP_PROTOS #define INCLUDED_STRNCASECMP_PROTOS #include #ifndef HAVE_STRNCASECMP #ifdef HAVE_STRNICMP # define strncasecmp(s1, s2, cnt) strnicmp(s1, s2, cnt) # define HAVE_STRNCASECMP /* don't include our own function */ #else namespace pvpgn { extern int strncasecmp(char const * str1, char const * str2, unsigned int cnt); } #endif #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strsep.cpp000066400000000000000000000027271462640461400203060ustar00rootroot00000000000000/* * Copyright (C) 2002 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #ifndef HAVE_STRSEP #include "strsep.h" #include #include "common/setup_after.h" namespace pvpgn { extern char * strsep(char * * str, char const * delims) { char * begin; begin = *str; if (!begin) return NULL; /* already returned last token */ /* FIXME: optimiz case of 1 char delims (maybe not worth the effort) */ for (; **str != '\0'; (*str)++) if (std::strchr(delims, **str)) { **str = '\0'; /* terminate token */ (*str)++; /* remember the position of the next char */ return begin; } *str = NULL; return begin; /* return the whole input string */ } } #else typedef int filenotempty; /* make ISO standard happy */ #endif pvpgn-1.99.7.2.1+dfsg/src/compat/strsep.h000066400000000000000000000017001462640461400177410ustar00rootroot00000000000000/* * Copyright (C) 2002 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_STRSEP_PROTOS #define INCLUDED_STRSEP_PROTOS #ifndef HAVE_STRSEP namespace pvpgn { extern char * strsep(char * * str, char const * delim); } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/termios.h000066400000000000000000000030521462640461400201050ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_TERMIOS_TYPES #define INCLUDED_TERMIOS_TYPES /* FIXME: this might already exist even if termios.h doesn't... check for the type in autoconf */ # ifndef HAVE_TERMIOS_H namespace pvpgn { struct termios { int c_lflag; int c_cc[1]; }; } # define ECHO 1 # define ICANON 1 # define VMIN 1 # define VTIME 1 # endif #endif #ifndef INCLUDED_TERMIOS_PROTOS #define INCLUDED_TERMIOS_PROTOS /* FIXME: check for functions or macros (autoconf doesn't protect header inclusion for function tests so it isn't easy */ #ifndef HAVE_TERMIOS_H # define tcgetattr(F,T) (-1) #endif /* FIXME: check for functions or macros (autoconf doesn't protect header inclusion for function tests so it isn't easy */ #ifndef HAVE_TERMIOS_H # define tcsetattr(F,A,T) (-1) #endif #endif pvpgn-1.99.7.2.1+dfsg/src/compat/uname.cpp000066400000000000000000000134461462640461400200730ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common/setup_before.h" #ifndef HAVE_UNAME #include "uname.h" #include #include #include #include #ifdef HAVE_WINDOWS_H #include #define STATUS_SUCCESS (0x00000000) #endif #include "common/setup_after.h" namespace pvpgn { int uname(struct utsname* buf) { if (!buf) { errno = EFAULT; return -1; } #ifdef HAVE_WINDOWS_H using RtlGetVersionProto = NTSTATUS(WINAPI*)(RTL_OSVERSIONINFOEXW* lpVersionInformation); HMODULE hNtdll = nullptr; if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, L"ntdll.dll", &hNtdll) != 0) { auto fnRtlGetVersion = reinterpret_cast(GetProcAddress(hNtdll, "RtlGetVersion")); if (fnRtlGetVersion != NULL) { RTL_OSVERSIONINFOEXW verinfo = {}; verinfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); if (fnRtlGetVersion(&verinfo) == STATUS_SUCCESS) { std::string temp; SYSTEM_INFO sysinfo = {}; GetSystemInfo(&sysinfo); switch (verinfo.dwMajorVersion) { case 10: { if (verinfo.wProductType == VER_NT_WORKSTATION) { temp.append("Windows 10"); } else { temp.append("Windows Server 2016"); } break; } case 6: { switch (verinfo.dwMinorVersion) { case 3: { if (verinfo.wProductType == VER_NT_WORKSTATION) { temp.append("Windows 8.1"); } else { temp.append("Windows Server 2012 R2"); } break; } case 2: { if (verinfo.wProductType == VER_NT_WORKSTATION) { temp.append("Windows 8"); } else { temp.append("Windows Server 2012"); } break; } case 1: { if (verinfo.wProductType == VER_NT_WORKSTATION) { temp.append("Windows 7"); } else { temp.append("Windows Server 2008 R2"); } break; } case 0: { if (verinfo.wProductType == VER_NT_WORKSTATION) { temp.append("Windows Vista"); } else { temp.append("Windows Server 2008"); } break; } default: { break; } } break; } case 5: { switch (verinfo.dwMinorVersion) { case 2: { if (GetSystemMetrics(SM_SERVERR2) != 0) { temp.append("Windows Server 2003 R2"); } else if (verinfo.wSuiteMask & VER_SUITE_WH_SERVER) { temp.append("Windows Home Server"); } else if (GetSystemMetrics(SM_SERVERR2) == 0) { temp.append("Windows Server 2003"); } else if ((verinfo.wProductType == VER_NT_WORKSTATION) && (sysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)) { temp.append("Windows XP Professional x64 Edition"); } break; } case 1: { temp.append("Windows XP"); break; } case 0: { temp.append("Windows 2000"); break; } default: { break; } } break; } default: { break; } } if (temp.empty()) { temp.append("Windows"); } if (verinfo.wServicePackMajor != 0) { temp.append(" SP" + std::to_string(verinfo.wServicePackMajor)); if (verinfo.wServicePackMinor != 0) { temp.append("." + std::to_string(verinfo.wServicePackMinor)); } } std::snprintf(buf->sysname, sizeof buf->sysname, "%s", temp.c_str()); DWORD len = sizeof buf->nodename; GetComputerNameA(buf->nodename, &len); std::snprintf(buf->release, sizeof buf->release, "Build %lu", verinfo.dwBuildNumber); std::snprintf(buf->version, sizeof buf->version, "%lu.%lu", verinfo.dwMajorVersion, verinfo.dwMinorVersion); std::string arch; switch (sysinfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: { arch = "x64"; break; } case PROCESSOR_ARCHITECTURE_ARM: { arch = "ARM"; break; } case PROCESSOR_ARCHITECTURE_IA64: { arch = "Intel Itanium-based"; break; } case PROCESSOR_ARCHITECTURE_INTEL: { arch = "x86"; break; } case PROCESSOR_ARCHITECTURE_UNKNOWN: default: arch = "Unknown"; } std::snprintf(buf->machine, sizeof buf->machine, "%s", arch.c_str()); // leave this empty for now std::snprintf(buf->domainname, sizeof buf->domainname, ""); } } } #else // !HAVE_WINDOWS_H std::snprintf(buf->sysname, sizeof buf->sysname, "Unknown"); std::snprintf(buf->nodename, sizeof buf->nodename, "Unknown"); std::snprintf(buf->release, sizeof buf->release, "Unknown"); std::snprintf(buf->version, sizeof buf->version, "Unknown"); std::snprintf(buf->machine, sizeof buf->machine, "Unknown"); std::snprintf(buf->domainname, sizeof buf->domainname, "Unknown"); #endif // HAVE_WINDOWS_H return 0; } } #endif pvpgn-1.99.7.2.1+dfsg/src/compat/uname.h000066400000000000000000000023711462640461400175330ustar00rootroot00000000000000/* * Copyright (C) 2000 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INCLUDED_UNAME_H #define INCLUDED_UNAME_H #ifdef HAVE_UNAME #ifdef HAVE_SYS_UTSNAME_H #include #endif #else #ifndef SYS_NMLN #define SYS_NMLN 64 #endif namespace pvpgn { struct utsname { char sysname[SYS_NMLN + 1]; char nodename[SYS_NMLN + 1]; char release[SYS_NMLN + 1]; char version[SYS_NMLN + 1]; char machine[SYS_NMLN + 1]; char domainname[SYS_NMLN + 1]; }; int uname(struct utsname* buf); } #endif // HAVE_UNAME #endif // INCLUDED_UNAME_Hpvpgn-1.99.7.2.1+dfsg/src/d2cs/000077500000000000000000000000001462640461400156225ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/d2cs/CMakeLists.txt000066400000000000000000000015411462640461400203630ustar00rootroot00000000000000set(D2CS_SOURCES bit.h bnetd.cpp bnetd.h cmdline.cpp cmdline.h connection.cpp connection.h d2charfile.cpp d2charfile.h d2charlist.cpp d2charlist.h d2gs.cpp d2gs.h d2ladder.cpp d2ladder.h game.cpp game.h gamequeue.cpp gamequeue.h handle_bnetd.cpp handle_bnetd.h handle_d2cs.cpp handle_d2cs.h handle_d2gs.cpp handle_d2gs.h handle_init.cpp handle_init.h handle_signal.cpp handle_signal.h main.cpp net.cpp net.h prefs.cpp prefs.h s2s.cpp s2s.h server.cpp server.h serverqueue.h serverqueue.cpp setup.h version.h ../win32/d2cs_winmain.cpp ../win32/d2cs_resource.h ../win32/d2cs_resource.rc) if(WITH_WIN32_GUI) add_executable(d2cs WIN32 ${D2CS_SOURCES}) else(WITH_WIN32_GUI) add_executable(d2cs ${D2CS_SOURCES}) endif(WITH_WIN32_GUI) target_link_libraries(d2cs common compat win32 ${NETWORK_LIBRARIES}) install(TARGETS d2cs DESTINATION ${SBINDIR}) pvpgn-1.99.7.2.1+dfsg/src/d2cs/bit.h000066400000000000000000000025631462640461400165570ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_BIT_H #define INCLUDED_BIT_H #define BIT_POS(n) ( 1u << (n) ) #define BIT_SET_FLAG(n,f) ( (n) |= (f) ) #define BIT_CLR_FLAG(n,f) ( (n) &= ~(f) ) #define BIT_TST_FLAG(n,f) ( ((n) & (f)) ? 1: 0 ) #define BIT_SET_CLR_FLAG(n, f, v) ( (v)?(BIT_SET_FLAG(n,f)):(BIT_CLR_FLAG(n,f)) ) #define BIT_RANGE(n,m) ( BIT_POS((m)+1-(n)) - BIT_POS(n) ) #define BIT_LSHIFT(n,m) ( (n) << (m) ) #define BIT_RSHIFT(n,m) ( (n) >> (m) ) #define BIT_GET(n, m) ( ((n) >> (m)) & 1) #define BIT_GET_RANGE(n, m, l) ( ((n) >> (m)) & (1u << (l) -1)) #define BIT_GET_RANGE_MASK(n, m, mask) ( ((n) >> (m)) & (mask)) #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/bnetd.cpp000066400000000000000000000044571462640461400174340ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "bnetd.h" #include #include "common/eventlog.h" #include "prefs.h" #include "s2s.h" #include "handle_bnetd.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static t_connection * bnetd_connection = NULL; extern int bnetd_init(void) { return bnetd_check(); } extern int bnetd_check(void) { static std::time_t prev_connecting_checktime = 0; if (bnetd_connection) { if (d2cs_conn_get_state(bnetd_connection) == conn_state_connecting) { if (std::time(nullptr) - prev_connecting_checktime > prefs_get_s2s_timeout()) { eventlog(eventlog_level_warn, __FUNCTION__, "connection to bnetd s2s timeout"); d2cs_conn_set_state(bnetd_connection, conn_state_destroy); return -1; } } return 0; } if (!(bnetd_connection = s2s_create(prefs_get_bnetdaddr(), BNETD_SERV_PORT, conn_class_bnetd))) { return -1; } if (d2cs_conn_get_state(bnetd_connection) == conn_state_init) { handle_bnetd_init(bnetd_connection); } else { prev_connecting_checktime = std::time(nullptr); } return 0; } extern t_connection * bnetd_conn(void) { return bnetd_connection; } extern int bnetd_set_connection(t_connection * c) { bnetd_connection = c; return 0; } extern int bnetd_destroy(t_connection * c) { if (bnetd_connection != c) { eventlog(eventlog_level_error, __FUNCTION__, "bnetd connection do not match"); return -1; } bnetd_connection = NULL; return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/bnetd.h000066400000000000000000000021531462640461400170700ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_BNETD_H #define INCLUDED_BNETD_H #include "connection.h" namespace pvpgn { namespace d2cs { extern int bnetd_init(void); extern int bnetd_check(void); extern t_connection * bnetd_conn(void); extern int bnetd_destroy(t_connection * c); extern int bnetd_set_connection(t_connection * c); extern int bnetd_keepalive(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/cmdline.cpp000066400000000000000000000204051462640461400177420ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * Copyright (C) 2005,2007 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "cmdline.h" #include #ifdef WIN32 # include "win32/service.h" #endif #include "compat/strcasecmp.h" #include "common/conf.h" #include "common/xalloc.h" #include "common/eventlog.h" #include "version.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static struct { #ifdef DO_DAEMONIZE unsigned foreground; #endif const char *preffile; const char *logfile; unsigned debug; #ifdef WIN32_GUI unsigned console; unsigned gui; #endif } cmdline_config; static unsigned exitflag; static const char *progname; static int conf_set_preffile(const char *valstr); static int conf_setdef_preffile(void); static int conf_set_logfile(const char * valstr); static int conf_setdef_logfile(void); static int conf_set_help(const char * valstr); static int conf_setdef_help(void); static int conf_set_version(const char * valstr); static int conf_setdef_version(void); static int conf_set_foreground(const char * valstr); static int conf_setdef_foreground(void); static int conf_set_debug(const char * valstr); static int conf_setdef_debug(void); #ifdef WIN32 static int conf_set_service(char const * valstr); static int conf_setdef_service(void); static int conf_set_servaction(const char * valstr); static int conf_setdef_servaction(void); #endif #ifdef WIN32_GUI static int conf_set_console(char const * valstr); static int conf_setdef_console(void); static int conf_set_gui(char const * valstr); static int conf_setdef_gui(void); #endif static t_conf_entry conftab[] = { { "c", conf_set_preffile, NULL, conf_setdef_preffile }, { "config", conf_set_preffile, NULL, conf_setdef_preffile }, { "l", conf_set_logfile, NULL, conf_setdef_logfile }, { "log", conf_set_logfile, NULL, conf_setdef_logfile }, { "h", conf_set_help, NULL, conf_setdef_help }, { "help", conf_set_help, NULL, conf_setdef_help }, { "usage", conf_set_help, NULL, conf_setdef_help }, { "v", conf_set_version, NULL, conf_setdef_version }, { "version", conf_set_version, NULL, conf_setdef_version }, #ifdef DO_DAEMONIZE { "f", conf_set_foreground, NULL, conf_setdef_foreground }, { "foreground", conf_set_foreground, NULL, conf_setdef_foreground }, #endif { "D", conf_set_debug, NULL, conf_setdef_debug }, { "debug", conf_set_debug, NULL, conf_setdef_debug }, #ifdef WIN32 { "service", conf_set_service, NULL, conf_setdef_service }, { "s", conf_set_servaction, NULL, conf_setdef_servaction }, #endif #ifdef WIN32_GUI { "console", conf_set_console, NULL, conf_setdef_console }, { "gui", conf_set_gui, NULL, conf_setdef_gui }, #endif { NULL, NULL, NULL, NULL } }; extern int cmdline_load(int argc, char ** argv) { int res; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return -1; } exitflag = 0; progname = argv[0]; res = conf_load_cmdline(argc, argv, conftab); if (res < 0) return -1; return exitflag ? 0 : 1; } extern void cmdline_unload(void) { conf_unload(conftab); } static void usage(void) { std::fprintf(stderr, "Usage: %s []\n" " -c FILE, --config=FILE use FILE as configuration file (default is " D2CS_DEFAULT_CONF_FILE ")\n" " -l FILE, --log=FILE set log to FILE\n" #ifdef DO_DAEMONIZE " -f, --foreground: don't daemonize\n" #endif " -D, --debug run in debug mode (run in foreground and log to stdout)\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n" #ifdef WIN32 " Running as service functions:\n" " --service run as service\n" " -s install install service\n" " -s uninstall uninstall service\n" #endif #ifdef WIN32GUI " -console run in console mode\n" #endif "\n" "Notes:\n" " 1.You should always use absolute path here for all FILE names\n\n", progname); } #ifdef DO_DAEMONIZE extern int cmdline_get_foreground(void) { return cmdline_config.foreground; } static int conf_set_foreground(const char *valstr) { return conf_set_bool(&cmdline_config.foreground, valstr, 0); } static int conf_setdef_foreground(void) { return conf_set_bool(&cmdline_config.foreground, NULL, 0); } #endif extern const char* cmdline_get_preffile(void) { return cmdline_config.preffile; } static int conf_set_preffile(const char *valstr) { return conf_set_str(&cmdline_config.preffile, valstr, NULL); } static int conf_setdef_preffile(void) { return conf_set_str(&cmdline_config.preffile, NULL, D2CS_DEFAULT_CONF_FILE); } extern const char* cmdline_get_logfile(void) { return cmdline_config.logfile; } static int conf_set_logfile(const char *valstr) { return conf_set_str(&cmdline_config.logfile, valstr, NULL); } static int conf_setdef_logfile(void) { return conf_set_str(&cmdline_config.logfile, NULL, NULL); } static int conf_set_debug(const char *valstr) { conf_set_bool(&cmdline_config.debug, valstr, 0); if (cmdline_config.debug) eventlog_set_debugmode(1); #ifdef DO_DAEMONIZE cmdline_config.foreground = 1; #endif return 0; } static int conf_setdef_debug(void) { return conf_set_bool(&cmdline_config.debug, NULL, 0); } static int conf_set_help(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { usage(); exitflag = 1; } return 0; } static int conf_setdef_help(void) { return 0; } static int conf_set_version(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { std::printf(D2CS_VERSION"\n"); exitflag = 1; } return 0; } static int conf_setdef_version(void) { return 0; } #ifdef WIN32 static int conf_set_service(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { Win32_ServiceRun(); exitflag = 1; } return 0; } static int conf_setdef_service(void) { return 0; } static int conf_set_servaction(const char *valstr) { const char* tmp = NULL; conf_set_str(&tmp, valstr, NULL); if (tmp) { if (!strcasecmp(tmp, "install")) { std::fprintf(stderr, "Installing service"); Win32_ServiceInstall(); } else if (!strcasecmp(tmp, "uninstall")) { std::fprintf(stderr, "Uninstalling service"); Win32_ServiceUninstall(); } else { std::fprintf(stderr, "Unknown service action '%s'\n", tmp); } exitflag = 1; xfree((void *)tmp); } return 0; } static int conf_setdef_servaction(void) { return 0; } #endif #ifdef WIN32_GUI static int conf_set_console(const char *valstr) { conf_set_bool(&cmdline_config.console, valstr, 0); if (cmdline_config.console){ conf_set_bool(&cmdline_config.gui, NULL, 0); } return 0; } static int conf_setdef_console(void) { return conf_set_bool(&cmdline_config.console, NULL, 0); } extern unsigned cmdline_get_console(void){ return cmdline_config.console; } static int conf_set_gui(const char *valstr) { conf_set_bool(&cmdline_config.gui, valstr, 0); if (cmdline_config.gui){ } return 0; } static int conf_setdef_gui(void) { return conf_set_bool(&cmdline_config.gui, NULL, 1); } extern unsigned cmdline_get_gui(void){ return cmdline_config.gui; } #endif } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/cmdline.h000066400000000000000000000025321462640461400174100ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * Copyright (C) 2005 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __D2CS_CMDLINE_H_PROTOS__ #define __D2CS_CMDLINE_H_PROTOS__ namespace pvpgn { namespace d2cs { extern int cmdline_load(int argc, char * * argv); extern void cmdline_unload(void); /* options exported by cmdline */ #ifdef DO_DAEMONIZE extern int cmdline_get_foreground(void); #endif extern const char* cmdline_get_preffile(void); extern const char* cmdline_get_logfile(void); #ifdef WIN32_GUI extern unsigned cmdline_get_console(void); extern unsigned cmdline_get_gui(void); #endif } } #endif /* __D2CS_CMDLINE_H_PROTOS__ */ pvpgn-1.99.7.2.1+dfsg/src/d2cs/connection.cpp000066400000000000000000000510151462640461400204670ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "connection.h" #include #include #include #include #include #include "compat/psock.h" #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/introtate.h" #include "common/addr.h" #include "common/xalloc.h" #include "common/network.h" #include "common/xstring.h" #include "prefs.h" #include "game.h" #include "net.h" #include "handle_init.h" #include "handle_bnetd.h" #include "handle_d2cs.h" #include "handle_d2gs.h" #include "d2gs.h" #include "s2s.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static t_hashtable * connlist_head = NULL; static t_hashtable * conn_charname_list_head = NULL; static t_list * connlist_dead = NULL; static unsigned int total_connection = 0; static int conn_handle_connecting(t_connection * c); static t_packet * conn_create_packet(t_connection * c); static int conn_handle_packet(t_connection * c, t_packet * packet); static int conn_handle_read(t_connection * c); static int conn_handle_write(t_connection * c); static unsigned int conn_charname_hash(char const * charname); static unsigned int conn_sessionnum_hash(unsigned int sessionnum); static unsigned int conn_sessionnum_hash(unsigned int sessionnum) { return sessionnum; } static unsigned int conn_charname_hash(char const * charname) { unsigned int hash; unsigned int i, len, pos; unsigned int ch; ASSERT(charname, 0); len = std::strlen(charname); for (hash = 0, i = 0, pos = 0; i < len; i++) { ch = safe_tolower(charname[i]); hash ^= ROTL(ch, pos, (std::numeric_limits::digits)); pos += (std::numeric_limits::digits) - 1; } return hash; } extern t_hashtable * d2cs_connlist(void) { return connlist_head; } extern int d2cs_connlist_create(void) { if (!(connlist_head = hashtable_create(200))) return -1; if (!(conn_charname_list_head = hashtable_create(200))) return -1; return 0; } extern int d2cs_connlist_destroy(void) { t_connection * c; t_elem * curr; if (connlist_dead) { d2cs_connlist_reap(); if (list_destroy(connlist_dead)) eventlog(eventlog_level_error, __FUNCTION__, "error destroy conndead list"); connlist_dead = NULL; } BEGIN_HASHTABLE_TRAVERSE_DATA(connlist_head, c, t_connection) { d2cs_conn_destroy(c, &curr); } END_HASHTABLE_TRAVERSE_DATA() if (hashtable_destroy(connlist_head) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error destroy connection list"); return -1; } connlist_head = NULL; if (hashtable_destroy(conn_charname_list_head) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error destroy connection charname list"); return -1; } conn_charname_list_head = NULL; return 0; } extern int d2cs_connlist_reap(void) { t_connection * c; if (!connlist_dead) return 0; BEGIN_LIST_TRAVERSE_DATA(connlist_dead, c, t_connection) { d2cs_conn_destroy(c, &curr_elem_); } END_LIST_TRAVERSE_DATA() return 0; } extern int conn_check_multilogin(t_connection const * c, char const * charname) { t_connection * conn; ASSERT(charname, -1); if (!prefs_check_multilogin()) return 0; if (gamelist_find_character(charname)) { return -1; } conn = d2cs_connlist_find_connection_by_charname(charname); if (conn && conn != c) { return -1; } return 0; } extern t_connection * d2cs_connlist_find_connection_by_sessionnum(unsigned int sessionnum) { t_connection * c; t_entry * curr; unsigned int hash; hash = conn_sessionnum_hash(sessionnum); HASHTABLE_TRAVERSE_MATCHING(connlist_head, curr, hash) { if (!(c = (t_connection*)entry_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection in list"); } else if (c->sessionnum == sessionnum) { hashtable_entry_release(curr); return c; } } return NULL; } extern t_connection * d2cs_connlist_find_connection_by_charname(char const * charname) { t_entry * curr; t_connection * c; unsigned int hash; hash = conn_charname_hash(charname); HASHTABLE_TRAVERSE_MATCHING(connlist_head, curr, hash) { if (!(c = (t_connection*)entry_get_data(curr))) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection in list"); } else { if (!c->charname) continue; if (!strcmp_charname(c->charname, charname)) { hashtable_entry_release(curr); return c; } } } return NULL; } static t_packet * conn_create_packet(t_connection * c) { t_packet * packet; switch (c->cclass) { CASE(conn_class_init, packet = packet_create(packet_class_init)); CASE(conn_class_d2cs, packet = packet_create(packet_class_d2cs)); CASE(conn_class_d2gs, packet = packet_create(packet_class_d2gs)); CASE(conn_class_bnetd, packet = packet_create(packet_class_d2cs_bnetd)); default: eventlog(eventlog_level_error, __FUNCTION__, "got bad connection class {}", c->cclass); return NULL; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "error create packet"); return NULL; } d2cs_conn_set_in_queue(c, packet); return packet; } static int conn_handle_connecting(t_connection * c) { int retval; if (net_check_connected(c->sock) < 0) { eventlog(eventlog_level_warn, __FUNCTION__, "can not connect to {}", addr_num_to_addr_str(c->addr, c->port)); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "connected to {}", addr_num_to_addr_str(c->addr, c->port)); c->state = conn_state_init; /* this is a kind of hack to not update fd but updating breaks kqueue * and the clean fix would require a cache a userland copy of the kernel * kqueue fds, considering that it also doesnt brake anything else should do * for the moment fdwatch_update_fd(c->sock, fdwatch_type_read); */ switch (c->cclass) { case conn_class_bnetd: retval = handle_bnetd_init(c); break; default: eventlog(eventlog_level_error, __FUNCTION__, "got bad connection class {}", c->cclass); return -1; } return retval; } static int conn_handle_packet(t_connection * c, t_packet * packet) { int retval; switch (c->cclass) { CASE(conn_class_init, retval = d2cs_handle_init_packet(c, packet)); CASE(conn_class_d2cs, retval = d2cs_handle_d2cs_packet(c, packet)); CASE(conn_class_d2gs, retval = handle_d2gs_packet(c, packet)); CASE(conn_class_bnetd, retval = handle_bnetd_packet(c, packet)); default: eventlog(eventlog_level_error, __FUNCTION__, "got bad connection class {} (close connection)", c->cclass); retval = -1; break; } return retval; } static int conn_handle_read(t_connection * c) { t_packet * packet; int retval; packet = d2cs_conn_get_in_queue(c); if (!packet) { packet = conn_create_packet(c); if (!packet) return -1; c->insize = 0; } switch (net_recv_packet(c->sock, packet, &c->insize)) { case -1: retval = -1; break; case 0: retval = 0; break; case 1: c->insize = 0; d2cs_conn_set_in_queue(c, NULL); retval = conn_handle_packet(c, packet); packet_del_ref(packet); break; default: retval = 0; break; } return retval; } static int conn_handle_write(t_connection * c) { t_packet * packet; int retval; if (c->state == conn_state_connecting) { return conn_handle_connecting(c); } if (!(packet = conn_peek_outqueue(c))) return 0; switch (net_send_packet(c->sock, packet, &c->outsize)) { case -1: retval = -1; break; case 0: retval = 0; break; case 1: c->outsize = 0; packet = conn_pull_outqueue(c); packet_del_ref(packet); retval = 0; break; default: retval = -1; } return retval; } extern int conn_handle_socket(t_connection * c) { std::time_t now; ASSERT(c, -1); now = std::time(NULL); if (c->socket_flag & SOCKET_FLAG_READ) { if (conn_handle_read(c) < 0) return -1; c->last_active = now; } if (c->socket_flag & SOCKET_FLAG_WRITE) { if (conn_handle_write(c) < 0) return -1; c->last_active = now; } c->socket_flag = 0; return 0; } extern int connlist_check_timeout(void) { t_connection * c; std::time_t now; now = std::time(NULL); BEGIN_HASHTABLE_TRAVERSE_DATA(connlist_head, c, t_connection) { switch (c->cclass) { case conn_class_d2cs: if (prefs_get_idletime() && (now - c->last_active > prefs_get_idletime())) { eventlog(eventlog_level_info, __FUNCTION__, "client {} idled too long std::time, destroy it", c->sessionnum); d2cs_conn_set_state(c, conn_state_destroy); } break; case conn_class_d2gs: if (prefs_get_s2s_idletime() && now - c->last_active > prefs_get_s2s_idletime()) { eventlog(eventlog_level_info, __FUNCTION__, "server {} timed out", c->sessionnum); d2cs_conn_set_state(c, conn_state_destroy); } break; case conn_class_bnetd: break; default: break; } } END_HASHTABLE_TRAVERSE_DATA() return 0; } extern t_connection * d2cs_conn_create(int sock, unsigned int local_addr, unsigned short local_port, unsigned int addr, unsigned short port) { static unsigned int sessionnum = 1; t_connection * c; if (sock < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad socket"); return NULL; } c = (t_connection*)xmalloc(sizeof(t_connection)); c->charname = NULL; c->account = NULL; c->sock = sock; c->fdw_idx = -1; c->socket_flag = 0; c->local_addr = local_addr; c->local_port = local_port; c->addr = addr; c->port = port; c->cclass = conn_class_init; c->state = conn_state_init; c->sessionnum = sessionnum++; c->outqueue = NULL; c->inqueue = NULL; c->outsize = 0; c->outsizep = 0; c->insize = 0; c->charinfo = NULL; c->d2gs_id = 0; c->gamequeue = NULL; c->last_active = std::time(NULL); c->sessionnum_hash = conn_sessionnum_hash(c->sessionnum); c->bnetd_sessionnum = 0; c->charname_hash = 0; if (hashtable_insert_data(connlist_head, c, c->sessionnum_hash) < 0) { xfree(c); eventlog(eventlog_level_error, __FUNCTION__, "error add connection to list"); return NULL; } total_connection++; eventlog(eventlog_level_info, __FUNCTION__, "created session={} socket={} ({} current connections)", c->sessionnum, sock, total_connection); return c; } extern int d2cs_conn_destroy(t_connection * c, t_elem ** curr) { t_elem * elem; ASSERT(c, -1); if (c->state == conn_state_destroying) return 0; if (hashtable_remove_data(connlist_head, c, c->sessionnum_hash) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error remove connection from list"); return -1; } c->state = conn_state_destroying; if (c->d2gs_id && c->cclass == conn_class_d2gs) { d2gs_deactive(d2gslist_find_gs(c->d2gs_id), c); } if (c->cclass == conn_class_bnetd) { s2s_destroy(c); } if (c->gamequeue) { gq_destroy(c->gamequeue, &elem); } if (c->account) xfree((void *)c->account); if (c->charinfo) xfree((void *)c->charinfo); if (c->charname) d2cs_conn_set_charname(c, NULL); if (c->inqueue) packet_del_ref(c->inqueue); queue_clear(&c->outqueue); if (connlist_dead) list_remove_data(connlist_dead, c, curr); fdwatch_del_fd(c->fdw_idx); psock_shutdown(c->sock, PSOCK_SHUT_RDWR); psock_close(c->sock); total_connection--; eventlog(eventlog_level_info, __FUNCTION__, "[{}] closed connection {} ({} left)", c->sock, c->sessionnum, total_connection); xfree(c); return 0; } extern int d2cs_conn_get_socket(t_connection const * c) { ASSERT(c, -1); return c->sock; } extern t_conn_state d2cs_conn_get_state(t_connection const * c) { ASSERT(c, conn_state_none); return c->state; } extern int d2cs_conn_set_state(t_connection * c, t_conn_state state) { t_elem * curr; ASSERT(c, -1); /* special case for destroying connections, add them to connlist_dead list */ if (state == conn_state_destroy && c->state != conn_state_destroy) { if (!connlist_dead) connlist_dead = list_create(); list_append_data(connlist_dead, c); } else if (state != conn_state_destroy && c->state == conn_state_destroy) { if (list_remove_data(connlist_dead, c, &curr)) { eventlog(eventlog_level_error, __FUNCTION__, "could not remove dead connection"); return -1; } } c->state = state; return 0; } extern t_conn_class d2cs_conn_get_class(t_connection const * c) { ASSERT(c, conn_class_none); return c->cclass; } extern int d2cs_conn_set_class(t_connection * c, t_conn_class cclass) { ASSERT(c, -1); c->cclass = cclass; return 0; } extern t_packet * d2cs_conn_get_in_queue(t_connection const * c) { ASSERT(c, NULL); return c->inqueue; } extern void d2cs_conn_set_in_queue(t_connection * c, t_packet *packet) { assert(c); c->inqueue = packet; } extern unsigned int d2cs_conn_get_out_size(t_connection const * c) { ASSERT(c, 0); return c->outsize; } extern t_queue * * d2cs_conn_get_out_queue(t_connection const * c) { ASSERT(c, NULL); return (t_queue * *)&c->outqueue; } extern unsigned int d2cs_conn_get_in_size(t_connection const * c) { ASSERT(c, 0); return c->insize; } extern int conn_push_outqueue(t_connection * c, t_packet * packet) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!packet) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet"); return -1; } queue_push_packet((t_queue * *)&c->outqueue, packet); if (!c->outsizep++) fdwatch_update_fd(c->fdw_idx, fdwatch_type_read | fdwatch_type_write); return 0; } extern t_packet * conn_peek_outqueue(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } return queue_peek_packet((t_queue const * const *)&c->outqueue); } extern t_packet * conn_pull_outqueue(t_connection * c) { if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return NULL; } if (c->outsizep) { if (!(--c->outsizep)) fdwatch_update_fd(c->fdw_idx, fdwatch_type_read); return queue_pull_packet((t_queue * *)&c->outqueue); } return NULL; } extern int conn_add_socket_flag(t_connection * c, unsigned int flag) { ASSERT(c, -1); c->socket_flag |= flag; return 0; } extern int conn_process_packet(t_connection * c, t_packet * packet, t_packet_handle_table * table, unsigned int table_size) { unsigned int type; unsigned int size; ASSERT(c, -1); ASSERT(packet, -1); ASSERT(table, -1); type = packet_get_type(packet); size = packet_get_size(packet); if (type >= table_size || !table[type].size) { eventlog(eventlog_level_error, __FUNCTION__, "got bad packet type {} (class {})", type, packet_get_class(packet)); return -1; } if (size < table[type].size) { eventlog(eventlog_level_error, __FUNCTION__, "got bad packet size {} (type {} class {})", size, type, packet_get_class(packet)); return -1; } if (!(c->state & table[type].state)) { eventlog(eventlog_level_error, __FUNCTION__, "connection {} state mismatch for packet type {} (class {})", c->sessionnum, type, packet_get_class(packet)); return -1; } if (!table[type].handler) { eventlog(eventlog_level_error, __FUNCTION__, "missing handler for packet type {} (class {})", type, packet_get_class(packet)); return -1; } return table[type].handler(c, packet); } extern int d2cs_conn_set_account(t_connection * c, char const * account) { ASSERT(c, -1); if (!account) { if (c->account) xfree((void *)c->account); c->account = NULL; } if (c->account) xfree((void *)c->account); c->account = xstrdup(account); return 0; } extern char const * d2cs_conn_get_account(t_connection const * c) { ASSERT(c, NULL); return c->account; } extern int d2cs_conn_set_charname(t_connection * c, char const * charname) { char const * temp; ASSERT(c, -1); temp = NULL; if (charname) temp = xstrdup(charname); if (c->charname) { if (hashtable_remove_data(conn_charname_list_head, c, c->charname_hash) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error remove charname {} from list", charname); if (temp) xfree((void *)temp); return -1; } hashtable_purge(conn_charname_list_head); xfree((void *)c->charname); } if (charname) { c->charname = temp; c->charname_hash = conn_charname_hash(charname); if (hashtable_insert_data(conn_charname_list_head, c, c->charname_hash) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error insert charname {} to list", charname); xfree((void *)c->charname); c->charname = NULL; return -1; } } else { c->charname = NULL; c->charname_hash = 0; } return 0; } extern char const * d2cs_conn_get_charname(t_connection const * c) { ASSERT(c, NULL); return c->charname; } extern unsigned int d2cs_conn_get_sessionnum(t_connection const * c) { ASSERT(c, 0); return c->sessionnum; } extern unsigned int conn_get_charinfo_ladder(t_connection const * c) { ASSERT(c, 0); return d2charinfo_get_ladder(c->charinfo); } extern unsigned int conn_get_charinfo_expansion(t_connection const * c) { ASSERT(c, 0); return d2charinfo_get_expansion(c->charinfo); } extern unsigned int conn_get_charinfo_hardcore(t_connection const * c) { ASSERT(c, 0); return d2charinfo_get_hardcore(c->charinfo); } extern unsigned int conn_get_charinfo_dead(t_connection const * c) { ASSERT(c, 0); return d2charinfo_get_dead(c->charinfo); } extern unsigned int conn_get_charinfo_difficulty(t_connection const * c) { ASSERT(c, 0); return d2charinfo_get_difficulty(c->charinfo); } extern unsigned int conn_get_charinfo_level(t_connection const * c) { ASSERT(c, 0); return d2charinfo_get_level(c->charinfo); } extern unsigned int conn_get_charinfo_class(t_connection const * c) { ASSERT(c, 0); return d2charinfo_get_class(c->charinfo); } extern int conn_set_charinfo(t_connection * c, t_d2charinfo_summary const * charinfo) { ASSERT(c, -1); if (!charinfo) { if (c->charinfo) xfree((void *)c->charinfo); c->charinfo = NULL; return 0; } if (c->charinfo) xfree((void *)c->charinfo); c->charinfo = (t_d2charinfo_summary*)xmalloc(sizeof(t_d2charinfo_summary)); std::memcpy((void*)c->charinfo, charinfo, sizeof(t_d2charinfo_summary)); return 0; } extern int conn_set_d2gs_id(t_connection * c, unsigned int d2gs_id) { ASSERT(c, -1) c->d2gs_id = d2gs_id; return 0; } extern unsigned int conn_get_d2gs_id(t_connection const * c) { ASSERT(c, 0) return c->d2gs_id; } extern unsigned int d2cs_conn_get_addr(t_connection const * c) { ASSERT(c, 0); return c->addr; } extern unsigned short d2cs_conn_get_port(t_connection const * c) { ASSERT(c, 0); return c->port; } extern t_gq * conn_get_gamequeue(t_connection const * c) { ASSERT(c, NULL); return c->gamequeue; } extern int conn_set_gamequeue(t_connection * c, t_gq * gq) { ASSERT(c, -1); c->gamequeue = gq; return 0; } extern int conn_set_bnetd_sessionnum(t_connection * c, unsigned int sessionnum) { ASSERT(c, -1); c->bnetd_sessionnum = sessionnum; return 0; } extern unsigned int conn_get_bnetd_sessionnum(t_connection const * c) { ASSERT(c, 0); return c->bnetd_sessionnum; } extern int conn_add_fd(t_connection * c, t_fdwatch_type rw, fdwatch_handler handler) { assert(c); c->fdw_idx = fdwatch_add_fd(c->sock, rw, handler, c); return c->fdw_idx; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/connection.h000066400000000000000000000130651462640461400201370ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_CONNECTION_H #define INCLUDED_CONNECTION_H #include "common/queue.h" #include "common/hashtable.h" #include "common/packet.h" #include "common/fdwatch.h" #include "d2charfile.h" #include "gamequeue.h" namespace pvpgn { namespace d2cs { typedef enum { conn_state_none = 0x00, conn_state_init = 0x01, conn_state_connecting = 0x02, conn_state_connected = 0x04, conn_state_authed = 0x08, conn_state_char_authed = 0x10, conn_state_destroy = 0x20, conn_state_destroying = 0x40, conn_state_any = 0xff } t_conn_state; typedef enum { conn_class_none, conn_class_init, conn_class_d2cs, conn_class_d2gs, conn_class_bnetd } t_conn_class; typedef struct { char const * charname; char const * account; int sock; int fdw_idx; unsigned int socket_flag; unsigned int addr; unsigned int local_addr; unsigned short port; unsigned short local_port; unsigned int last_active; t_conn_class cclass; t_conn_state state; unsigned int sessionnum; t_queue * outqueue; unsigned int outsize; unsigned int outsizep; t_packet * inqueue; unsigned int insize; t_d2charinfo_summary const * charinfo; unsigned int d2gs_id; t_gq * gamequeue; unsigned int bnetd_sessionnum; unsigned int sessionnum_hash; unsigned int charname_hash; } t_connection; typedef int ( * packet_handle_func) (t_connection * c, t_packet * packet); typedef struct { unsigned int size; unsigned state; packet_handle_func handler; } t_packet_handle_table; #define SOCKET_FLAG_READ 0x1 #define SOCKET_FLAG_WRITE 0x2 extern t_hashtable * d2cs_connlist(void); extern int d2cs_connlist_destroy(void); extern int d2cs_connlist_reap(void); extern int d2cs_connlist_create(void); extern int conn_check_multilogin(t_connection const * c,char const * charname); extern t_connection * d2cs_connlist_find_connection_by_sessionnum(unsigned int sessionnum); extern t_connection * d2cs_connlist_find_connection_by_charname(char const * charname); extern int conn_handle_socket(t_connection * c); extern t_connection * d2cs_conn_create(int sock, unsigned int local_addr, unsigned short local_port, unsigned int addr, unsigned short port); extern int d2cs_conn_destroy(t_connection * c, t_elem ** elem); extern int d2cs_conn_get_socket(t_connection const * c); extern unsigned int d2cs_conn_get_sessionnum(t_connection const * c); extern t_conn_class d2cs_conn_get_class(t_connection const * c); extern int d2cs_conn_set_class(t_connection * c, t_conn_class cclass); extern t_conn_state d2cs_conn_get_state(t_connection const * c); extern int d2cs_conn_set_state(t_connection * c, t_conn_state state); extern t_queue * * d2cs_conn_get_out_queue(t_connection const * c); extern t_packet * d2cs_conn_get_in_queue(t_connection const * c); extern void d2cs_conn_set_in_queue(t_connection * c, t_packet * packet); extern unsigned int d2cs_conn_get_in_size(t_connection const * c); extern unsigned int d2cs_conn_get_out_size(t_connection const * c); extern int conn_push_outqueue(t_connection * c, t_packet * packet); extern t_packet * conn_peek_outqueue(t_connection * c); extern t_packet * conn_pull_outqueue(t_connection * c); extern int conn_add_socket_flag(t_connection * c, unsigned int flag); extern int conn_process_packet(t_connection * c, t_packet * packet, t_packet_handle_table * table, unsigned int table_size); extern int d2cs_conn_set_account(t_connection * c, char const * account); extern int d2cs_conn_set_charname(t_connection * c, char const * charname); extern char const * d2cs_conn_get_account(t_connection const * c); extern char const * d2cs_conn_get_charname(t_connection const * c); extern int conn_set_charinfo(t_connection * c, t_d2charinfo_summary const * charinfo); extern unsigned int conn_get_charinfo_ladder(t_connection const * c); extern unsigned int conn_get_charinfo_expansion(t_connection const * c); extern unsigned int conn_get_charinfo_hardcore(t_connection const * c); extern unsigned int conn_get_charinfo_dead(t_connection const * c); extern unsigned int conn_get_charinfo_difficulty(t_connection const * c); extern unsigned int conn_get_charinfo_level(t_connection const * c); extern unsigned int conn_get_charinfo_class(t_connection const * c); extern unsigned int conn_get_d2gs_id(t_connection const * c); extern int conn_set_d2gs_id(t_connection * c, unsigned int d2gs_id); extern unsigned int d2cs_conn_get_addr(t_connection const * c); extern unsigned short d2cs_conn_get_port(t_connection const * c); extern t_gq * conn_get_gamequeue(t_connection const * c); extern int conn_set_gamequeue(t_connection * c, t_gq * gq); extern int conn_set_bnetd_sessionnum(t_connection * c, unsigned int sessionnum); extern unsigned int conn_get_bnetd_sessionnum(t_connection const * c); extern int conn_add_fd(t_connection * c, t_fdwatch_type rw, fdwatch_handler handler); extern int connlist_check_timeout(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2charfile.cpp000066400000000000000000000750111462640461400203350ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "d2charfile.h" #include #include #include #include #include #include #include "compat/access.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/d2char_checksum.h" #include "common/xstring.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { typedef enum { character_class_amazon, character_class_sorceress, character_class_necromancer, character_class_paladin, character_class_barbarian, character_class_druid, character_class_assassin } t_character_class; static int d2charsave_init(void * buffer, char const * charname, unsigned char chclass, unsigned short status); static int d2charsave_init_from_d2s(unsigned char * buffer, char const * charname, unsigned char chclass, unsigned short status, unsigned int size); static int d2charinfo_init(t_d2charinfo_file * chardata, char const * account, char const * charname, unsigned char chclass, unsigned short status); static int d2charsave_init(void * buffer, char const * charname, unsigned char chclass, unsigned short status) { ASSERT(buffer, -1); ASSERT(charname, -1); bn_byte_set((bn_byte *)((char *)buffer + D2CHARSAVE_CLASS_OFFSET), chclass); bn_short_set((bn_short *)((char *)buffer + D2CHARSAVE_STATUS_OFFSET), status); std::strncpy((char *)buffer + D2CHARSAVE_CHARNAME_OFFSET, charname, MAX_CHARNAME_LEN); return 0; } /* create new character from newbie.save that is normal d2s character file */ static int d2charsave_init_from_d2s(unsigned char * buffer, char const * charname, unsigned char chclass, unsigned short status, unsigned int size) { unsigned int checksum; ASSERT(buffer, -1); ASSERT(charname, -1); // class bn_byte_set((bn_byte *)((char *)buffer + D2CHARSAVE_CLASS_OFFSET_109), chclass); // status (ladder, hardcore, expansion, etc) bn_short_set((bn_short *)((char *)buffer + D2CHARSAVE_STATUS_OFFSET_109), status); // charname std::memset(buffer + D2CHARSAVE_CHARNAME_OFFSET_109, '\0', MAX_CHARNAME_LEN); // clear first std::strncpy((char *)buffer + D2CHARSAVE_CHARNAME_OFFSET_109, charname, MAX_CHARNAME_LEN); std::memset(buffer + D2CHARSAVE_CHARNAME_OFFSET_109 + MAX_CHARNAME_LEN - 1, '\0', 1); // checksum checksum = d2charsave_checksum((unsigned char *)buffer, size, D2CHARSAVE_CHECKSUM_OFFSET); bn_int_set((bn_int *)(buffer + D2CHARSAVE_CHECKSUM_OFFSET), 0); // clear first bn_int_set((bn_int *)(buffer + D2CHARSAVE_CHECKSUM_OFFSET), checksum); return 0; } static int d2charinfo_init(t_d2charinfo_file * chardata, char const * account, char const * charname, unsigned char chclass, unsigned short status) { unsigned int i; std::time_t now; now = std::time(NULL); bn_int_set(&chardata->header.magicword, D2CHARINFO_MAGICWORD); bn_int_set(&chardata->header.version, D2CHARINFO_VERSION); bn_int_set(&chardata->header.create_time, now); bn_int_set(&chardata->header.last_time, now); bn_int_set(&chardata->header.total_play_time, 0); std::memset(chardata->header.charname, 0, MAX_CHARNAME_LEN); std::strncpy((char*)chardata->header.charname, charname, MAX_CHARNAME_LEN); std::memset(chardata->header.account, 0, MAX_USERNAME_LEN); std::strncpy((char*)chardata->header.account, account, MAX_USERNAME_LEN); std::memset(chardata->header.realmname, 0, MAX_REALMNAME_LEN); std::strncpy((char*)chardata->header.realmname, prefs_get_realmname(), MAX_REALMNAME_LEN); bn_int_set(&chardata->header.checksum, 0); for (i = 0; i < NELEMS(chardata->header.reserved); i++) { bn_int_set(&chardata->header.reserved[i], 0); } bn_int_set(&chardata->summary.charlevel, 1); bn_int_set(&chardata->summary.experience, 0); bn_int_set(&chardata->summary.charclass, chclass); bn_int_set(&chardata->summary.charstatus, status); std::memset(chardata->portrait.gfx, D2CHARINFO_PORTRAIT_PADBYTE, sizeof(chardata->portrait.gfx)); std::memset(chardata->portrait.color, D2CHARINFO_PORTRAIT_PADBYTE, sizeof(chardata->portrait.color)); std::memset(chardata->portrait.u2, D2CHARINFO_PORTRAIT_PADBYTE, sizeof(chardata->portrait.u2)); std::memset(chardata->portrait.u1, D2CHARINFO_PORTRAIT_MASK, sizeof(chardata->portrait.u1)); std::memset(chardata->pad, 0, sizeof(chardata->pad)); bn_short_set(&chardata->portrait.header, D2CHARINFO_PORTRAIT_HEADER); bn_byte_set(&chardata->portrait.status, status | D2CHARINFO_PORTRAIT_MASK); bn_byte_set(&chardata->portrait.chclass, chclass + 1); bn_byte_set(&chardata->portrait.level, 1); if (charstatus_get_ladder(status)) bn_byte_set(&chardata->portrait.ladder, 1); else bn_byte_set(&chardata->portrait.ladder, D2CHARINFO_PORTRAIT_PADBYTE); bn_byte_set(&chardata->portrait.end, '\0'); std::memset(chardata->pad, 0, sizeof(chardata->pad)); return 0; } extern int d2char_create(char const * account, char const * charname, unsigned char chclass, unsigned short status) { t_d2charinfo_file chardata; char * savefile, *infofile; const char * newbiefile; unsigned char buffer[MAX_SAVEFILE_SIZE]; unsigned int size; unsigned int version; std::FILE * fp; int status_init = status; ASSERT(account, -1); ASSERT(charname, -1); if (chclass > D2CHAR_MAX_CLASS) chclass = 0; status &= D2CHARINFO_STATUS_FLAG_INIT_MASK; charstatus_set_init(status, 1); /* We need to make sure we are creating the correct character (Classic or Expansion) for the type of game server we are running. If lod_realm = 1 then only Expansion characters can be created and if set to 0 then only Classic character can be created */ if (!(prefs_get_lod_realm() == 2)) { if (prefs_get_lod_realm() && ((status & 0x20) != 0x20)) { eventlog(eventlog_level_warn, __FUNCTION__, "This Realm is for LOD Characters Only"); return -1; } if (!prefs_get_lod_realm() && ((status & 0x20) != 0x0)) { eventlog(eventlog_level_warn, __FUNCTION__, "This Realm is for Classic Characters Only"); return -1; } } /* Once correct type of character is varified then continue with creation of character */ if (!prefs_allow_newchar()) { eventlog(eventlog_level_warn, __FUNCTION__, "creation of new character is disabled"); return -1; } if (d2char_check_charname(charname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad character name \"{}\"", charname); return -1; } if (d2char_check_acctname(account) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad account name \"{}\"", account); return -1; } /* get character template file depending of it's class */ switch ((t_character_class)chclass) { case character_class_amazon: newbiefile = prefs_get_charsave_newbie_amazon(); break; case character_class_sorceress: newbiefile = prefs_get_charsave_newbie_sorceress(); break; case character_class_necromancer: newbiefile = prefs_get_charsave_newbie_necromancer(); break; case character_class_paladin: newbiefile = prefs_get_charsave_newbie_paladin(); break; case character_class_barbarian: newbiefile = prefs_get_charsave_newbie_barbarian(); break; case character_class_druid: newbiefile = prefs_get_charsave_newbie_druid(); break; case character_class_assassin: newbiefile = prefs_get_charsave_newbie_assasin(); break; } size = sizeof(buffer); if (file_read(newbiefile, buffer, &size) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error loading newbie save file"); return -1; } if (size >= sizeof(buffer)) { eventlog(eventlog_level_error, __FUNCTION__, "newbie save file \"{}\" is corrupt (length {}, expected <{})", newbiefile, (unsigned long)size, (unsigned long)sizeof(buffer)); return -1; } savefile = (char*)xmalloc(std::strlen(prefs_get_charsave_dir()) + 1 + std::strlen(charname) + 1); d2char_get_savefile_name(savefile, charname); if ((fp = std::fopen(savefile, "rb"))) { eventlog(eventlog_level_warn, __FUNCTION__, "character save file \"{}\" for \"{}\" already exist", savefile, charname); std::fclose(fp); xfree(savefile); return -1; } infofile = (char*)xmalloc(std::strlen(prefs_get_charinfo_dir()) + 1 + std::strlen(account) + 1 + std::strlen(charname) + 1); d2char_get_infofile_name(infofile, account, charname); std::time_t now = std::time(nullptr); std::time_t ladder_time = prefs_get_ladder_start_time(); if ((ladder_time > 0) && (now < ladder_time)) charstatus_set_ladder(status, 0); /* create from newbie.save or normal d2s template? */ version = bn_int_get(buffer + D2CHARSAVE_VERSION_OFFSET); if (version >= 0x0000005C) d2charsave_init_from_d2s(buffer, charname, chclass, status_init, size); else d2charsave_init(buffer, charname, chclass, status); d2charinfo_init(&chardata, account, charname, chclass, status); if (file_write(infofile, &chardata, sizeof(chardata)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error writing info file \"{}\"", infofile); std::remove(infofile); xfree(infofile); xfree(savefile); return -1; } if (file_write(savefile, buffer, size) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error writing save file \"{}\"", savefile); std::remove(infofile); std::remove(savefile); xfree(savefile); xfree(infofile); return -1; } xfree(savefile); xfree(infofile); eventlog(eventlog_level_info, __FUNCTION__, "character {}(*{}) class {} status 0x{:X} created", charname, account, chclass, status); return 0; } extern int d2char_find(char const * account, char const * charname) { char * file; std::FILE * fp; ASSERT(account, -1); ASSERT(charname, -1); file = (char*)xmalloc(std::strlen(prefs_get_charinfo_dir()) + 1 + std::strlen(account) + 1 + std::strlen(charname) + 1); d2char_get_infofile_name(file, account, charname); fp = std::fopen(file, "rb"); xfree(file); if (fp) { std::fclose(fp); return 0; } return -1; } extern int d2char_convert(char const * account, char const * charname) { std::FILE * fp; char * file; unsigned char buffer[MAX_SAVEFILE_SIZE]; unsigned int status_offset; unsigned char status; unsigned int charstatus; t_d2charinfo_file charinfo; unsigned int size; unsigned int version; unsigned int checksum; ASSERT(account, -1); ASSERT(charname, -1); /* Playing with a expanstion char on a classic realm will cause the game server to crash, therefore I recommed setting allow_convert = 0 in the d2cs.conf We need to do this to prevent creating classic char and converting to expantion on a classic realm. LOD Char must be created on LOD realm */ if (!prefs_get_allow_convert()) { eventlog(eventlog_level_info, __FUNCTION__, "Convert char has been disabled"); return -1; } /* Procedure is stopped here and returned if allow_convet = 0 in d2cs.conf */ if (d2char_check_charname(charname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad character name \"{}\"", charname); return -1; } if (d2char_check_acctname(account) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad account name \"{}\"", account); return -1; } file = (char*)xmalloc(std::strlen(prefs_get_charinfo_dir()) + 1 + std::strlen(account) + 1 + std::strlen(charname) + 1); d2char_get_infofile_name(file, account, charname); if (!(fp = std::fopen(file, "rb+"))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to open charinfo file \"{}\" for reading and writing (std::fopen: {})", file, std::strerror(errno)); xfree(file); return -1; } xfree(file); if (std::fread(&charinfo, 1, sizeof(charinfo), fp) != sizeof(charinfo)) { eventlog(eventlog_level_error, __FUNCTION__, "error reading charinfo file for character \"{}\" (std::fread: {})", charname, std::strerror(errno)); std::fclose(fp); return -1; } charstatus = bn_int_get(charinfo.summary.charstatus); charstatus_set_expansion(charstatus, 1); bn_int_set(&charinfo.summary.charstatus, charstatus); status = bn_byte_get(charinfo.portrait.status); charstatus_set_expansion(status, 1); bn_byte_set(&charinfo.portrait.status, status); std::fseek(fp, 0, SEEK_SET); /* FIXME: check return */ if (std::fwrite(&charinfo, 1, sizeof(charinfo), fp) != sizeof(charinfo)) { eventlog(eventlog_level_error, __FUNCTION__, "error writing charinfo file for character \"{}\" (std::fwrite: {})", charname, std::strerror(errno)); std::fclose(fp); return -1; } if (std::fclose(fp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close charinfo file for character \"{}\" after writing (std::fclose: {})", charname, std::strerror(errno)); return -1; } file = (char*)xmalloc(std::strlen(prefs_get_charsave_dir()) + 1 + std::strlen(charname) + 1); d2char_get_savefile_name(file, charname); if (!(fp = std::fopen(file, "rb+"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open charsave file \"{}\" for reading and writing (std::fopen: {})", file, std::strerror(errno)); xfree(file); return -1; } xfree(file); size = std::fread(buffer, 1, sizeof(buffer), fp); if (!std::feof(fp)) { eventlog(eventlog_level_error, __FUNCTION__, "error reading charsave file for character \"{}\" (std::fread: {})", charname, std::strerror(errno)); std::fclose(fp); return -1; } version = bn_int_get(buffer + D2CHARSAVE_VERSION_OFFSET); if (version >= 0x0000005C) { status_offset = D2CHARSAVE_STATUS_OFFSET_109; } else { status_offset = D2CHARSAVE_STATUS_OFFSET; } status = bn_byte_get(buffer + status_offset); charstatus_set_expansion(status, 1); bn_byte_set((bn_byte *)(buffer + status_offset), status); /* FIXME: shouldn't abuse bn_*_set()... what's the best way to do this? */ if (version >= 0x0000005C) { checksum = d2charsave_checksum(buffer, size, D2CHARSAVE_CHECKSUM_OFFSET); bn_int_set((bn_int *)(buffer + D2CHARSAVE_CHECKSUM_OFFSET), checksum); /* FIXME: shouldn't abuse bn_*_set()... what's the best way to do this? */ } std::fseek(fp, 0, SEEK_SET); /* FIXME: check return */ if (std::fwrite(buffer, 1, size, fp) != size) { eventlog(eventlog_level_error, __FUNCTION__, "error writing charsave file for character {} (std::fwrite: {})", charname, std::strerror(errno)); std::fclose(fp); return -1; } if (std::fclose(fp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close charsave file for character \"{}\" after writing (std::fclose: {})", charname, std::strerror(errno)); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "character {}(*{}) converted to expansion", charname, account); return 0; } extern int d2char_delete(char const * account, char const * charname) { char * file; ASSERT(account, -1); ASSERT(charname, -1); if (d2char_check_charname(charname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad character name \"{}\"", charname); return -1; } if (d2char_check_acctname(account) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad account name \"{}\"", account); return -1; } /* charsave file */ file = (char*)xmalloc(std::strlen(prefs_get_charinfo_dir()) + 1 + std::strlen(account) + 1 + std::strlen(charname) + 1); d2char_get_infofile_name(file, account, charname); if (std::remove(file) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to delete charinfo file \"{}\" (std::remove: {})", file, std::strerror(errno)); xfree(file); return -1; } xfree(file); /* charinfo file */ file = (char*)xmalloc(std::strlen(prefs_get_charsave_dir()) + 1 + std::strlen(charname) + 1); d2char_get_savefile_name(file, charname); if (std::remove(file) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to delete charsave file \"{}\" (std::remove: {})", file, std::strerror(errno)); } xfree(file); /* bak charsave file */ file = (char*)xmalloc(std::strlen(prefs_get_bak_charinfo_dir()) + 1 + std::strlen(account) + 1 + std::strlen(charname) + 1); d2char_get_bak_infofile_name(file, account, charname); if (access(file, F_OK) == 0) { if (std::remove(file) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to delete bak charinfo file \"{}\" (std::remove: {})", file, std::strerror(errno)); } } xfree(file); /* bak charinfo file */ file = (char*)xmalloc(std::strlen(prefs_get_bak_charsave_dir()) + 1 + std::strlen(charname) + 1); d2char_get_bak_savefile_name(file, charname); if (access(file, F_OK) == 0) { if (std::remove(file) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to delete bak charsave file \"{}\" (std::remove: {})", file, std::strerror(errno)); } } xfree(file); eventlog(eventlog_level_info, __FUNCTION__, "character {}(*{}) deleted", charname, account); return 0; } extern int d2char_get_summary(char const * account, char const * charname, t_d2charinfo_summary * charinfo) { t_d2charinfo_file data; ASSERT(account, -1); ASSERT(charname, -1); ASSERT(charinfo, -1); if (d2charinfo_load(account, charname, &data) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error loading character {}(*{})", charname, account); return -1; } std::memcpy(charinfo, &data.summary, sizeof(data.summary)); eventlog(eventlog_level_info, __FUNCTION__, "character {} difficulty {} expansion {} hardcore {} dead {} loaded", charname, d2charinfo_get_difficulty(charinfo), d2charinfo_get_expansion(charinfo), d2charinfo_get_hardcore(charinfo), d2charinfo_get_dead(charinfo)); return 0; } extern int d2charinfo_load(char const * account, char const * charname, t_d2charinfo_file * data) { char * file; int size; if (d2char_check_charname(charname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad character name \"{}\"", charname); return -1; } if (d2char_check_acctname(account) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "got bad account name \"{}\"", account); return -1; } file = (char*)xmalloc(std::strlen(prefs_get_charinfo_dir()) + 1 + std::strlen(account) + 1 + std::strlen(charname) + 1); d2char_get_infofile_name(file, account, charname); size = sizeof(t_d2charinfo_file); if (file_read(file, data, (unsigned int*)&size) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error loading character file {}", file); xfree(file); return -1; } if (size != sizeof(t_d2charinfo_file)) { eventlog(eventlog_level_error, __FUNCTION__, "got bad charinfo file {} (length {})", charname, size); xfree(file); return -1; } d2char_portrait_init(&data->portrait); if (d2charinfo_check(data) < 0) { xfree(file); return -1; } if (!(charstatus_get_ladder(bn_int_get(data->summary.charstatus)))) { bn_byte_set(&data->portrait.ladder, D2CHARINFO_PORTRAIT_PADBYTE); xfree(file); return 0; } unsigned int ladder_time = prefs_get_ladder_start_time(); if ((ladder_time > 0) && bn_int_get(data->header.create_time) < ladder_time) { char buffer[MAX_SAVEFILE_SIZE]; unsigned int status_offset; unsigned char status; unsigned int charstatus; unsigned int size; unsigned int version; unsigned int checksum; std::FILE * fp; eventlog(eventlog_level_info, __FUNCTION__, "{}(*{}) was created in old ladder season, set to non-ladder", charname, account); if (!(fp = std::fopen(file, "wb"))) { eventlog(eventlog_level_error, __FUNCTION__, "charinfo file \"{}\" does not exist for account \"{}\"", file, account); xfree(file); return 0; } xfree(file); charstatus = bn_int_get(data->summary.charstatus); charstatus_set_ladder(charstatus, 0); bn_int_set(&data->summary.charstatus, charstatus); status = bn_byte_get(data->portrait.status); charstatus_set_ladder(status, 0); bn_byte_set(&data->portrait.status, status); bn_byte_set(&data->portrait.ladder, D2CHARINFO_PORTRAIT_PADBYTE); if (std::fwrite(data, 1, sizeof(*data), fp) != sizeof(*data)) { eventlog(eventlog_level_error, __FUNCTION__, "error writing charinfo file for character \"{}\" (std::fwrite: {})", charname, std::strerror(errno)); std::fclose(fp); return 0; } std::fclose(fp); file = (char*)xmalloc(std::strlen(prefs_get_charsave_dir()) + 1 + std::strlen(charname) + 1); d2char_get_savefile_name(file, charname); if (!(fp = std::fopen(file, "rb+"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open charsave file \"{}\" for reading and writing (std::fopen: {})", file, std::strerror(errno)); xfree(file); return 0; } xfree(file); size = std::fread(buffer, 1, sizeof(buffer), fp); if (!std::feof(fp)) { eventlog(eventlog_level_error, __FUNCTION__, "error reading charsave file for character \"{}\" (std::fread: {})", charname, std::strerror(errno)); std::fclose(fp); return 0; } version = bn_int_get((bn_basic*)(buffer + D2CHARSAVE_VERSION_OFFSET)); if (version >= 0x5C) { status_offset = D2CHARSAVE_STATUS_OFFSET_109; } else { status_offset = D2CHARSAVE_STATUS_OFFSET; } status = bn_byte_get((bn_basic*)(buffer + status_offset)); charstatus_set_ladder(status, 0); /* FIXME: shouldn't abuse bn_*_set()... what's the best way to do this? */ bn_byte_set((bn_byte *)(buffer + status_offset), status); if (version >= 0x5C) { checksum = d2charsave_checksum((unsigned char*)buffer, size, D2CHARSAVE_CHECKSUM_OFFSET); bn_int_set((bn_int *)(buffer + D2CHARSAVE_CHECKSUM_OFFSET), checksum); } std::fseek(fp, 0, SEEK_SET); if (std::fwrite(buffer, 1, size, fp) != size) { eventlog(eventlog_level_error, __FUNCTION__, "error writing charsave file for character {} (std::fwrite: {})", charname, std::strerror(errno)); std::fclose(fp); return 0; } std::fclose(fp); } else { bn_byte_set(&data->portrait.ladder, 1); xfree(file); } return 0; } extern int d2charinfo_check(t_d2charinfo_file * data) { ASSERT(data, -1); if (bn_int_get(data->header.magicword) != D2CHARINFO_MAGICWORD) { eventlog(eventlog_level_error, __FUNCTION__, "info data check failed (header 0x{:08X})", bn_int_get(data->header.magicword)); return -1; } if (bn_int_get(data->header.version) != D2CHARINFO_VERSION) { eventlog(eventlog_level_error, __FUNCTION__, "info data check failed (version 0x{:08X})", bn_int_get(data->header.version)); return -1; } return 0; } extern int d2char_portrait_init(t_d2charinfo_portrait * portrait) { unsigned int i; unsigned char * p; p = (unsigned char *)portrait; for (i = 0; i < sizeof(t_d2charinfo_portrait); i++) { if (!p[i]) p[i] = D2CHARINFO_PORTRAIT_PADBYTE; } p[i - 1] = '\0'; return 0; } extern int d2char_get_portrait(char const * account, char const * charname, t_d2charinfo_portrait * portrait) { t_d2charinfo_file data; ASSERT(charname, -1); ASSERT(account, -1); ASSERT(portrait, -1); if (d2charinfo_load(account, charname, &data) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error loading character {}(*{})", charname, account); return -1; } std::strcpy((char *)portrait, (char *)&data.portrait); return 0; } extern int d2char_check_charname(char const * name) { unsigned int i; unsigned char ch; if (!name) return -1; if (!std::isalpha((int)name[0])) return -1; for (i = 1; i <= MAX_CHARNAME_LEN; i++) { ch = name[i]; if (ch == '\0') break; if (std::isalpha(ch)) continue; if (ch == '-') continue; if (ch == '_') continue; if (ch == '.') continue; return -1; } if (i >= MIN_CHARNAME_LEN || i <= MAX_CHARNAME_LEN) return 0; return -1; } extern int d2char_check_acctname(char const * name) { unsigned int i; unsigned char ch; if (!name) return -1; if (!std::isalnum((unsigned char)name[0])) return -1; for (i = 1; i <= MAX_CHARNAME_LEN; i++) { ch = name[i]; if (ch == '\0') break; if (std::isalnum(ch)) continue; if (std::strchr(prefs_get_d2cs_account_allowed_symbols(), ch)) continue; return -1; } if (i >= MIN_USERNAME_LEN || i <= MAX_USERNAME_LEN) return 0; return -1; } extern int d2char_get_savefile_name(char * filename, char const * charname) { char tmpchar[MAX_CHARNAME_LEN]; ASSERT(filename, -1); ASSERT(charname, -1); std::strncpy(tmpchar, charname, sizeof(tmpchar)); tmpchar[sizeof(tmpchar)-1] = '\0'; strtolower(tmpchar); std::sprintf(filename, "%s/%s", prefs_get_charsave_dir(), tmpchar); return 0; } extern int d2char_get_bak_savefile_name(char * filename, char const * charname) { char tmpchar[MAX_CHARNAME_LEN]; ASSERT(filename, -1); ASSERT(charname, -1); std::strncpy(tmpchar, charname, sizeof(tmpchar)); tmpchar[sizeof(tmpchar)-1] = '\0'; strtolower(tmpchar); std::sprintf(filename, "%s/%s", prefs_get_bak_charsave_dir(), tmpchar); return 0; } extern int d2char_get_infodir_name(char * filename, char const * account) { char tmpacct[MAX_USERNAME_LEN]; ASSERT(filename, -1); ASSERT(account, -1); std::strncpy(tmpacct, account, sizeof(tmpacct)); tmpacct[sizeof(tmpacct)-1] = '\0'; strtolower(tmpacct); std::sprintf(filename, "%s/%s", prefs_get_charinfo_dir(), tmpacct); return 0; } extern int d2char_get_infofile_name(char * filename, char const * account, char const * charname) { char tmpchar[MAX_CHARNAME_LEN]; char tmpacct[MAX_USERNAME_LEN]; ASSERT(filename, -1); ASSERT(account, -1); ASSERT(charname, -1); std::strncpy(tmpchar, charname, sizeof(tmpchar)); tmpchar[sizeof(tmpchar)-1] = '\0'; strtolower(tmpchar); std::strncpy(tmpacct, account, sizeof(tmpacct)); tmpchar[sizeof(tmpacct)-1] = '\0'; strtolower(tmpacct); std::sprintf(filename, "%s/%s/%s", prefs_get_charinfo_dir(), tmpacct, tmpchar); return 0; } extern int d2char_get_bak_infofile_name(char * filename, char const * account, char const * charname) { char tmpchar[MAX_CHARNAME_LEN]; char tmpacct[MAX_USERNAME_LEN]; ASSERT(filename, -1); ASSERT(account, -1); ASSERT(charname, -1); std::strncpy(tmpchar, charname, sizeof(tmpchar)); tmpchar[sizeof(tmpchar)-1] = '\0'; strtolower(tmpchar); std::strncpy(tmpacct, account, sizeof(tmpacct)); tmpchar[sizeof(tmpacct)-1] = '\0'; strtolower(tmpacct); std::sprintf(filename, "%s/%s/%s", prefs_get_bak_charinfo_dir(), tmpacct, tmpchar); return 0; } extern unsigned int d2charinfo_get_ladder(t_d2charinfo_summary const * charinfo) { ASSERT(charinfo, 0); return charstatus_get_ladder(bn_int_get(charinfo->charstatus)); } extern unsigned int d2charinfo_get_expansion(t_d2charinfo_summary const * charinfo) { ASSERT(charinfo, 0); return charstatus_get_expansion(bn_int_get(charinfo->charstatus)); } extern unsigned int d2charinfo_get_level(t_d2charinfo_summary const * charinfo) { ASSERT(charinfo, 0); return bn_int_get(charinfo->charlevel); } extern unsigned int d2charinfo_get_class(t_d2charinfo_summary const * charinfo) { ASSERT(charinfo, 0); return bn_int_get(charinfo->charclass); } extern unsigned int d2charinfo_get_hardcore(t_d2charinfo_summary const * charinfo) { ASSERT(charinfo, 0); return charstatus_get_hardcore(bn_int_get(charinfo->charstatus)); } extern unsigned int d2charinfo_get_dead(t_d2charinfo_summary const * charinfo) { ASSERT(charinfo, 0); return charstatus_get_dead(bn_int_get(charinfo->charstatus)); } extern unsigned int d2charinfo_get_difficulty(t_d2charinfo_summary const * charinfo) { unsigned int difficulty; ASSERT(charinfo, 0); if (d2charinfo_get_expansion(charinfo)) { difficulty = charstatus_get_difficulty_expansion(bn_int_get(charinfo->charstatus)); } else { difficulty = charstatus_get_difficulty(bn_int_get(charinfo->charstatus)); } if (difficulty > 2) difficulty = 2; return difficulty; } /* those functions should move to util.c */ extern int file_read(char const * filename, void * data, unsigned int * size) { std::FILE * fp; unsigned int n; ASSERT(filename, -1); ASSERT(data, -1); ASSERT(size, -1); if (!(fp = std::fopen(filename, "rb"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for reading (std::fopen: {})", filename, std::strerror(errno)); return -1; } std::fseek(fp, 0, SEEK_END); /* FIXME: check return value */ n = std::ftell(fp); n = std::min(*size, n); std::rewind(fp); /* FIXME: check return value */ if (std::fread(data, 1, n, fp) != n) { eventlog(eventlog_level_error, __FUNCTION__, "error reading file \"{}\" (std::fread: {})", filename, std::strerror(errno)); std::fclose(fp); return -1; } if (std::fclose(fp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close file \"{}\" after reading (std::fclose: {})", filename, std::strerror(errno)); return -1; } *size = n; return 0; } extern int file_write(char const * filename, void * data, unsigned int size) { std::FILE * fp; ASSERT(filename, -1); ASSERT(data, -1); ASSERT(size, -1); if (!(fp = std::fopen(filename, "wb"))) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file \"{}\" for writing (std::fopen: {})", filename, std::strerror(errno)); return -1; } if (std::fwrite(data, 1, size, fp) != size) { eventlog(eventlog_level_error, __FUNCTION__, "error writing file \"{}\" (std::fwrite: {})", filename, std::strerror(errno)); std::fclose(fp); return -1; } if (std::fclose(fp) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not close file \"{}\" after writing (std::fclose: {})", filename, std::strerror(errno)); return -1; } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2charfile.h000066400000000000000000000122131462640461400177750ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CHARFILE_H #define INCLUDED_D2CHARFILE_H /* only used in char convert */ #define D2CHARSAVE_VERSION_OFFSET 0x04 #define D2CHARSAVE_CHECKSUM_OFFSET 0x0C /* newbie.save offset or old version */ #define D2CHARSAVE_STATUS_OFFSET 0x18 #define D2CHARSAVE_CLASS_OFFSET 0x22 #define D2CHARSAVE_CHARNAME_OFFSET 0x08 /* 1.09 or later version (1.10) */ #define D2CHARSAVE_CLASS_OFFSET_109 0x28 #define D2CHARSAVE_STATUS_OFFSET_109 0x24 #define D2CHARSAVE_CHARNAME_OFFSET_109 0x14 /* unused */ #define D2CHARSAVE_LEVEL_OFFSET 0x24 #define D2CHAR_MAX_CLASS 0x06 #define D2CHARINFO_PORTRAIT_PADBYTE 0xff #define D2CHARINFO_PORTRAIT_HEADER 0x8084 #define D2CHARINFO_PORTRAIT_MASK 0x80 #define D2CHARINFO_STATUS_FLAG_INIT 0x01 #define D2CHARINFO_STATUS_FLAG_EXPANSION 0x20 #define D2CHARINFO_STATUS_FLAG_LADDER 0x40 #define D2CHARINFO_STATUS_FLAG_HARDCORE 0x04 #define D2CHARINFO_STATUS_FLAG_DEAD 0x08 #define D2CHARINFO_STATUS_FLAG_INIT_MASK (D2CHARINFO_STATUS_FLAG_INIT | \ D2CHARINFO_STATUS_FLAG_EXPANSION| D2CHARINFO_STATUS_FLAG_LADDER |D2CHARINFO_STATUS_FLAG_HARDCORE) #include "bit.h" #define charstatus_set_init(status,n) BIT_SET_CLR_FLAG(status, D2CHARINFO_STATUS_FLAG_INIT, n) #define charstatus_set_expansion(status,n) BIT_SET_CLR_FLAG(status, D2CHARINFO_STATUS_FLAG_EXPANSION, n) #define charstatus_set_ladder(status,n) BIT_SET_CLR_FLAG(status, D2CHARINFO_STATUS_FLAG_LADDER, n) #define charstatus_set_hardcore(status,n) BIT_SET_CLR_FLAG(status, D2CHARINFO_STATUS_FLAG_HARDCORE, n) #define charstatus_set_dead(status,n) BIT_SET_CLR_FLAG(status, D2CHARINFO_STATUS_FLAG_DEAD, n) #define charstatus_get_init(status) BIT_TST_FLAG(status, D2CHARINFO_STATUS_FLAG_INIT) #define charstatus_get_expansion(status) BIT_TST_FLAG(status, D2CHARINFO_STATUS_FLAG_EXPANSION) #define charstatus_get_ladder(status) BIT_TST_FLAG(status, D2CHARINFO_STATUS_FLAG_LADDER) #define charstatus_get_hardcore(status) BIT_TST_FLAG(status, D2CHARINFO_STATUS_FLAG_HARDCORE) #define charstatus_get_dead(status) BIT_TST_FLAG(status, D2CHARINFO_STATUS_FLAG_DEAD) #define charstatus_get_difficulty(status) ((( status >> 0x08) & 0x0f )/4) /* number of act = 4 */ #define charstatus_get_difficulty_expansion(status) ((( status >> 0x08) & 0x0f )/5) /* number of act = 5 */ #ifndef JUST_NEED_TYPES #include "common/d2cs_d2gs_character.h" namespace pvpgn { namespace d2cs { extern int d2char_create(char const * account, char const * charname, unsigned char chclass, unsigned short status); extern int d2char_delete(char const * account, char const * charname); extern int d2char_get_summary(char const * account, char const * charname,t_d2charinfo_summary * charinfo); extern int d2char_get_portrait(char const * account, char const * filename, t_d2charinfo_portrait * portrait); extern int d2char_portrait_init(t_d2charinfo_portrait * portrait); extern int d2charinfo_load(char const * account, char const * charname, t_d2charinfo_file * data); extern int d2charinfo_check(t_d2charinfo_file * data); extern unsigned int d2charinfo_get_expansion(t_d2charinfo_summary const * charinfo); extern unsigned int d2charinfo_get_level(t_d2charinfo_summary const * charinfo); extern unsigned int d2charinfo_get_class(t_d2charinfo_summary const * charinfo); extern unsigned int d2charinfo_get_hardcore(t_d2charinfo_summary const * charinfo); extern unsigned int d2charinfo_get_ladder(t_d2charinfo_summary const * charinfo); extern unsigned int d2charinfo_get_dead(t_d2charinfo_summary const * charinfo); extern unsigned int d2charinfo_get_difficulty(t_d2charinfo_summary const * charinfo); extern int d2char_convert(char const * account, char const * charname); extern int d2char_find(char const * account, char const * charname); extern int d2char_get_savefile_name(char * filename,char const * charname); extern int d2char_get_infofile_name(char * filename,char const * account, char const * charname); extern int d2char_get_bak_savefile_name(char * filename,char const * charname); extern int d2char_get_bak_infofile_name(char * filename,char const * account, char const * charname); extern int d2char_get_infodir_name(char * filename,char const * account); extern int d2char_check_acctname(char const * name); extern int d2char_check_charname(char const * name); extern int file_write(char const * filename, void * data, unsigned int size); extern int file_read(char const * filename, void * data, unsigned int * size); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2charlist.cpp000066400000000000000000000061401462640461400203660ustar00rootroot00000000000000/* * Copyright (C) 2004 ls_sons (ls@gamelife.org) * Copyright (C) 2004 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "d2charlist.h" #include #include "compat/strcasecmp.h" #include "compat/strncasecmp.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { extern int d2charlist_add_char(t_elist * list_head, t_d2charinfo_file * charinfo, unsigned int expiration_time) { t_d2charlist * charlist, *ccharlist; char const * d2char_sort; t_elist * curr; d2char_sort = prefs_get_charlist_sort(); charlist = (t_d2charlist*)xmalloc(sizeof(t_d2charlist)); charlist->charinfo = charinfo; charlist->expiration_time = expiration_time; if (elist_empty(list_head)) elist_add(list_head, &charlist->list); else { if (strcasecmp(d2char_sort, "name") == 0) { elist_for_each(curr, list_head) { ccharlist = elist_entry(curr, t_d2charlist, list); if (strncasecmp((char*)charinfo->header.charname, (char*)ccharlist->charinfo->header.charname, std::strlen((char*)charinfo->header.charname)) < 0) break; } elist_add_tail(curr, &charlist->list); } else if (strcasecmp(d2char_sort, "ctime") == 0) { elist_for_each(curr, list_head) { ccharlist = elist_entry(curr, t_d2charlist, list); if (bn_int_get(charinfo->header.create_time) < bn_int_get(ccharlist->charinfo->header.create_time)) break; } elist_add_tail(curr, &charlist->list); } else if (strcasecmp(d2char_sort, "mtime") == 0) { elist_for_each(curr, list_head) { ccharlist = elist_entry(curr, t_d2charlist, list); if (bn_int_get(charinfo->header.last_time) < bn_int_get(ccharlist->charinfo->header.last_time)) break; } elist_add_tail(curr, &charlist->list); } else if (strcasecmp(d2char_sort, "level") == 0) { elist_for_each(curr, list_head) { ccharlist = elist_entry(curr, t_d2charlist, list); if (bn_int_get(charinfo->summary.experience) < bn_int_get(ccharlist->charinfo->summary.experience)) break; } elist_add_tail(curr, &charlist->list); } else { eventlog(eventlog_level_debug, __FUNCTION__, "unsorted"); elist_add_tail(list_head, &charlist->list); } } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2charlist.h000066400000000000000000000022051462640461400200310ustar00rootroot00000000000000/* * Copyright (C) 2004 ls_sons (ls@gamelife.org) * Copyright (C) 2004 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/elist.h" #include "common/d2cs_d2gs_character.h" namespace pvpgn { namespace d2cs { typedef struct d2charlist{ t_d2charinfo_file *charinfo; int expiration_time; t_elist list; } t_d2charlist; extern int d2charlist_add_char(t_elist *, t_d2charinfo_file *i, unsigned int); } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2gs.cpp000066400000000000000000000262031462640461400171700ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "d2gs.h" #include #include #include #include #include "compat/psock.h" #include "common/addr.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/introtate.h" #include "prefs.h" #include "game.h" #include "net.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static t_list * d2gslist_head = NULL; static unsigned int d2gs_id = 0; static unsigned int total_d2gs = 0; extern t_list * d2gslist(void) { return d2gslist_head; } extern int d2gslist_create(void) { d2gslist_head = list_create(); return d2gslist_reload(prefs_get_d2gs_list()); } extern int d2gslist_reload(char const * gslist) { t_addrlist * gsaddrs; t_d2gs * gs; if (!d2gslist_head) return -1; BEGIN_LIST_TRAVERSE_DATA(d2gslist_head, gs, t_d2gs) { BIT_CLR_FLAG(gs->flag, D2GS_FLAG_VALID); } END_LIST_TRAVERSE_DATA() gsaddrs = addrlist_create(gslist, INADDR_ANY, 0); if (gsaddrs) { t_elem const *acurr; t_addr * curr_laddr; LIST_TRAVERSE_CONST(gsaddrs, acurr) { curr_laddr = (t_addr*)elem_get_data(acurr); if (!curr_laddr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL value in gslist"); continue; } if (!(gs = d2gslist_find_gs_by_ip(addr_get_ip(curr_laddr)))) gs = d2gs_create(addr_num_to_ip_str(addr_get_ip(curr_laddr))); if (gs) BIT_SET_FLAG(gs->flag, D2GS_FLAG_VALID); } addrlist_destroy(gsaddrs); } BEGIN_LIST_TRAVERSE_DATA(d2gslist_head, gs, t_d2gs) { if (!BIT_TST_FLAG(gs->flag, D2GS_FLAG_VALID)) { d2gs_destroy(gs, &curr_elem_); } } END_LIST_TRAVERSE_DATA() return 0; } extern int d2gslist_destroy(void) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head, gs, t_d2gs) { d2gs_destroy(gs, (t_elem **)&curr_elem_); } END_LIST_TRAVERSE_DATA_CONST() d2cs_connlist_reap(); if (list_destroy(d2gslist_head) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error destroy d2gs list"); return -1; } d2gslist_head = NULL; return 0; } extern t_d2gs * d2gslist_find_gs_by_ip(unsigned int ip) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head, gs, t_d2gs) { if (gs->ip == ip) return gs; } END_LIST_TRAVERSE_DATA_CONST() return NULL; } extern t_d2gs * d2gslist_find_gs(unsigned int id) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head, gs, t_d2gs) { if (gs->id == id) return gs; } END_LIST_TRAVERSE_DATA_CONST() return NULL; } extern t_d2gs * d2gs_create(char const * ipaddr) { t_d2gs * gs; unsigned int ip; ASSERT(ipaddr, NULL); if ((ip = net_inet_addr(ipaddr)) == ~0U) { eventlog(eventlog_level_error, __FUNCTION__, "got bad ip address {}", ipaddr); return NULL; } if (d2gslist_find_gs_by_ip(ntohl(ip))) { eventlog(eventlog_level_error, __FUNCTION__, "game server {} already in list", ipaddr); return NULL; } gs = (t_d2gs*)xmalloc(sizeof(t_d2gs)); gs->ip = ntohl(ip); gs->id = ++d2gs_id; gs->active = 0; gs->token = 0; gs->state = d2gs_state_none; gs->gamenum = 0; gs->maxgame = 0; gs->connection = NULL; if (list_append_data(d2gslist_head, gs) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error add gs to list"); xfree(gs); return NULL; } eventlog(eventlog_level_info, __FUNCTION__, "added game server {} (id: {}) to list", ipaddr, gs->id); return gs; } extern int d2gs_destroy(t_d2gs * gs, t_elem ** curr) { ASSERT(gs, -1); if (list_remove_data(d2gslist_head, gs, curr) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error remove gs from list"); return -1; } if (gs->active && gs->connection) { d2cs_conn_set_state(gs->connection, conn_state_destroy); d2gs_deactive(gs, gs->connection); } eventlog(eventlog_level_info, __FUNCTION__, "removed game server {} (id: {}) from list", addr_num_to_ip_str(gs->ip), gs->id); xfree(gs); return 0; } extern t_d2gs * d2gslist_get_server_by_id(unsigned int id) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head, gs, t_d2gs) { if (gs->id == id) return gs; } END_LIST_TRAVERSE_DATA_CONST() return NULL; } extern t_d2gs * d2gslist_choose_server(void) { t_d2gs * gs; t_d2gs * ogs; unsigned int percent; unsigned int min_percent = 100; ogs = NULL; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head, gs, t_d2gs) { if (!gs->active) continue; if (!gs->connection) continue; if (gs->state != d2gs_state_authed) continue; if (!gs->maxgame) continue; if (gs->gamenum >= gs->maxgame) continue; percent = 100 * gs->gamenum / gs->maxgame; if (percent < min_percent) { min_percent = percent; ogs = gs; } } END_LIST_TRAVERSE_DATA_CONST() return ogs; } extern int d2gs_set_state(t_d2gs * gs, t_d2gs_state state) { ASSERT(gs, -1); gs->state = state; return 0; } extern t_d2gs_state d2gs_get_state(t_d2gs const * gs) { ASSERT(gs, d2gs_state_none); return gs->state; } extern int d2gs_add_gamenum(t_d2gs * gs, int number) { ASSERT(gs, -1); gs->gamenum += number; return 0; } extern unsigned int d2gs_get_gamenum(t_d2gs const * gs) { ASSERT(gs, 0); return gs->gamenum; } extern int d2gs_set_maxgame(t_d2gs * gs, unsigned int maxgame) { ASSERT(gs, -1); gs->maxgame = maxgame; return 0; } extern unsigned int d2gs_get_maxgame(t_d2gs const * gs) { ASSERT(gs, 0); return gs->maxgame; } extern unsigned int d2gs_get_id(t_d2gs const * gs) { ASSERT(gs, 0); return gs->id; } extern unsigned int d2gs_get_ip(t_d2gs const * gs) { ASSERT(gs, 0); return gs->ip; } extern unsigned int d2gs_get_token(t_d2gs const * gs) { return gs->token; } extern unsigned int d2gs_make_token(t_d2gs * gs) { return ((unsigned int)std::rand()) ^ ((++(gs->token)) + ((unsigned int)std::time(NULL))); } extern t_connection * d2gs_get_connection(t_d2gs const * gs) { ASSERT(gs, NULL); return gs->connection; } extern int d2gs_active(t_d2gs * gs, t_connection * c) { ASSERT(gs, -1); ASSERT(c, -1); if (gs->active && gs->connection) { eventlog(eventlog_level_warn, __FUNCTION__, "game server {} is already actived, deactive previous connection first", gs->id); d2gs_deactive(gs, gs->connection); } total_d2gs++; eventlog(eventlog_level_info, __FUNCTION__, "game server {} (id: {}) actived ({} total)", addr_num_to_addr_str(d2cs_conn_get_addr(c), d2cs_conn_get_port(c)), gs->id, total_d2gs); gs->state = d2gs_state_authed; gs->connection = c; gs->active = 1; gs->gamenum = 0; gs->maxgame = 0; return 0; } extern int d2gs_deactive(t_d2gs * gs, t_connection * c) { t_game * game; ASSERT(gs, -1); if (!gs->active || !gs->connection) { eventlog(eventlog_level_warn, __FUNCTION__, "game server {} is not actived yet", gs->id); return -1; } if (gs->connection != c) { eventlog(eventlog_level_debug, __FUNCTION__, "game server {} connection mismatch,ignore it", gs->id); return 0; } total_d2gs--; eventlog(eventlog_level_info, __FUNCTION__, "game server {} (id: {}) deactived ({} left)", addr_num_to_addr_str(d2cs_conn_get_addr(gs->connection), d2cs_conn_get_port(gs->connection)), gs->id, total_d2gs); gs->state = d2gs_state_none; gs->connection = NULL; gs->active = 0; gs->maxgame = 0; eventlog(eventlog_level_info, __FUNCTION__, "destroying all games on game server {}", gs->id); BEGIN_LIST_TRAVERSE_DATA(d2cs_gamelist(), game, t_game) { if (game_get_d2gs(game) == gs) game_destroy(game, &curr_elem_); } END_LIST_TRAVERSE_DATA() if (gs->gamenum != 0) { eventlog(eventlog_level_error, __FUNCTION__, "game server {} deactived but still with games left", gs->id); } gs->gamenum = 0; return 0; } extern unsigned int d2gs_calc_checksum(t_connection * c) { unsigned int sessionnum, checksum, port, addr; unsigned int i, len, ch; char const * realmname; char const * password; ASSERT(c, 0); sessionnum = d2cs_conn_get_sessionnum(c); checksum = prefs_get_d2gs_checksum(); port = d2cs_conn_get_port(c); addr = d2cs_conn_get_addr(c); realmname = prefs_get_realmname(); password = prefs_get_d2gs_password(); len = std::strlen(realmname); for (i = 0; i < len; i++) { ch = (unsigned int)(unsigned char)realmname[i]; checksum ^= ROTL(sessionnum, i, (std::numeric_limits::digits)); checksum ^= ROTL(port, ch, (std::numeric_limits::digits)); } len = std::strlen(password); for (i = 0; i < len; i++) { ch = (unsigned int)(unsigned char)password[i]; checksum ^= ROTL(sessionnum, i, (std::numeric_limits::digits)); checksum ^= ROTL(port, ch, (std::numeric_limits::digits)); } checksum ^= addr; return checksum; } extern int d2gs_keepalive(void) { t_packet * packet; t_d2gs * gs; if (!(packet = packet_create(packet_class_d2gs))) { eventlog(eventlog_level_error, __FUNCTION__, "error creating packet"); return -1; } packet_set_size(packet, sizeof(t_d2cs_d2gs_echoreq)); packet_set_type(packet, D2CS_D2GS_ECHOREQ); /* FIXME: sequence number not set */ bn_int_set(&packet->u.d2cs_d2gs.h.seqno, 0); BEGIN_LIST_TRAVERSE_DATA(d2gslist_head, gs, t_d2gs) { if (gs->active && gs->connection) { conn_push_outqueue(gs->connection, packet); } } END_LIST_TRAVERSE_DATA() packet_del_ref(packet); return 0; } extern int d2gs_restart_all_gs(void) { t_packet * packet; t_d2gs * gs; if (!(packet = packet_create(packet_class_d2gs))) { eventlog(eventlog_level_error, __FUNCTION__, "error creating packet"); return -1; } packet_set_size(packet, sizeof(t_d2cs_d2gs_control)); packet_set_type(packet, D2CS_D2GS_CONTROL); /* FIXME: sequence number not set */ bn_int_set(&packet->u.d2cs_d2gs.h.seqno, 0); bn_int_set(&packet->u.d2cs_d2gs_control.cmd, D2CS_D2GS_CONTROL_CMD_RESTART); bn_int_set(&packet->u.d2cs_d2gs_control.value, prefs_get_d2gs_restart_delay()); BEGIN_LIST_TRAVERSE_DATA(d2gslist_head, gs, t_d2gs) { if (gs->connection) { conn_push_outqueue(gs->connection, packet); } } END_LIST_TRAVERSE_DATA() packet_del_ref(packet); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2gs.h000066400000000000000000000051661462640461400166420ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2GS_H #define INCLUDED_D2GS_H #include "common/list.h" #include "connection.h" namespace pvpgn { namespace d2cs { typedef enum { d2gs_state_none, d2gs_state_connected, d2gs_state_authed } t_d2gs_state; typedef struct d2gs { unsigned int ip; unsigned int id; unsigned int flag; unsigned int active; unsigned int token; t_d2gs_state state; unsigned int maxgame; unsigned int gamenum; t_connection * connection; } t_d2gs; #define D2GS_FLAG_VALID 0x01 extern t_list * d2gslist(void); extern int d2gslist_create(void); extern int d2gslist_destroy(void); extern int d2gslist_reload(char const * gslist); extern t_d2gs * d2gs_create(char const * ip); extern int d2gs_destroy(t_d2gs * gs,t_elem ** curr); extern t_d2gs * d2gslist_get_server_by_id(unsigned int id); extern t_d2gs * d2gslist_choose_server(void); extern t_d2gs * d2gslist_find_gs(unsigned int id); extern t_d2gs * d2gslist_find_gs_by_ip(unsigned int ip); extern unsigned int d2gs_get_id(t_d2gs const * gs); extern unsigned int d2gs_get_ip(t_d2gs const * gs); extern t_d2gs_state d2gs_get_state(t_d2gs const * gs); extern int d2gs_set_state(t_d2gs * gs, t_d2gs_state state); extern unsigned int d2gs_get_maxgame(t_d2gs const * gs); extern int d2gs_set_maxgame(t_d2gs * gs,unsigned int maxgame); extern unsigned int d2gs_get_gamenum(t_d2gs const * gs); extern int d2gs_add_gamenum(t_d2gs * gs,int number); extern unsigned int d2gs_get_token(t_d2gs const * gs); extern unsigned int d2gs_make_token(t_d2gs * gs); extern t_connection * d2gs_get_connection(t_d2gs const * gs); extern int d2gs_active(t_d2gs * gs, t_connection * c); extern int d2gs_deactive(t_d2gs * gs, t_connection * c); extern unsigned int d2gs_calc_checksum(t_connection * c); extern int d2gs_keepalive(void); extern int d2gs_restart_all_gs(void); } } #define D2GS_MAJOR_VERSION_MASK 0xffffff00 #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2ladder.cpp000066400000000000000000000206661462640461400200210ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "d2ladder.h" #include #include #include #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/tag.h" #include "prefs.h" #include "d2charfile.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static t_d2ladder * ladder_data = NULL; static unsigned int max_ladder_type = 0; static int d2ladderlist_create(unsigned int maxtype); static int d2ladder_create(unsigned int type, unsigned int len); static int d2ladder_append_ladder(unsigned int type, t_d2ladderfile_ladderinfo * info); static int d2ladder_readladder(void); extern int d2ladder_init(void) { if (d2ladder_readladder() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to initialize ladder data"); return -1; } eventlog(eventlog_level_info, __FUNCTION__, "ladder data initialized"); return 0; } static int d2ladder_readladder(void) { std::FILE * fp; t_d2ladderfile_ladderindex * ladderheader; t_d2ladderfile_ladderinfo * ladderinfo; char * ladderfile; t_d2ladderfile_header header; unsigned int i, n, temp, count, type, number; ladderfile = (char*)xmalloc(std::strlen(prefs_get_ladder_dir()) + 1 + std::strlen(LADDER_FILE_PREFIX) + 1 + std::strlen(CLIENTTAG_DIABLO2DV) + 1); std::sprintf(ladderfile, "%s/%s.%s", prefs_get_ladder_dir(), LADDER_FILE_PREFIX, CLIENTTAG_DIABLO2DV); if (!(fp = std::fopen(ladderfile, "rb"))) { eventlog(eventlog_level_error, __FUNCTION__, "error opening ladder file \"{}\" for reading (std::fopen: {})", ladderfile, std::strerror(errno)); xfree(ladderfile); return -1; } xfree(ladderfile); if (std::fread(&header, 1, sizeof(header), fp) != sizeof(header)) { eventlog(eventlog_level_error, __FUNCTION__, "error reading ladder file"); std::fclose(fp); return -1; } max_ladder_type = bn_int_get(header.maxtype); if (d2ladderlist_create(max_ladder_type) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error create ladder list"); std::fclose(fp); return -1; } temp = max_ladder_type * sizeof(*ladderheader); ladderheader = (t_d2ladderfile_ladderindex*)xmalloc(temp); if (std::fread(ladderheader, 1, temp, fp) != temp) { eventlog(eventlog_level_error, __FUNCTION__, "error read ladder file"); xfree(ladderheader); std::fclose(fp); return -1; } for (i = 0, count = 0; i < max_ladder_type; i++) { type = bn_int_get(ladderheader[i].type); number = bn_int_get(ladderheader[i].number); if (d2ladder_create(type, number) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error create ladder {}", type); continue; } std::fseek(fp, bn_int_get(ladderheader[i].offset), SEEK_SET); temp = number * sizeof(*ladderinfo); ladderinfo = (t_d2ladderfile_ladderinfo*)xmalloc(temp); if (std::fread(ladderinfo, 1, temp, fp) != temp) { eventlog(eventlog_level_error, __FUNCTION__, "error read ladder file"); xfree(ladderinfo); continue; } for (n = 0; n < number; n++) { d2ladder_append_ladder(type, ladderinfo + n); } xfree(ladderinfo); if (number) count++; } xfree(ladderheader); std::fclose(fp); eventlog(eventlog_level_info, __FUNCTION__, "ladder file loaded successfully ({} types {} maxtype)", count, max_ladder_type); return 0; } static int d2ladderlist_create(unsigned int maxtype) { ladder_data = (t_d2ladder*)xmalloc(maxtype * sizeof(*ladder_data)); std::memset(ladder_data, 0, maxtype * sizeof(*ladder_data)); return 0; } extern int d2ladder_refresh(void) { d2ladder_destroy(); return d2ladder_readladder(); } static int d2ladder_create(unsigned int type, unsigned int len) { if (type > max_ladder_type) { eventlog(eventlog_level_error, __FUNCTION__, "ladder type {} exceed max ladder type {}", type, max_ladder_type); return -1; } ladder_data[type].info = (t_d2cs_client_ladderinfo*)xmalloc(sizeof(t_d2cs_client_ladderinfo)* len); ladder_data[type].len = len; ladder_data[type].type = type; ladder_data[type].curr_len = 0; return 0; } static int d2ladder_append_ladder(unsigned int type, t_d2ladderfile_ladderinfo * info) { t_d2cs_client_ladderinfo * ladderinfo; unsigned short ladderstatus; unsigned short status; unsigned char chclass; if (!info) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL info"); return -1; } if (type > max_ladder_type) { eventlog(eventlog_level_error, __FUNCTION__, "ladder type {} exceed max ladder type {}", type, max_ladder_type); return -1; } if (!ladder_data[type].info) { eventlog(eventlog_level_error, __FUNCTION__, "ladder data info not initialized"); return -1; } if (ladder_data[type].curr_len >= ladder_data[type].len) { eventlog(eventlog_level_error, __FUNCTION__, "ladder data overflow {} > {}", ladder_data[type].curr_len, ladder_data[type].len); return -1; } status = bn_short_get(info->status); chclass = bn_byte_get(info->chclass); ladderstatus = (status & LADDERSTATUS_FLAG_DIFFICULTY); if (charstatus_get_hardcore(status)) { ladderstatus |= LADDERSTATUS_FLAG_HARDCORE; if (charstatus_get_dead(status)) { ladderstatus |= LADDERSTATUS_FLAG_DEAD; } } if (charstatus_get_expansion(status)) { ladderstatus |= LADDERSTATUS_FLAG_EXPANSION; ladderstatus |= std::min(chclass, D2CHAR_EXP_CLASS_MAX); } else { ladderstatus |= std::min(chclass, D2CHAR_CLASS_MAX); } ladderinfo = ladder_data[type].info + ladder_data[type].curr_len; bn_int_set(&ladderinfo->explow, bn_int_get(info->experience)); bn_int_set(&ladderinfo->exphigh, 0); bn_short_set(&ladderinfo->status, ladderstatus); bn_byte_set(&ladderinfo->level, bn_byte_get(info->level)); bn_byte_set(&ladderinfo->u1, 0); std::strncpy(ladderinfo->charname, info->charname, MAX_CHARNAME_LEN); ladder_data[type].curr_len++; return 0; } extern int d2ladder_destroy(void) { unsigned int i; if (ladder_data) { for (i = 0; i < max_ladder_type; i++) { if (ladder_data[i].info) { xfree(ladder_data[i].info); ladder_data[i].info = NULL; } } xfree(ladder_data); ladder_data = NULL; } max_ladder_type = 0; return 0; } extern int d2ladder_get_ladder(unsigned int * from, unsigned int * count, unsigned int type, t_d2cs_client_ladderinfo const * * info) { t_d2ladder * ladder; if (!ladder_data) return -1; ladder = ladder_data + type; if (!ladder->curr_len || !ladder->info) { eventlog(eventlog_level_warn, __FUNCTION__, "ladder type {} not found", type); return -1; } if (ladder->type != type) { eventlog(eventlog_level_error, __FUNCTION__, "got bad ladder data"); return -1; } if (ladder->curr_len < *count) { *from = 0; *count = ladder->curr_len; } else if (*from + *count> ladder->curr_len) { *from = ladder->curr_len - *count; } *info = ladder->info + *from; return 0; } extern int d2ladder_find_character_pos(unsigned int type, char const * charname) { unsigned int i; t_d2ladder * ladder; if (!ladder_data) return -1; ladder = ladder_data + type; if (!ladder->curr_len || !ladder->info) { eventlog(eventlog_level_warn, __FUNCTION__, "ladder type {} not found", type); return -1; } if (ladder->type != type) { eventlog(eventlog_level_error, __FUNCTION__, "got bad ladder data"); return -1; } for (i = 0; i < ladder->curr_len; i++) { if (!strcasecmp(ladder->info[i].charname, charname)) return i; } return -1; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/d2ladder.h000066400000000000000000000027261462640461400174630ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2LADDER_H #define INCLUDED_D2LADDER_H #include "common/bn_type.h" #include "common/d2cs_protocol.h" #include "common/d2cs_d2dbs_ladder.h" namespace pvpgn { namespace d2cs { typedef struct { unsigned int type; unsigned int len; unsigned int curr_len; t_d2cs_client_ladderinfo * info; } t_d2ladder; extern int d2ladder_init(void); extern int d2ladder_refresh(void); extern int d2ladder_destroy(void); extern int d2ladder_get_ladder(unsigned int * from, unsigned int * count, unsigned int type, t_d2cs_client_ladderinfo const * * info); extern int d2ladder_find_character_pos(unsigned int type, char const * charname); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/game.cpp000066400000000000000000000312671462640461400172500ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "game.h" #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static t_list * gamelist_head = NULL; static t_elem const * gamelist_curr_elem = NULL; static unsigned int total_game = 0; static unsigned int game_id = 0; static t_game_charinfo * game_find_character(t_game * game, char const * charname); extern t_list * d2cs_gamelist(void) { return gamelist_head; } extern t_elem const * gamelist_get_curr_elem(void) { return gamelist_curr_elem; } extern void gamelist_set_curr_elem(t_elem const * elem) { gamelist_curr_elem = elem; return; } extern int d2cs_gamelist_create(void) { gamelist_head = list_create(); return 0; } extern int d2cs_gamelist_destroy(void) { t_game * game; BEGIN_LIST_TRAVERSE_DATA(gamelist_head, game, t_game) { game_destroy(game, &curr_elem_); } END_LIST_TRAVERSE_DATA(); if (list_destroy(gamelist_head) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error destroy connection list"); return -1; } gamelist_head = NULL; return 0; } extern t_game * d2cs_gamelist_find_game(char const * gamename) { t_game * game; ASSERT(gamename, NULL); BEGIN_LIST_TRAVERSE_DATA(gamelist_head, game, t_game) { if (!strcasecmp(game->name, gamename)) return game; } END_LIST_TRAVERSE_DATA() return NULL; } extern t_game * gamelist_find_game_by_id(unsigned int id) { t_game * game; BEGIN_LIST_TRAVERSE_DATA(gamelist_head, game, t_game) { if (game->id == id) return game; } END_LIST_TRAVERSE_DATA() return NULL; } extern t_game * gamelist_find_game_by_d2gs_and_id(unsigned int d2gs_id, unsigned int d2gs_gameid) { t_game * game; BEGIN_LIST_TRAVERSE_DATA(gamelist_head, game, t_game) { if (!game->created) continue; if (game->d2gs_gameid != d2gs_gameid) continue; if (d2gs_get_id(game->d2gs) != d2gs_id) continue; return game; } END_LIST_TRAVERSE_DATA() return NULL; } extern t_game * gamelist_find_character(char const * charname) { t_game * game; ASSERT(charname, NULL); BEGIN_LIST_TRAVERSE_DATA(gamelist_head, game, t_game) { if (game_find_character(game, charname)) return game; } END_LIST_TRAVERSE_DATA(); return NULL; } extern void d2cs_gamelist_check_voidgame(void) { t_game * game; std::time_t now; int timeout; timeout = prefs_get_max_game_idletime(); if (!timeout) return; now = std::time(NULL); BEGIN_LIST_TRAVERSE_DATA(gamelist_head, game, t_game) { if (!game->currchar) { if ((now - game->lastaccess_time) > timeout) { eventlog(eventlog_level_info, __FUNCTION__, "game {} is empty too long std::time,destroying it", game->name); game_destroy(game, &curr_elem_); } } } END_LIST_TRAVERSE_DATA() } extern t_game * d2cs_game_create(char const * gamename, char const * gamepass, char const * gamedesc, unsigned int gameflag) { t_game * game; std::time_t now; ASSERT(gamename, NULL); ASSERT(gamepass, NULL); ASSERT(gamedesc, NULL); if (d2cs_gamelist_find_game(gamename)) { eventlog(eventlog_level_error, __FUNCTION__, "game {} already exist", gamename); return NULL; } game = (t_game*)xmalloc(sizeof(t_game)); game->name = xstrdup(gamename); game->pass = xstrdup(gamepass); game->desc = xstrdup(gamedesc); game->charlist = list_create(); now = std::time(NULL); game_id++; if (game_id == 0) game_id = 1; game->id = game_id; game->created = 0; game->create_time = now; game->lastaccess_time = now; game->gameflag = gameflag; game->charlevel = 0; game->leveldiff = 0; game->d2gs_gameid = 0; game->d2gs = NULL; game->maxchar = MAX_CHAR_PER_GAME; game->currchar = 0; list_prepend_data(gamelist_head, game); total_game++; eventlog(eventlog_level_info, __FUNCTION__, "game {} pass={} desc={}gameflag=0x{:08X} created ({} total)", gamename, gamepass, gamedesc, gameflag, total_game); return game; } extern int game_destroy(t_game * game, t_elem ** elem) { t_elem * curr; t_game_charinfo * charinfo; ASSERT(game, -1); if (gamelist_curr_elem && (game == elem_get_data(gamelist_curr_elem))) { gamelist_curr_elem = elem_get_next_const(gamelist_head, gamelist_curr_elem); } if (list_remove_data(gamelist_head, game, elem) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error remove game {} on game list", game->name); return -1; } total_game--; eventlog(eventlog_level_info, __FUNCTION__, "game {} removed from game list ({} left)", game->name, total_game); LIST_TRAVERSE(game->charlist, curr) { if ((charinfo = (t_game_charinfo*)elem_get_data(curr))) { if (charinfo->charname) xfree((void *)charinfo->charname); xfree(charinfo); } list_remove_elem(game->charlist, &curr); } list_destroy(game->charlist); if (game->d2gs) { d2gs_add_gamenum(game->d2gs, -1); gqlist_check_creategame(d2gs_get_maxgame(game->d2gs) - d2gs_get_gamenum(game->d2gs)); } if (game->desc) xfree((void *)game->desc); if (game->pass) xfree((void *)game->pass); if (game->name) xfree((void *)game->name); xfree(game); return 0; } static t_game_charinfo * game_find_character(t_game * game, char const * charname) { t_game_charinfo * charinfo; ASSERT(game, NULL); ASSERT(charname, NULL); if (!game->charlist) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL character list in game {}", game->name); return NULL; } BEGIN_LIST_TRAVERSE_DATA(game->charlist, charinfo, t_game_charinfo) { if (!charinfo->charname) continue; if (!strcmp_charname(charinfo->charname, charname)) return charinfo; } END_LIST_TRAVERSE_DATA() return NULL; } extern int game_add_character(t_game * game, char const * charname, unsigned char chclass, unsigned char level) { t_game_charinfo * charinfo; ASSERT(game, -1); ASSERT(charname, -1); charinfo = game_find_character(game, charname); if (charinfo) { eventlog(eventlog_level_info, __FUNCTION__, "updating character {} (game {}) status", charname, game->name); charinfo->chclass = chclass; charinfo->level = level; return 0; } charinfo = (t_game_charinfo*)xmalloc(sizeof(t_game_charinfo)); charinfo->charname = xstrdup(charname); charinfo->chclass = chclass; charinfo->level = level; list_append_data(game->charlist, charinfo); game->currchar++; game->lastaccess_time = std::time(NULL); eventlog(eventlog_level_info, __FUNCTION__, "added character {} to game {} ({} total)", charname, game->name, game->currchar); return 0; } extern int game_del_character(t_game * game, char const * charname) { t_game_charinfo * charinfo; t_elem * elem; ASSERT(game, -1); ASSERT(charname, -1); if (!(charinfo = game_find_character(game, charname))) { eventlog(eventlog_level_error, __FUNCTION__, "character {} not found in game {}", charname, game->name); return -1; } if (list_remove_data(game->charlist, charinfo, &elem)) { eventlog(eventlog_level_error, __FUNCTION__, "error remove character {} from game {}", charname, game->name); return -1; } if (charinfo->charname) xfree((void *)charinfo->charname); xfree(charinfo); game->currchar--; game->lastaccess_time = std::time(NULL); eventlog(eventlog_level_info, __FUNCTION__, "removed character {} from game {} ({} left)", charname, game->name, game->currchar); return 0; } extern int game_set_d2gs_gameid(t_game * game, unsigned int d2gs_gameid) { ASSERT(game, -1); game->d2gs_gameid = d2gs_gameid; return 0; } extern unsigned int game_get_d2gs_gameid(t_game const * game) { ASSERT(game, 0); return game->d2gs_gameid; } extern unsigned int d2cs_game_get_id(t_game const * game) { ASSERT(game, 0); return game->id; } extern unsigned int game_get_gameflag_ladder(t_game const * game) { ASSERT(game, 0); return gameflag_get_ladder(game->gameflag); } extern int game_set_d2gs(t_game * game, t_d2gs * gs) { ASSERT(game, -1); game->d2gs = gs; return 0; } extern t_d2gs * game_get_d2gs(t_game const * game) { ASSERT(game, NULL); return game->d2gs; } extern int game_set_leveldiff(t_game * game, unsigned int leveldiff) { ASSERT(game, -1); game->leveldiff = leveldiff; return 0; } extern int game_set_charlevel(t_game * game, unsigned int charlevel) { ASSERT(game, -1); game->charlevel = charlevel; return 0; } extern unsigned int game_get_charlevel(t_game const * game) { ASSERT(game, 0); return game->charlevel; } extern unsigned int game_get_leveldiff(t_game const * game) { ASSERT(game, 0); return game->leveldiff; } extern unsigned int game_get_maxlevel(t_game const * game) { int maxlevel; ASSERT(game, 0); maxlevel = game->charlevel + game->leveldiff; if (maxlevel > prefs_get_game_maxlevel()) maxlevel = prefs_get_game_maxlevel(); return maxlevel; } extern unsigned int game_get_minlevel(t_game const * game) { int minlevel; ASSERT(game, 0); minlevel = game->charlevel - game->leveldiff; if (minlevel < 0) minlevel = 0; return minlevel; } extern unsigned int game_get_gameflag_expansion(t_game const * game) { ASSERT(game, 0); return gameflag_get_expansion(game->gameflag); } extern unsigned int game_get_gameflag_hardcore(t_game const * game) { ASSERT(game, 0); return gameflag_get_hardcore(game->gameflag); } extern unsigned int game_get_gameflag_difficulty(t_game const * game) { ASSERT(game, 0); return gameflag_get_difficulty(game->gameflag); } extern int game_set_gameflag_ladder(t_game * game, unsigned int ladder) { ASSERT(game, -1); gameflag_set_ladder(game->gameflag, ladder); return 0; } extern int game_set_gameflag_expansion(t_game * game, unsigned int expansion) { ASSERT(game, -1); gameflag_set_expansion(game->gameflag, expansion); return 0; } extern int game_set_gameflag_hardcore(t_game * game, unsigned int hardcore) { ASSERT(game, -1); gameflag_set_hardcore(game->gameflag, hardcore); return 0; } extern int game_set_gameflag_difficulty(t_game * game, unsigned int difficulty) { ASSERT(game, -1); gameflag_set_difficulty(game->gameflag, difficulty); return 0; } extern unsigned int game_get_created(t_game const * game) { ASSERT(game, 0); return game->created; } extern int game_set_created(t_game * game, unsigned int created) { ASSERT(game, -1); game->created = created; return 0; } extern unsigned int game_get_maxchar(t_game const * game) { ASSERT(game, 0); return game->maxchar; } extern int game_set_maxchar(t_game * game, unsigned int maxchar) { ASSERT(game, -1); game->maxchar = maxchar; return 0; } extern unsigned int game_get_currchar(t_game const * game) { ASSERT(game, 0); return game->currchar; } extern char const * d2cs_game_get_name(t_game const * game) { ASSERT(game, NULL); return game->name; } extern char const * game_get_desc(t_game const * game) { ASSERT(game, NULL); return game->desc; } extern char const * d2cs_game_get_pass(t_game const * game) { ASSERT(game, NULL); return game->pass; } extern unsigned int game_get_gameflag(t_game const * game) { ASSERT(game, 0); return game->gameflag; } extern int d2cs_game_get_create_time(t_game const * game) { ASSERT(game, -1); return game->create_time; } extern int game_set_create_time(t_game * game, int create_time) { ASSERT(game, -1); game->create_time = create_time; return 0; } extern t_list * game_get_charlist(t_game const * game) { ASSERT(game, NULL); return game->charlist; } extern unsigned int gamelist_get_totalgame(void) { return total_game; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/game.h000066400000000000000000000137241462640461400167130ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_GAME_H #define INCLUDED_GAME_H #include "d2gs.h" #include "connection.h" #include "bit.h" namespace pvpgn { namespace d2cs { typedef struct { unsigned char chclass; unsigned char level; char const * charname; } t_game_charinfo; typedef struct { unsigned int id; char const * name; char const * pass; char const * desc; int create_time; int lastaccess_time; unsigned int created; unsigned int gameflag; unsigned int charlevel; unsigned int leveldiff; unsigned int d2gs_gameid; unsigned int maxchar; unsigned int currchar; t_list * charlist; t_d2gs * d2gs; } t_game; #define D2_GAMEFLAG_BETA 0x00000001 #define D2_GAMEFLAG_RELEASE 0x00000002 #define D2_GAMEFLAG_TEMPLATE 0x00000008 #define D2_GAMEFLAG_SINGLE 0x00000010 #define D2_GAMEFLAG_HARDCORE 0x00000800 #define D2_GAMEFLAG_EXPANSION 0x00100000 #define D2_GAMEFLAG_LADDER 0x00200000 #define gameflag_get_difficulty(gameflag) ( (gameflag >> 0x0C) & 0x07 ) #define gameflag_get_expansion(gameflag) BIT_TST_FLAG(gameflag, D2_GAMEFLAG_EXPANSION) #define gameflag_get_ladder(gameflag) BIT_TST_FLAG(gameflag, D2_GAMEFLAG_LADDER) #define gameflag_get_hardcore(gameflag) BIT_TST_FLAG(gameflag, D2_GAMEFLAG_HARDCORE) #define gameflag_get_beta(gameflag) BIT_TST_FLAG(gameflag, D2_GAMEFLAG_BETA) /* true if the game is a beta version with limitations */ #define gameflag_get_release(gameflag) BIT_TST_FLAG(gameflag, D2_GAMEFLAG_RELEASE) /* this bit must always not be zero */ #define gameflag_get_single(gameflag) BIT_TST_FLAG(gameflag, D2_GAMEFLAG_SINGLE) /* true if the game is single mode without party */ #define gameflag_get_template(gameflag) BIT_TST_FLAG(gameflag, D2_GAMEFLAG_TEMPLATE) /* true if use template settings */ #define gameflag_set_difficulty(gameflag,n) ( gameflag |= ((n & 0x07) << 0x0C) ) #define gameflag_set_expansion(gameflag,n) BIT_SET_CLR_FLAG(gameflag, D2_GAMEFLAG_EXPANSION, n) #define gameflag_set_ladder(gameflag,n) BIT_SET_CLR_FLAG(gameflag, D2_GAMEFLAG_LADDER, n) #define gameflag_set_hardcore(gameflag,n) BIT_SET_CLR_FLAG(gameflag, D2_GAMEFLAG_HARDCORE, n) #define gameflag_create(l,e,h,d) (0x04|(e?D2_GAMEFLAG_EXPANSION:0) | (h?D2_GAMEFLAG_HARDCORE:0) | ((d & 0x07) << 0x0c) | (l?D2_GAMEFLAG_LADDER:0)) extern t_list * d2cs_gamelist(void); extern int d2cs_gamelist_destroy(void); extern int d2cs_gamelist_create(void); extern t_game * d2cs_gamelist_find_game(char const * gamename); extern t_game * gamelist_find_game_by_id(unsigned int id); extern t_game * gamelist_find_game_by_d2gs_and_id(unsigned int d2gs_id, unsigned int d2gs_gameid); extern void d2cs_gamelist_check_voidgame(void); extern t_game * d2cs_game_create(char const * gamename, char const * gamepass, char const * gamedesc, unsigned int gameflag); extern int game_destroy(t_game * game, t_elem ** elem); extern int game_add_character(t_game * game, char const * charname, unsigned char chclass, unsigned char level); extern int game_del_character(t_game * game, char const * charname); extern t_d2gs * game_get_d2gs(t_game const * game); extern int game_set_d2gs(t_game * game, t_d2gs * gs); extern int game_set_d2gs_gameid(t_game * game, unsigned int gameid); extern unsigned int game_get_d2gs_gameid(t_game const * game); extern int game_set_id(t_game * game, unsigned int id); extern unsigned int d2cs_game_get_id(t_game const * game); extern int game_set_created(t_game * game, unsigned int created); extern unsigned int game_get_created(t_game const * game); extern int game_set_leveldiff(t_game * game, unsigned int leveldiff); extern int game_set_charlevel(t_game * game, unsigned int charlevel); extern unsigned int game_get_charlevel(t_game const * game); extern unsigned int game_get_leveldiff(t_game const * game); extern unsigned int game_get_maxlevel(t_game const * game); extern unsigned int game_get_minlevel(t_game const * game); extern int game_set_gameflag_ladder(t_game * game, unsigned int ladder); extern int game_set_gameflag_expansion(t_game * game, unsigned int expansion); extern int game_set_gameflag_hardcore(t_game * game, unsigned int hardcore); extern int game_set_gameflag_difficulty(t_game * game, unsigned int difficulty); extern unsigned int game_get_gameflag_ladder(t_game const * game); extern unsigned int game_get_gameflag_expansion(t_game const * game); extern unsigned int game_get_gameflag_hardcore(t_game const * game); extern unsigned int game_get_gameflag_difficulty(t_game const * game); extern int game_set_maxchar(t_game * game, unsigned int maxchar); extern unsigned int game_get_maxchar(t_game const * game); extern unsigned int game_get_currchar(t_game const * game); extern char const * d2cs_game_get_name(t_game const * game); extern char const * d2cs_game_get_pass(t_game const * game); extern char const * game_get_desc(t_game const * game); extern unsigned int game_get_gameflag(t_game const * game); extern int d2cs_game_get_create_time(t_game const * game); extern int game_set_create_time(t_game * game,int create_time); extern t_list * game_get_charlist(t_game const * game); extern t_game * gamelist_find_character(char const * charname); extern unsigned int gamelist_get_totalgame(void); extern t_elem const * gamelist_get_curr_elem(void); extern void gamelist_set_curr_elem(t_elem const * elem); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/gamequeue.cpp000066400000000000000000000113511462640461400203050ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "gamequeue.h" #include #include "compat/strcasecmp.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "connection.h" #include "handle_d2cs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static t_list * gqlist_head = NULL; static unsigned int gqlist_seqno = 0; extern t_list * gqlist(void) { return gqlist_head; } extern int gqlist_create(void) { gqlist_head = list_create(); return 0; } extern int gqlist_destroy(void) { t_gq * gq; BEGIN_LIST_TRAVERSE_DATA(gqlist_head, gq, t_gq) { gq_destroy(gq, (t_elem **)curr_elem_); } END_LIST_TRAVERSE_DATA() if (list_destroy(gqlist_head) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error destroy game queue list"); return -1; } gqlist_head = NULL; return 0; } extern t_gq * gq_create(unsigned int clientid, t_packet * packet, char const * gamename) { t_gq * gq; gq = (t_gq*)xmalloc(sizeof(t_gq)); gq->seqno = ++gqlist_seqno; gq->clientid = clientid; gq->packet = packet; std::strncpy(gq->gamename, gamename, MAX_GAMENAME_LEN); if (packet) packet_add_ref(packet); list_append_data(gqlist_head, gq); return gq; } extern int gq_destroy(t_gq * gq, t_elem ** elem) { ASSERT(gq, -1); if (list_remove_data(gqlist_head, gq, elem) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error remove game queue from list"); return -1; } if (gq->packet) packet_del_ref(gq->packet); xfree(gq); return 0; } extern unsigned int gq_get_clientid(t_gq const * gq) { ASSERT(gq, 0); return gq->clientid; } extern int gqlist_check_creategame(int number) { t_connection * c; t_gq * gq; int i = 0; if (number <= 0) return -1; BEGIN_LIST_TRAVERSE_DATA(gqlist_head, gq, t_gq) { c = d2cs_connlist_find_connection_by_sessionnum(gq->clientid); if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "client {} not found (gamename: {})", gq->clientid, gq->gamename); gq_destroy(gq, &curr_elem_); continue; } else if (!conn_get_gamequeue(c)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL game queue for client {}", d2cs_conn_get_account(c)); gq_destroy(gq, &curr_elem_); continue; } else { eventlog(eventlog_level_info, __FUNCTION__, "try create game {} for account {}", gq->gamename, d2cs_conn_get_account(c)); d2cs_handle_client_creategame(c, gq->packet); conn_set_gamequeue(c, NULL); gq_destroy(gq, &curr_elem_); i++; if (i >= number) break; } } END_LIST_TRAVERSE_DATA() return 0; } extern int gqlist_update_all_clients(void) { t_connection * c; t_gq * gq; unsigned int n; n = 0; BEGIN_LIST_TRAVERSE_DATA(gqlist_head, gq, t_gq) { c = d2cs_connlist_find_connection_by_sessionnum(gq->clientid); if (!c) { eventlog(eventlog_level_error, __FUNCTION__, "client {} not found (gamename: {})", gq->clientid, gq->gamename); gq_destroy(gq, &curr_elem_); continue; } else { n++; eventlog(eventlog_level_debug, __FUNCTION__, "update client {} position to {}", d2cs_conn_get_account(c), n); d2cs_send_client_creategamewait(c, n); } } END_LIST_TRAVERSE_DATA() if (n) eventlog(eventlog_level_info, __FUNCTION__, "total {} game queues", n); return 0; } extern unsigned int gqlist_get_gq_position(t_gq * gq) { t_gq * tmp; unsigned int pos; pos = 0; BEGIN_LIST_TRAVERSE_DATA(gqlist_head, tmp, t_gq) { pos++; if (tmp == gq) return pos; } END_LIST_TRAVERSE_DATA() return 0; } extern unsigned int gqlist_get_length(void) { return list_get_length(gqlist_head); } extern t_gq * gqlist_find_game(char const * gamename) { t_gq * gq; BEGIN_LIST_TRAVERSE_DATA(gqlist_head, gq, t_gq) { if (!strcasecmp(gq->gamename, gamename)) return gq; } END_LIST_TRAVERSE_DATA() return NULL; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/gamequeue.h000066400000000000000000000031011462640461400177440ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_GAMEQUEUE_H #define INCLUDED_GAMEQUEUE_H #include "common/packet.h" #include "common/list.h" namespace pvpgn { namespace d2cs { typedef struct { unsigned int seqno; unsigned int clientid; t_packet * packet; char gamename[MAX_GAMENAME_LEN]; } t_gq; extern unsigned int gq_get_clientid(t_gq const * gq); extern int gq_destroy(t_gq * gq, t_elem ** elem); extern t_gq * gq_create(unsigned int clientid, t_packet * packet, char const * gamename); extern int gqlist_destroy(void); extern int gqlist_create(void); extern t_list * gqlist(void); extern unsigned int gqlist_get_gq_position(t_gq * gq); extern int gqlist_update_all_clients(void); extern int gqlist_check_creategame(int number); extern t_gq * gqlist_find_game(char const * gamename); extern unsigned int gqlist_get_length(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_bnetd.cpp000066400000000000000000000254111462640461400207400ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "handle_bnetd.h" #include "common/init_protocol.h" #include "common/eventlog.h" /* #include "common/d2cs_bnetd_protocol.h" #include "common/d2cs_protocol.h" #include "common/packet.h" #include "connection.h" */ #include "version.h" #include "prefs.h" #include "serverqueue.h" #include "game.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { DECLARE_PACKET_HANDLER(on_bnetd_accountloginreply) DECLARE_PACKET_HANDLER(on_bnetd_charloginreply) DECLARE_PACKET_HANDLER(on_bnetd_authreq) DECLARE_PACKET_HANDLER(on_bnetd_authreply) DECLARE_PACKET_HANDLER(on_bnetd_gameinforeq) static t_packet_handle_table bnetd_packet_handle_table[] = { /* 0x00 */{ 0, conn_state_none, NULL }, /* 0x01 */{ sizeof(t_bnetd_d2cs_authreq), conn_state_connected, on_bnetd_authreq }, /* 0x02 */{ sizeof(t_bnetd_d2cs_authreply), conn_state_connected, on_bnetd_authreply }, /* 0x03 */{ 0, conn_state_none, NULL }, /* 0x04 */{ 0, conn_state_none, NULL }, /* 0x05 */{ 0, conn_state_none, NULL }, /* 0x06 */{ 0, conn_state_none, NULL }, /* 0x07 */{ 0, conn_state_none, NULL }, /* 0x08 */{ 0, conn_state_none, NULL }, /* 0x09 */{ 0, conn_state_none, NULL }, /* 0x0a */{ 0, conn_state_none, NULL }, /* 0x0b */{ 0, conn_state_none, NULL }, /* 0x0c */{ 0, conn_state_none, NULL }, /* 0x0d */{ 0, conn_state_none, NULL }, /* 0x03 */{ 0, conn_state_none, NULL }, /* 0x0f */{ 0, conn_state_none, NULL }, /* 0x10 */{ sizeof(t_bnetd_d2cs_accountloginreply), conn_state_authed, on_bnetd_accountloginreply }, /* 0x11 */{ sizeof(t_bnetd_d2cs_charloginreply), conn_state_authed, on_bnetd_charloginreply }, /* 0x12 */{ sizeof(t_bnetd_d2cs_gameinforeq), conn_state_authed, on_bnetd_gameinforeq } }; extern int handle_bnetd_packet(t_connection * c, t_packet * packet) { conn_process_packet(c, packet, bnetd_packet_handle_table, NELEMS(bnetd_packet_handle_table)); return 0; } extern int handle_bnetd_init(t_connection * c) { t_packet * packet; packet = packet_create(packet_class_init); packet_set_size(packet, sizeof(t_client_initconn)); bn_byte_set(&packet->u.client_initconn.cclass, CLIENT_INITCONN_CLASS_D2CS_BNETD); conn_push_outqueue(c, packet); packet_del_ref(packet); d2cs_conn_set_state(c, conn_state_connected); eventlog(eventlog_level_info, __FUNCTION__, "sent init class packet to bnetd"); return 0; } static int on_bnetd_authreq(t_connection * c, t_packet * packet) { t_packet * rpacket; unsigned int sessionnum; sessionnum = bn_int_get(packet->u.bnetd_d2cs_authreq.sessionnum); eventlog(eventlog_level_info, __FUNCTION__, "received bnetd sessionnum {}", sessionnum); if ((rpacket = packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket, sizeof(t_d2cs_bnetd_authreply)); packet_set_type(rpacket, D2CS_BNETD_AUTHREPLY); bn_int_set(&rpacket->u.d2cs_bnetd_authreply.h.seqno, 1); bn_int_set(&rpacket->u.d2cs_bnetd_authreply.version, D2CS_VERSION_NUMBER); packet_append_string(rpacket, prefs_get_realmname()); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } static int on_bnetd_authreply(t_connection * c, t_packet * packet) { unsigned int reply; reply = bn_int_get(packet->u.bnetd_d2cs_authreply.reply); if (reply == BNETD_D2CS_AUTHREPLY_SUCCEED) { eventlog(eventlog_level_info, __FUNCTION__, "authed by bnetd"); d2cs_conn_set_state(c, conn_state_authed); } else { eventlog(eventlog_level_error, __FUNCTION__, "failed to auth by bnetd (error={})", reply); d2cs_conn_set_state(c, conn_state_destroy); } return 0; } static int on_bnetd_accountloginreply(t_connection * c, t_packet * packet) { unsigned int seqno; t_sq * sq; t_packet * opacket, *rpacket; t_connection * client; int result, reply; char const * account; t_elem * elem; if (!packet || !c) return -1; seqno = bn_int_get(packet->u.d2cs_bnetd.h.seqno); if (!(sq = sqlist_find_sq(seqno))) { eventlog(eventlog_level_error, __FUNCTION__, "seqno {} not found", seqno); return -1; } if (!(client = d2cs_connlist_find_connection_by_sessionnum(sq_get_clientid(sq)))) { eventlog(eventlog_level_error, __FUNCTION__, "client {} not found", sq_get_clientid(sq)); sq_destroy(sq, &elem); return -1; } if (!(opacket = sq_get_packet(sq))) { eventlog(eventlog_level_error, __FUNCTION__, "previous packet missing (seqno: {})", seqno); sq_destroy(sq, &elem); return -1; } result = bn_int_get(packet->u.bnetd_d2cs_accountloginreply.reply); if (result == BNETD_D2CS_CHARLOGINREPLY_SUCCEED) { reply = D2CS_CLIENT_LOGINREPLY_SUCCEED; account = packet_get_str_const(opacket, sizeof(t_client_d2cs_loginreq), MAX_CHARNAME_LEN); d2cs_conn_set_account(client, account); d2cs_conn_set_state(client, conn_state_authed); eventlog(eventlog_level_info, __FUNCTION__, "account {} authed", account); } else { eventlog(eventlog_level_warn, __FUNCTION__, "client {} login request was rejected by bnetd", sq_get_clientid(sq)); reply = D2CS_CLIENT_LOGINREPLY_BADPASS; } if ((rpacket = packet_create(packet_class_d2cs))) { packet_set_size(rpacket, sizeof(t_d2cs_client_loginreply)); packet_set_type(rpacket, D2CS_CLIENT_LOGINREPLY); bn_int_set(&rpacket->u.d2cs_client_loginreply.reply, reply); conn_push_outqueue(client, rpacket); packet_del_ref(rpacket); } sq_destroy(sq, &elem); return 0; } static int on_bnetd_charloginreply(t_connection * c, t_packet * packet) { unsigned int seqno; t_sq * sq; t_connection * client; t_packet * opacket, *rpacket; int result, reply, type; char const * charname; t_elem * elem; if (!packet || !c) return -1; seqno = bn_int_get(packet->u.d2cs_bnetd.h.seqno); if (!(sq = sqlist_find_sq(seqno))) { eventlog(eventlog_level_error, __FUNCTION__, "seqno {} not found", seqno); return -1; } if (!(client = d2cs_connlist_find_connection_by_sessionnum(sq_get_clientid(sq)))) { eventlog(eventlog_level_error, __FUNCTION__, "client {} not found", sq_get_clientid(sq)); sq_destroy(sq, &elem); return -1; } if (!(opacket = sq_get_packet(sq))) { eventlog(eventlog_level_error, __FUNCTION__, "previous packet missing (seqno: {})", seqno); sq_destroy(sq, &elem); return -1; } type = packet_get_type(opacket); result = bn_int_get(packet->u.bnetd_d2cs_charloginreply.reply); if (type == CLIENT_D2CS_CREATECHARREQ) { charname = packet_get_str_const(opacket, sizeof(t_client_d2cs_createcharreq), MAX_CHARNAME_LEN); if (result == BNETD_D2CS_CHARLOGINREPLY_SUCCEED) { if (conn_check_multilogin(client, charname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "character {} is already logged in", charname); reply = D2CS_CLIENT_CHARLOGINREPLY_FAILED; } else { reply = D2CS_CLIENT_CREATECHARREPLY_SUCCEED; eventlog(eventlog_level_info, __FUNCTION__, "character {} authed", charname); d2cs_conn_set_charname(client, charname); d2cs_conn_set_state(client, conn_state_char_authed); } } else { reply = D2CS_CLIENT_CREATECHARREPLY_FAILED; eventlog(eventlog_level_error, __FUNCTION__, "failed to auth character {}", charname); } if ((rpacket = packet_create(packet_class_d2cs))) { packet_set_size(rpacket, sizeof(t_d2cs_client_createcharreply)); packet_set_type(rpacket, D2CS_CLIENT_CREATECHARREPLY); bn_int_set(&rpacket->u.d2cs_client_createcharreply.reply, reply); conn_push_outqueue(client, rpacket); packet_del_ref(rpacket); } } else if (type == CLIENT_D2CS_CHARLOGINREQ) { charname = packet_get_str_const(opacket, sizeof(t_client_d2cs_charloginreq), MAX_CHARNAME_LEN); if (result == BNETD_D2CS_CHARLOGINREPLY_SUCCEED) { if (conn_check_multilogin(client, charname) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "character {} is already logged in", charname); reply = D2CS_CLIENT_CHARLOGINREPLY_FAILED; } else { reply = D2CS_CLIENT_CHARLOGINREPLY_SUCCEED; eventlog(eventlog_level_info, __FUNCTION__, "character {} authed", charname); d2cs_conn_set_charname(client, charname); d2cs_conn_set_state(client, conn_state_char_authed); } } else { reply = D2CS_CLIENT_CHARLOGINREPLY_FAILED; eventlog(eventlog_level_error, __FUNCTION__, "failed to auth character {}", charname); } if ((rpacket = packet_create(packet_class_d2cs))) { packet_set_size(rpacket, sizeof(t_d2cs_client_charloginreply)); packet_set_type(rpacket, D2CS_CLIENT_CHARLOGINREPLY); bn_int_set(&rpacket->u.d2cs_client_charloginreply.reply, reply); conn_push_outqueue(client, rpacket); packet_del_ref(rpacket); } } else { eventlog(eventlog_level_error, __FUNCTION__, "got bad packet type {}", type); sq_destroy(sq, &elem); return -1; } sq_destroy(sq, &elem); return 0; } int on_bnetd_gameinforeq(t_connection * c, t_packet * packet) { t_packet * rpacket; t_game * game; char const * gamename; if (!(c)) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection"); return -1; } if (!(gamename = packet_get_str_const(packet, sizeof(t_bnetd_d2cs_gameinforeq), MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error, __FUNCTION__, "missing or too long gamename"); return -1; } if (!(game = d2cs_gamelist_find_game(gamename))) { eventlog(eventlog_level_error, __FUNCTION__, "request for unknown game \"{}\"", gamename); return -1; } if ((rpacket = packet_create(packet_class_d2cs_bnetd))) { packet_set_size(rpacket, sizeof(t_d2cs_bnetd_gameinforeply)); packet_set_type(rpacket, D2CS_BNETD_GAMEINFOREPLY); bn_int_set(&rpacket->u.d2cs_bnetd_gameinforeply.h.seqno, 0); packet_append_string(rpacket, gamename); bn_byte_set(&rpacket->u.d2cs_bnetd_gameinforeply.difficulty, game_get_gameflag_difficulty(game)); conn_push_outqueue(c, rpacket); packet_del_ref(rpacket); } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_bnetd.h000066400000000000000000000020451462640461400204030ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_BNETD_H #define INCLUDED_HANDLE_BNETD_H #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace d2cs { extern int handle_bnetd_packet(t_connection * c, t_packet * packet); extern int handle_bnetd_init(t_connection * c); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_d2cs.cpp000066400000000000000000001220571462640461400205030ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2004 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "handle_d2cs.h" #include #include #include "compat/mkdir.h" #include "compat/pdir.h" #include "compat/psock.h" #include "compat/statmacros.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/d2cs_d2dbs_ladder.h" #include "game.h" #include "bnetd.h" #include "serverqueue.h" #include "prefs.h" #include "d2ladder.h" #include "d2charfile.h" #include "d2charlist.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static int d2cs_send_client_ladder(t_connection * c, unsigned char type, unsigned short from); static unsigned int d2cs_try_joingame(t_connection const * c, t_game const * game, char const * gamepass); DECLARE_PACKET_HANDLER(on_client_loginreq) DECLARE_PACKET_HANDLER(on_client_createcharreq) DECLARE_PACKET_HANDLER(on_client_creategamereq) DECLARE_PACKET_HANDLER(on_client_joingamereq) DECLARE_PACKET_HANDLER(on_client_gamelistreq) DECLARE_PACKET_HANDLER(on_client_gameinforeq) DECLARE_PACKET_HANDLER(on_client_charloginreq) DECLARE_PACKET_HANDLER(on_client_deletecharreq) DECLARE_PACKET_HANDLER(on_client_ladderreq) DECLARE_PACKET_HANDLER(on_client_motdreq) DECLARE_PACKET_HANDLER(on_client_cancelcreategame) DECLARE_PACKET_HANDLER(on_client_charladderreq) DECLARE_PACKET_HANDLER(on_client_charlistreq) DECLARE_PACKET_HANDLER(on_client_charlistreq_110) DECLARE_PACKET_HANDLER(on_client_convertcharreq) static t_packet_handle_table d2cs_packet_handle_table[]={ /* 0x00 */ { 0, conn_state_none, NULL }, /* 0x01 */ { sizeof(t_client_d2cs_loginreq), conn_state_connected, on_client_loginreq }, /* 0x02 */ { sizeof(t_client_d2cs_createcharreq), conn_state_authed|conn_state_char_authed, on_client_createcharreq }, /* 0x03 */ { sizeof(t_client_d2cs_creategamereq), conn_state_char_authed, on_client_creategamereq }, /* 0x04 */ { sizeof(t_client_d2cs_joingamereq), conn_state_char_authed, on_client_joingamereq }, /* 0x05 */ { sizeof(t_client_d2cs_gamelistreq), conn_state_char_authed, on_client_gamelistreq }, /* 0x06 */ { sizeof(t_client_d2cs_gameinforeq), conn_state_char_authed, on_client_gameinforeq }, /* 0x07 */ { sizeof(t_client_d2cs_charloginreq), conn_state_authed|conn_state_char_authed, on_client_charloginreq }, /* 0x08 */ { 0, conn_state_none, NULL }, /* 0x09 */ { 0, conn_state_none, NULL }, /* 0x0a */ { sizeof(t_client_d2cs_deletecharreq), conn_state_authed|conn_state_char_authed, on_client_deletecharreq }, /* 0x0b */ { 0, conn_state_none, NULL }, /* 0x0c */ { 0, conn_state_none, NULL }, /* 0x0d */ { 0, conn_state_none, NULL }, /* 0x0e */ { 0, conn_state_none, NULL }, /* 0x0f */ { 0, conn_state_none, NULL }, /* 0x10 */ { 0, conn_state_none, NULL }, /* 0x11 */ { sizeof(t_client_d2cs_ladderreq), conn_state_char_authed, on_client_ladderreq }, /* 0x12 */ { sizeof(t_client_d2cs_motdreq), conn_state_char_authed, on_client_motdreq }, /* 0x13 */ { sizeof(t_client_d2cs_cancelcreategame),conn_state_char_authed, on_client_cancelcreategame}, /* 0x14 */ { 0, conn_state_none, NULL }, /* 0x15 */ { 0, conn_state_none, NULL }, /* 0x16 */ { sizeof(t_client_d2cs_charladderreq), conn_state_char_authed, on_client_charladderreq }, /* 0x17 */ { sizeof(t_client_d2cs_charlistreq), conn_state_authed|conn_state_char_authed, on_client_charlistreq }, /* 0x18 */ { sizeof(t_client_d2cs_convertcharreq), conn_state_authed|conn_state_char_authed, on_client_convertcharreq }, /* 0x19 */ { sizeof(t_client_d2cs_charlistreq_110), conn_state_authed|conn_state_char_authed, on_client_charlistreq_110 } }; extern int d2cs_handle_d2cs_packet(t_connection * c, t_packet * packet) { return conn_process_packet(c,packet,d2cs_packet_handle_table,NELEMS(d2cs_packet_handle_table)); } static int on_client_loginreq(t_connection * c, t_packet * packet) { t_packet * bnpacket; char const * account; t_sq * sq; unsigned int sessionnum; if (!(account=packet_get_str_const(packet,sizeof(t_client_d2cs_loginreq),MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad account name"); return -1; } if (d2char_check_acctname(account)<0) { eventlog(eventlog_level_error,__FUNCTION__,"got bad account name"); return -1; } if (!bnetd_conn()) { eventlog(eventlog_level_warn,__FUNCTION__,"d2cs is offline with bnetd, login request will be rejected"); return -1; } sessionnum=bn_int_get(packet->u.client_d2cs_loginreq.sessionnum); conn_set_bnetd_sessionnum(c,sessionnum); eventlog(eventlog_level_info,__FUNCTION__,"got client (*{}) login request sessionnum=0x{:X}",account,sessionnum); if ((bnpacket=packet_create(packet_class_d2cs_bnetd))) { if ((sq=sq_create(d2cs_conn_get_sessionnum(c),packet,0))) { packet_set_size(bnpacket,sizeof(t_d2cs_bnetd_accountloginreq)); packet_set_type(bnpacket,D2CS_BNETD_ACCOUNTLOGINREQ); bn_int_set(&bnpacket->u.d2cs_bnetd_accountloginreq.h.seqno,sq_get_seqno(sq)); bn_int_set(&bnpacket->u.d2cs_bnetd_accountloginreq.seqno, bn_int_get(packet->u.client_d2cs_loginreq.seqno)); bn_int_set(&bnpacket->u.d2cs_bnetd_accountloginreq.sessionkey, bn_int_get(packet->u.client_d2cs_loginreq.sessionkey)); bn_int_set(&bnpacket->u.d2cs_bnetd_accountloginreq.sessionnum,sessionnum); std::memcpy(bnpacket->u.d2cs_bnetd_accountloginreq.secret_hash, packet->u.client_d2cs_loginreq.secret_hash, sizeof(bnpacket->u.d2cs_bnetd_accountloginreq.secret_hash)); packet_append_string(bnpacket,account); conn_push_outqueue(bnetd_conn(),bnpacket); } packet_del_ref(bnpacket); } return 0; } static int on_client_createcharreq(t_connection * c, t_packet * packet) { t_packet * rpacket, * bnpacket; char const * charname; char const * account; char * path; t_sq * sq; unsigned int reply; unsigned short status, chclass; t_d2charinfo_file data; if (!(charname=packet_get_str_const(packet,sizeof(t_client_d2cs_createcharreq),MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad character name"); return -1; } if (!(account=d2cs_conn_get_account(c))) { eventlog(eventlog_level_error,__FUNCTION__,"missing account for character {}",charname); return -1; } chclass=bn_short_get(packet->u.client_d2cs_createcharreq.chclass); status=bn_short_get(packet->u.client_d2cs_createcharreq.status); path=(char*)xmalloc(std::strlen(prefs_get_charinfo_dir())+1+std::strlen(account)+1); d2char_get_infodir_name(path,account); try { Directory dir(path); } catch (const Directory::OpenError&) { INFO1("(*{}) charinfo directory do not exist, building it",account); p_mkdir(path,S_IRWXU); } xfree(path); if (d2char_create(account,charname,chclass,status)<0) { eventlog(eventlog_level_warn,__FUNCTION__,"error create character {} for account {}",charname,account); reply=D2CS_CLIENT_CREATECHARREPLY_ALREADY_EXIST; } else if (d2charinfo_load(account,charname,&data)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error loading charinfo for character {}(*{})",charname,account); reply=D2CS_CLIENT_CREATECHARREPLY_FAILED; } else { eventlog(eventlog_level_info,__FUNCTION__,"character {}(*{}) created",charname,account); reply=D2CS_CLIENT_CREATECHARREPLY_SUCCEED; conn_set_charinfo(c,&data.summary); if ((bnpacket=packet_create(packet_class_d2cs_bnetd))) { if ((sq=sq_create(d2cs_conn_get_sessionnum(c),packet,0))) { packet_set_size(bnpacket,sizeof(t_d2cs_bnetd_charloginreq)); packet_set_type(bnpacket,D2CS_BNETD_CHARLOGINREQ); bn_int_set(&bnpacket->u.d2cs_bnetd_charloginreq.h.seqno,sq_get_seqno(sq)); bn_int_set(&bnpacket->u.d2cs_bnetd_charloginreq.sessionnum, conn_get_bnetd_sessionnum(c)); packet_append_string(bnpacket,charname); packet_append_string(bnpacket,(char const *)&data.portrait); conn_push_outqueue(bnetd_conn(),bnpacket); } packet_del_ref(bnpacket); } return 0; } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_createcharreply)); packet_set_type(rpacket,D2CS_CLIENT_CREATECHARREPLY); bn_int_set(&rpacket->u.d2cs_client_createcharreply.reply,reply); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } return 0; } static int on_client_creategamereq(t_connection * c, t_packet * packet) { char const * gamename; char const * gamepass; char const * gamedesc; t_game * game; t_d2gs * gs; t_gq * gq; unsigned int tempflag,gameflag; unsigned int leveldiff, maxchar, difficulty, expansion, hardcore, ladder; unsigned int seqno, reply; unsigned int pos; t_elem * elem; pos=sizeof(t_client_d2cs_creategamereq); if (!(gamename=packet_get_str_const(packet,pos,MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad game name"); return -1; } pos+=std::strlen(gamename)+1; if (!(gamepass=packet_get_str_const(packet,pos,MAX_GAMEPASS_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad game pass"); return -1; } pos+=std::strlen(gamepass)+1; if (!(gamedesc=packet_get_str_const(packet,pos,MAX_GAMEDESC_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad game desc"); return -1; } tempflag=bn_int_get(packet->u.client_d2cs_creategamereq.gameflag); leveldiff=bn_byte_get(packet->u.client_d2cs_creategamereq.leveldiff); maxchar=bn_byte_get(packet->u.client_d2cs_creategamereq.maxchar); difficulty=gameflag_get_difficulty(tempflag); if (difficulty > conn_get_charinfo_difficulty(c)) { eventlog(eventlog_level_error,__FUNCTION__,"game difficulty exceed character limit {} {}",difficulty, conn_get_charinfo_difficulty(c)); return 0; } expansion=conn_get_charinfo_expansion(c); hardcore=conn_get_charinfo_hardcore(c); ladder=conn_get_charinfo_ladder(c); gameflag=gameflag_create(ladder,expansion,hardcore,difficulty); gs = NULL; game = NULL; gq=conn_get_gamequeue(c); if (d2cs_gamelist_find_game(gamename)) { eventlog(eventlog_level_info,__FUNCTION__,"game name {} is already exist in gamelist",gamename); reply=D2CS_CLIENT_CREATEGAMEREPLY_NAME_EXIST; } else if (!gq && gqlist_find_game(gamename)) { eventlog(eventlog_level_info,__FUNCTION__,"game name {} is already exist in game queue",gamename); reply=D2CS_CLIENT_CREATEGAMEREPLY_NAME_EXIST; } else if (!(gs=d2gslist_choose_server())) { if (gq) { eventlog(eventlog_level_error,__FUNCTION__,"client {} is already in game queue",d2cs_conn_get_sessionnum(c)); conn_set_gamequeue(c,NULL); gq_destroy(gq,&elem); return 0; } else if ((gq=gq_create(d2cs_conn_get_sessionnum(c), packet, gamename))) { conn_set_gamequeue(c,gq); d2cs_send_client_creategamewait(c,gqlist_get_length()); return 0; } reply=D2CS_CLIENT_CREATEGAMEREPLY_FAILED; } else if (hardcore && conn_get_charinfo_dead(c)) { reply=D2CS_CLIENT_CREATEGAMEREPLY_FAILED; } else if (!(game=d2cs_game_create(gamename,gamepass,gamedesc,gameflag))) { reply=D2CS_CLIENT_CREATEGAMEREPLY_NAME_EXIST; } else { reply=D2CS_CLIENT_CREATEGAMEREPLY_SUCCEED; game_set_d2gs(game,gs); d2gs_add_gamenum(gs, 1); game_set_gameflag_ladder(game,ladder); game_set_gameflag_expansion(game,expansion); game_set_created(game,0); game_set_leveldiff(game,leveldiff); game_set_charlevel(game,conn_get_charinfo_level(c)); game_set_maxchar(game,maxchar); game_set_gameflag_difficulty(game,difficulty); game_set_gameflag_hardcore(game,hardcore); } seqno=bn_short_get(packet->u.client_d2cs_creategamereq.seqno); if (reply!=D2CS_CLIENT_CREATEGAMEREPLY_SUCCEED) { t_packet * rpacket; if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_creategamereply)); packet_set_type(rpacket,D2CS_CLIENT_CREATEGAMEREPLY); bn_short_set(&rpacket->u.d2cs_client_creategamereply.seqno,seqno); bn_short_set(&rpacket->u.d2cs_client_creategamereply.u1,0); bn_short_set(&rpacket->u.d2cs_client_creategamereply.gameid,0); bn_int_set(&rpacket->u.d2cs_client_creategamereply.reply,reply); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } } else { t_packet * gspacket; t_sq * sq; struct in_addr addr; if ((gspacket=packet_create(packet_class_d2gs))) { if ((sq=sq_create(d2cs_conn_get_sessionnum(c),packet,d2cs_game_get_id(game)))) { packet_set_size(gspacket,sizeof(t_d2cs_d2gs_creategamereq)); packet_set_type(gspacket,D2CS_D2GS_CREATEGAMEREQ); bn_int_set(&gspacket->u.d2cs_d2gs_creategamereq.h.seqno,sq_get_seqno(sq)); bn_byte_set(&gspacket->u.d2cs_d2gs_creategamereq.difficulty,difficulty); bn_byte_set(&gspacket->u.d2cs_d2gs_creategamereq.hardcore,hardcore); bn_byte_set(&gspacket->u.d2cs_d2gs_creategamereq.expansion,expansion); bn_byte_set(&gspacket->u.d2cs_d2gs_creategamereq.ladder,ladder); packet_append_string(gspacket,gamename); packet_append_string(gspacket,gamepass); packet_append_string(gspacket,gamedesc); packet_append_string(gspacket,d2cs_conn_get_account(c)); packet_append_string(gspacket,d2cs_conn_get_charname(c)); addr.s_addr = htonl(d2cs_conn_get_addr(c)); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(addr), addrstr, sizeof(addrstr)); packet_append_string(gspacket,addrstr); conn_push_outqueue(d2gs_get_connection(gs),gspacket); } packet_del_ref(gspacket); eventlog(eventlog_level_info,__FUNCTION__,"request create game {} on gs {}",gamename,d2gs_get_id(gs)); } } return 0; } static int on_client_joingamereq(t_connection * c, t_packet * packet) { char const * gamename; char const * gamepass; char const * charname; char const * account; t_game * game; t_d2gs * gs; int reply; unsigned int pos; unsigned int seqno; gs = NULL; pos=sizeof(t_client_d2cs_joingamereq); if (!(gamename=packet_get_str_const(packet,pos,MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad game name"); return -1; } pos+=std::strlen(gamename)+1; if (!(gamepass=packet_get_str_const(packet,pos,MAX_GAMEPASS_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad game pass"); return -1; } if (!(charname=d2cs_conn_get_charname(c))) { eventlog(eventlog_level_error,__FUNCTION__,"missing character name for connection"); return -1; } if (!(account=d2cs_conn_get_account(c))) { eventlog(eventlog_level_error,__FUNCTION__,"missing account for connection"); return -1; } if (conn_check_multilogin(c,charname)<0) { eventlog(eventlog_level_error,__FUNCTION__,"character {} is already logged in",charname); return -1; } if (!(game=d2cs_gamelist_find_game(gamename))) { eventlog(eventlog_level_info,__FUNCTION__,"game {} not found",gamename); reply=D2CS_CLIENT_JOINGAMEREPLY_NOT_EXIST; } else if (!(gs=game_get_d2gs(game))) { eventlog(eventlog_level_error,__FUNCTION__,"missing game server for game {}",gamename); reply=D2CS_CLIENT_JOINGAMEREPLY_NOT_EXIST; } else { reply=d2cs_try_joingame(c,game,gamepass); } seqno=bn_short_get(packet->u.client_d2cs_joingamereq.seqno); if (reply!=D2CS_CLIENT_JOINGAMEREPLY_SUCCEED) { t_packet * rpacket; if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_joingamereply)); packet_set_type(rpacket,D2CS_CLIENT_JOINGAMEREPLY); bn_short_set(&rpacket->u.d2cs_client_joingamereply.seqno,seqno); bn_short_set(&rpacket->u.d2cs_client_joingamereply.u1,0); bn_short_set(&rpacket->u.d2cs_client_joingamereply.gameid,0); bn_int_set(&rpacket->u.d2cs_client_joingamereply.addr,0); bn_int_set(&rpacket->u.d2cs_client_joingamereply.token,0); bn_int_set(&rpacket->u.d2cs_client_joingamereply.reply,reply); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } } else { t_packet * gspacket; t_sq * sq; struct in_addr addr; if ((gspacket=packet_create(packet_class_d2gs))) { if ((sq=sq_create(d2cs_conn_get_sessionnum(c),packet,d2cs_game_get_id(game)))) { packet_set_size(gspacket,sizeof(t_d2cs_d2gs_joingamereq)); packet_set_type(gspacket,D2CS_D2GS_JOINGAMEREQ); bn_int_set(&gspacket->u.d2cs_d2gs_joingamereq.h.seqno,sq_get_seqno(sq)); bn_int_set(&gspacket->u.d2cs_d2gs_joingamereq.gameid, game_get_d2gs_gameid(game)); sq_set_gametoken(sq,d2gs_make_token(gs)); bn_int_set(&gspacket->u.d2cs_d2gs_joingamereq.token,sq_get_gametoken(sq)); packet_append_string(gspacket,charname); packet_append_string(gspacket,account); addr.s_addr = htonl(d2cs_conn_get_addr(c)); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(addr), addrstr, sizeof(addrstr)); packet_append_string(gspacket,addrstr); conn_push_outqueue(d2gs_get_connection(gs),gspacket); } packet_del_ref(gspacket); eventlog(eventlog_level_info,__FUNCTION__,"request join game {} for character {} on gs {}",gamename, charname,d2gs_get_id(gs)); } } return 0; } static int on_client_gamelistreq(t_connection * c, t_packet * packet) { t_packet * rpacket; t_game * game; unsigned int count; unsigned int seqno; std::time_t now; unsigned int maxlifetime; t_elem const * start_elem; t_elem const * elem; seqno=bn_short_get(packet->u.client_d2cs_gamelistreq.seqno); /* if (seqno%2) return 0; */ count=0; now=std::time(NULL); maxlifetime=prefs_get_game_maxlifetime(); elem=start_elem=gamelist_get_curr_elem(); if (!elem) elem=list_get_first_const(d2cs_gamelist()); else elem=elem_get_next_const(d2cs_gamelist(),elem); for (; elem != start_elem; elem=elem_get_next_const(d2cs_gamelist(),elem)) { if (!elem) { elem=list_get_first_const(d2cs_gamelist()); if (elem == start_elem) break; } if (!(game=(t_game*)elem_get_data(elem))) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL game"); break; } if (maxlifetime && (now-game->create_time>maxlifetime)) continue; if (!game_get_currchar(game)) continue; if (!prefs_allow_gamelist_showall()) { if (conn_get_charinfo_difficulty(c)!=game_get_gameflag_difficulty(game)) continue; } if (prefs_hide_pass_games()) if (d2cs_game_get_pass(game)) continue; if (d2cs_try_joingame(c,game,"")!=D2CS_CLIENT_JOINGAMEREPLY_SUCCEED) continue; if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_gamelistreply)); packet_set_type(rpacket,D2CS_CLIENT_GAMELISTREPLY); bn_short_set(&rpacket->u.d2cs_client_gamelistreply.seqno,seqno); bn_int_set(&rpacket->u.d2cs_client_gamelistreply.token,d2cs_game_get_id(game)); bn_byte_set(&rpacket->u.d2cs_client_gamelistreply.currchar,game_get_currchar(game)); bn_int_set(&rpacket->u.d2cs_client_gamelistreply.gameflag,game_get_gameflag(game)); packet_append_string(rpacket,d2cs_game_get_name(game)); packet_append_string(rpacket,game_get_desc(game)); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); count++; if (prefs_get_maxgamelist() && count>=prefs_get_maxgamelist()) break; } } gamelist_set_curr_elem(elem); if (count) { if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_gamelistreply)); packet_set_type(rpacket,D2CS_CLIENT_GAMELISTREPLY); bn_short_set(&rpacket->u.d2cs_client_gamelistreply.seqno,seqno); bn_int_set(&rpacket->u.d2cs_client_gamelistreply.token,0); bn_byte_set(&rpacket->u.d2cs_client_gamelistreply.currchar,0); bn_int_set(&rpacket->u.d2cs_client_gamelistreply.gameflag,0); packet_append_string(rpacket,""); packet_append_string(rpacket,""); packet_append_string(rpacket,""); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } } return 0; } static int on_client_gameinforeq(t_connection * c, t_packet * packet) { t_game_charinfo * info; t_packet * rpacket; char const * gamename; t_game * game; unsigned int seqno, n; if (!(gamename=packet_get_str_const(packet,sizeof(t_client_d2cs_gameinforeq),MAX_GAMENAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad game name"); return -1; } if (!(game=d2cs_gamelist_find_game(gamename))) { eventlog(eventlog_level_error,__FUNCTION__,"game {} not found",gamename); return 0; } seqno=bn_short_get(packet->u.client_d2cs_gameinforeq.seqno); if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_gameinforeply)); packet_set_type(rpacket,D2CS_CLIENT_GAMEINFOREPLY); bn_short_set(&rpacket->u.d2cs_client_gameinforeply.seqno,seqno); bn_int_set(&rpacket->u.d2cs_client_gameinforeply.gameflag,game_get_gameflag(game)); bn_int_set(&rpacket->u.d2cs_client_gameinforeply.etime,std::time(NULL)-d2cs_game_get_create_time(game)); bn_byte_set(&rpacket->u.d2cs_client_gameinforeply.charlevel,game_get_charlevel(game)); bn_byte_set(&rpacket->u.d2cs_client_gameinforeply.leveldiff,game_get_leveldiff(game)); bn_byte_set(&rpacket->u.d2cs_client_gameinforeply.maxchar,game_get_maxchar(game)); packet_append_string(rpacket, game_get_desc(game) ? game_get_desc(game) : NULL); n=0; BEGIN_LIST_TRAVERSE_DATA_CONST(game_get_charlist(game),info,t_game_charinfo) { if (!info->charname) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL charname in game {} char list",gamename); continue; } packet_append_string(rpacket,info->charname); bn_byte_set(&rpacket->u.d2cs_client_gameinforeply.chclass[n],info->chclass); /* GUI is limited to a max level of 255 */ if (info->level < 255) { bn_byte_set(&rpacket->u.d2cs_client_gameinforeply.level[n],info->level); } else { bn_byte_set(&rpacket->u.d2cs_client_gameinforeply.level[n], 255); } n++; } END_LIST_TRAVERSE_DATA_CONST() bn_byte_set(&rpacket->u.d2cs_client_gameinforeply.currchar,n); if (n!=game_get_currchar(game)) { eventlog(eventlog_level_error,__FUNCTION__,"game {} character list corrupted",gamename); } conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } return 0; } static int on_client_charloginreq(t_connection * c, t_packet * packet) { t_packet * bnpacket; char const * charname; char const * account; t_sq * sq; t_d2charinfo_file data; unsigned int expire_time; if (!(charname=packet_get_str_const(packet,sizeof(t_client_d2cs_charloginreq),MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad character name"); return -1; } if (!(account=d2cs_conn_get_account(c))) { eventlog(eventlog_level_error,__FUNCTION__,"missing account for connection"); return -1; } if (d2charinfo_load(account,charname,&data)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error loading charinfo for character {}(*{})",charname,account); return -1; } else if (!bnetd_conn()) { eventlog(eventlog_level_error,__FUNCTION__,"no bnetd connection available,character login rejected"); return -1; } expire_time = prefs_get_char_expire_time(); if (expire_time && (std::time(NULL) > bn_int_get(data.header.last_time) + expire_time)) { t_packet * rpacket; if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_charloginreply)); packet_set_type(rpacket,D2CS_CLIENT_CHARLOGINREPLY); bn_int_set(&rpacket->u.d2cs_client_charloginreply.reply, D2CS_CLIENT_CHARLOGINREPLY_EXPIRED); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } eventlog(eventlog_level_info,__FUNCTION__,"character {}(*{}) login rejected due to char expired",charname,account); return 0; } conn_set_charinfo(c,&data.summary); eventlog(eventlog_level_info,__FUNCTION__,"got character {}(*{}) login request",charname,account); if ((bnpacket=packet_create(packet_class_d2cs_bnetd))) { if ((sq=sq_create(d2cs_conn_get_sessionnum(c),packet,0))) { packet_set_size(bnpacket,sizeof(t_d2cs_bnetd_charloginreq)); packet_set_type(bnpacket,D2CS_BNETD_CHARLOGINREQ); bn_int_set(&bnpacket->u.d2cs_bnetd_charloginreq.h.seqno,sq_get_seqno(sq)); bn_int_set(&bnpacket->u.d2cs_bnetd_charloginreq.sessionnum, conn_get_bnetd_sessionnum(c)); packet_append_string(bnpacket,charname); packet_append_string(bnpacket,(char const *)&data.portrait); conn_push_outqueue(bnetd_conn(),bnpacket); } packet_del_ref(bnpacket); } return 0; } static int on_client_deletecharreq(t_connection * c, t_packet * packet) { t_packet * rpacket; char const * charname; char const * account; unsigned int reply; if (!(charname=packet_get_str_const(packet,sizeof(t_client_d2cs_deletecharreq),MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad character name"); return -1; } if (conn_check_multilogin(c,charname)<0) { eventlog(eventlog_level_error,__FUNCTION__,"character {} is already logged in",charname); return -1; } d2cs_conn_set_charname(c,NULL); account=d2cs_conn_get_account(c); if (d2char_delete(account,charname)<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to delete character {}(*{})",charname,account); reply = D2CS_CLIENT_DELETECHARREPLY_FAILED; } else { reply = D2CS_CLIENT_DELETECHARREPLY_SUCCEED; } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_deletecharreply)); packet_set_type(rpacket,D2CS_CLIENT_DELETECHARREPLY); bn_short_set(&rpacket->u.d2cs_client_deletecharreply.u1,0); bn_int_set(&rpacket->u.d2cs_client_deletecharreply.reply,reply); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } return 0; } static int on_client_ladderreq(t_connection * c, t_packet * packet) { unsigned char type; unsigned short start_pos; type=bn_byte_get(packet->u.client_d2cs_ladderreq.type); start_pos=bn_short_get(packet->u.client_d2cs_ladderreq.start_pos); d2cs_send_client_ladder(c,type,start_pos); return 0; } static int d2cs_send_client_ladder(t_connection * c, unsigned char type, unsigned short from) { t_packet * rpacket; t_d2cs_client_ladderinfo const * ladderinfo; unsigned int curr_len, cont_len, total_len; t_d2cs_client_ladderheader ladderheader; t_d2cs_client_ladderinfoheader infoheader; unsigned int start_pos, count, count_per_packet, npacket; unsigned int i, n, curr_pos; start_pos=from; count=prefs_get_ladderlist_count(); if (d2ladder_get_ladder(&start_pos,&count,type,&ladderinfo)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error get ladder for type {} start_pos {}",type,from); return 0; } count_per_packet=14; npacket=count/count_per_packet; if (count % count_per_packet) npacket++; curr_len=0; cont_len=0; total_len = count * sizeof(*ladderinfo) + sizeof(ladderheader) + sizeof(infoheader) * npacket; total_len -= 4; bn_short_set(&ladderheader.start_pos,start_pos); bn_short_set(&ladderheader.u1,0); bn_int_set(&ladderheader.count1,count); for (i=0; i< npacket; i++) { curr_len=0; if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_ladderreply)); packet_set_type(rpacket,D2CS_CLIENT_LADDERREPLY); bn_byte_set(&rpacket->u.d2cs_client_ladderreply.type, type); bn_short_set(&rpacket->u.d2cs_client_ladderreply.total_len, total_len); bn_short_set(&rpacket->u.d2cs_client_ladderreply.cont_len,cont_len); if (i==0) { bn_int_set(&infoheader.count2,count); packet_append_data(rpacket,&ladderheader,sizeof(ladderheader)); curr_len += sizeof(ladderheader); } else { bn_int_set(&infoheader.count2,0); } packet_append_data(rpacket,&infoheader,sizeof(infoheader)); curr_len += sizeof(infoheader); for (n=0; n< count_per_packet; n++) { curr_pos = n + i * count_per_packet; if (curr_pos >= count) break; packet_append_data(rpacket, ladderinfo+curr_pos, sizeof(*ladderinfo)); curr_len += sizeof(*ladderinfo); } if (i==0) { packet_set_size(rpacket, packet_get_size(rpacket)-4); curr_len -= 4; } bn_short_set(&rpacket->u.d2cs_client_ladderreply.curr_len,curr_len); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } cont_len += curr_len; } return 0; } #define MAX_MOTD_LENGTH 511 static int on_client_motdreq(t_connection * c, t_packet * packet) { t_packet * rpacket; char * motd; int motd_len; if (!packet) return -1; /* client will crash if motd is too long */ motd = xstrdup(prefs_get_motd()); motd_len = std::strlen(motd); if (motd_len > MAX_MOTD_LENGTH) { WARN2("motd length ({}) exceeds maximun value ({})",motd_len,MAX_MOTD_LENGTH); motd[MAX_MOTD_LENGTH]='\0'; } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_motdreply)); packet_set_type(rpacket,D2CS_CLIENT_MOTDREPLY); bn_byte_set(&rpacket->u.d2cs_client_motdreply.u1,0); packet_append_string(rpacket,motd); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } xfree(motd); return 0; } static int on_client_cancelcreategame(t_connection * c, t_packet * packet) { t_gq * gq; t_elem * elem; if (!packet) return -1; if (!(gq=conn_get_gamequeue(c))) { return 0; } conn_set_gamequeue(c,NULL); gq_destroy(gq,&elem); return 0; } static int on_client_charladderreq(t_connection * c, t_packet * packet) { t_packet * rpacket; char const * charname; unsigned int expansion, hardcore, type; int pos; if (!(charname=packet_get_str_const(packet,sizeof(t_client_d2cs_charladderreq),MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad character name"); return -1; } expansion=bn_int_get(packet->u.client_d2cs_charladderreq.expansion); hardcore=bn_int_get(packet->u.client_d2cs_charladderreq.hardcore); type=0; /* avoid warning */ if (hardcore && expansion) { type=D2LADDER_EXP_HC_OVERALL; } else if (!hardcore && expansion) { type=D2LADDER_EXP_STD_OVERALL; } else if (hardcore && !expansion) { type=D2LADDER_HC_OVERALL; } else if (!hardcore && !expansion) { type=D2LADDER_STD_OVERALL; } if ((pos=d2ladder_find_character_pos(type,charname))<0) { if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_ladderreply)); packet_set_type(rpacket,D2CS_CLIENT_LADDERREPLY); bn_byte_set(&rpacket->u.d2cs_client_ladderreply.type, type); bn_short_set(&rpacket->u.d2cs_client_ladderreply.total_len,0); bn_short_set(&rpacket->u.d2cs_client_ladderreply.curr_len,0); bn_short_set(&rpacket->u.d2cs_client_ladderreply.cont_len,0); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } return 0; } pos -= prefs_get_ladderlist_count()/2; if (pos < 0) pos=0; d2cs_send_client_ladder(c,type,pos); return 0; } static int on_client_charlistreq(t_connection * c, t_packet * packet) { t_packet * rpacket; char const * account; char const * charname; char * path; t_d2charinfo_file * charinfo; unsigned int n, maxchar; t_elist charlist_head; char const * charlist_sort_order; if (!packet) return -1; if (!(account=d2cs_conn_get_account(c))) { eventlog(eventlog_level_error,__FUNCTION__,"missing account for connection"); return -1; } path=(char*)xmalloc(std::strlen(prefs_get_charinfo_dir())+1+std::strlen(account)+1); charlist_sort_order = prefs_get_charlist_sort_order(); elist_init(&charlist_head); d2char_get_infodir_name(path,account); maxchar=prefs_get_maxchar(); if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_charlistreply)); packet_set_type(rpacket,D2CS_CLIENT_CHARLISTREPLY); bn_short_set(&rpacket->u.d2cs_client_charlistreply.u1,0); n=0; try { Directory dir(path); while ((charname=dir.read())) { charinfo = (t_d2charinfo_file*)xmalloc(sizeof(t_d2charinfo_file)); if (d2charinfo_load(account,charname,charinfo)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error loading charinfo for {}(*{})",charname,account); xfree((void *)charinfo); continue; } eventlog(eventlog_level_debug,__FUNCTION__,"adding char {} (*{})", charname, account); d2charlist_add_char(&charlist_head,charinfo,0); n++; if (n>=maxchar) break; } if (prefs_allow_newchar() && (nu.d2cs_client_charlistreply.maxchar,maxchar); } else { bn_short_set(&rpacket->u.d2cs_client_charlistreply.maxchar,0); } if (!std::strcmp(charlist_sort_order, "ASC")) { t_elist * curr, * safe; t_d2charlist * ccharlist; elist_for_each_safe(curr,&charlist_head,safe) { ccharlist = elist_entry(curr,t_d2charlist,list); packet_append_string(rpacket,(char*)ccharlist->charinfo->header.charname); packet_append_string(rpacket,(char *)&ccharlist->charinfo->portrait); xfree((void *)ccharlist->charinfo); xfree((void *)ccharlist); } } else { t_elist * curr, * safe; t_d2charlist * ccharlist; elist_for_each_safe_rev(curr,&charlist_head,safe) { ccharlist = elist_entry(curr,t_d2charlist,list); packet_append_string(rpacket,(char*)ccharlist->charinfo->header.charname); packet_append_string(rpacket,(char *)&ccharlist->charinfo->portrait); xfree((void *)ccharlist->charinfo); xfree((void *)ccharlist); } } } catch(const Directory::OpenError&) { INFO1("(*{}) charinfo directory do not exist, building it",account); p_mkdir(path,S_IRWXU); } bn_short_set(&rpacket->u.d2cs_client_charlistreply.currchar,n); bn_short_set(&rpacket->u.d2cs_client_charlistreply.currchar2,n); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } xfree(path); return 0; } static int on_client_charlistreq_110(t_connection * c, t_packet * packet) { t_packet * rpacket; char const * account; char const * charname; char * path; t_d2charinfo_file * charinfo; unsigned int n, maxchar; t_elist charlist_head; unsigned int exp_time; unsigned int curr_exp_time; char const * charlist_sort_order; if (!packet) return -1; if (!(account=d2cs_conn_get_account(c))) { eventlog(eventlog_level_error,__FUNCTION__,"missing account for connection"); return -1; } path=(char*)xmalloc(std::strlen(prefs_get_charinfo_dir())+1+std::strlen(account)+1); charlist_sort_order = prefs_get_charlist_sort_order(); elist_init(&charlist_head); d2char_get_infodir_name(path,account); if (prefs_allow_newchar()) maxchar=prefs_get_maxchar(); else maxchar=0; if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_charlistreply_110)); packet_set_type(rpacket,D2CS_CLIENT_CHARLISTREPLY_110); bn_short_set(&rpacket->u.d2cs_client_charlistreply_110.u1,0); n=0; try { Directory dir(path); exp_time = prefs_get_char_expire_time(); while ((charname=dir.read())) { charinfo = (t_d2charinfo_file*)xmalloc(sizeof(t_d2charinfo_file)); if (d2charinfo_load(account,charname,charinfo)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error loading charinfo for {}(*{})",charname,account); xfree(charinfo); continue; } if (exp_time) { curr_exp_time = bn_int_get(charinfo->header.last_time)+exp_time; } else { curr_exp_time = 0x7FFFFFFF; } eventlog(eventlog_level_debug,__FUNCTION__,"adding char {} (*{})", charname, account); d2charlist_add_char(&charlist_head,charinfo,curr_exp_time); n++; if (n>=maxchar) break; } if (n>=maxchar) maxchar = 0; if (!std::strcmp(charlist_sort_order, "ASC")) { t_elist * curr, *safe; t_d2charlist * ccharlist; elist_for_each_safe(curr,&charlist_head,safe) { bn_int bn_exp_time; ccharlist = elist_entry(curr,t_d2charlist,list); bn_int_set(&bn_exp_time,ccharlist->expiration_time); packet_append_data(rpacket,bn_exp_time,sizeof(bn_exp_time)); packet_append_string(rpacket,(char*)ccharlist->charinfo->header.charname); packet_append_string(rpacket,(char *)&ccharlist->charinfo->portrait); xfree((void *)ccharlist->charinfo); xfree((void *)ccharlist); } } else { t_elist * curr, *safe; t_d2charlist * ccharlist; elist_for_each_safe_rev(curr,&charlist_head,safe) { bn_int bn_exp_time; ccharlist = elist_entry(curr,t_d2charlist,list); bn_int_set(&bn_exp_time,ccharlist->expiration_time); packet_append_data(rpacket,bn_exp_time,sizeof(bn_exp_time)); packet_append_string(rpacket,(char*)ccharlist->charinfo->header.charname); packet_append_string(rpacket,(char *)&ccharlist->charinfo->portrait); xfree((void *)ccharlist->charinfo); xfree((void *)ccharlist); } } } catch (const Directory::OpenError&) { INFO1("(*{}) charinfo directory do not exist, building it",account); p_mkdir(path,S_IRWXU); } bn_short_set(&rpacket->u.d2cs_client_charlistreply.currchar,n); bn_short_set(&rpacket->u.d2cs_client_charlistreply.currchar2,n); bn_short_set(&rpacket->u.d2cs_client_charlistreply.maxchar,maxchar); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } xfree(path); return 0; } static int on_client_convertcharreq(t_connection * c, t_packet * packet) { t_packet * rpacket; char const * charname; char const * account; unsigned int reply; if (!(charname=packet_get_str_const(packet,sizeof(t_client_d2cs_convertcharreq),MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad character name"); return -1; } if (conn_check_multilogin(c,charname)<0) { eventlog(eventlog_level_error,__FUNCTION__,"character {} is already logged in",charname); return -1; } account=d2cs_conn_get_account(c); if (d2char_convert(account,charname)<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to convert character {}(*{})",charname,account); reply = D2CS_CLIENT_CONVERTCHARREPLY_FAILED; } else { reply = D2CS_CLIENT_CONVERTCHARREPLY_SUCCEED; } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_convertcharreply)); packet_set_type(rpacket,D2CS_CLIENT_CONVERTCHARREPLY); bn_int_set(&rpacket->u.d2cs_client_convertcharreply.reply,reply); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } return 0; } extern int d2cs_send_client_creategamewait(t_connection * c, unsigned int position) { t_packet * packet; ASSERT(c,-1); if ((packet=packet_create(packet_class_d2cs))) { packet_set_size(packet,sizeof(t_d2cs_client_creategamewait)); packet_set_type(packet,D2CS_CLIENT_CREATEGAMEWAIT); bn_int_set(&packet->u.d2cs_client_creategamewait.position,position); conn_push_outqueue(c,packet); packet_del_ref(packet); } return 0; } extern int d2cs_handle_client_creategame(t_connection * c, t_packet * packet) { return on_client_creategamereq(c,packet); } static unsigned int d2cs_try_joingame(t_connection const * c, t_game const * game, char const * gamepass) { unsigned int reply; ASSERT(c,D2CS_CLIENT_JOINGAMEREPLY_NOT_EXIST); ASSERT(game,D2CS_CLIENT_JOINGAMEREPLY_NOT_EXIST); if (!game_get_created(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_NOT_EXIST; } else if (!game_get_d2gs(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_NOT_EXIST; } else if (conn_get_charinfo_ladder(c) != game_get_gameflag_ladder(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_NORMAL_LADDER; } else if (conn_get_charinfo_expansion(c) != game_get_gameflag_expansion(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_CLASSIC_EXPANSION; } else if (conn_get_charinfo_hardcore(c) != game_get_gameflag_hardcore(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_HARDCORE_SOFTCORE; } else if (conn_get_charinfo_difficulty(c) < game_get_gameflag_difficulty(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_NORMAL_NIGHTMARE; } else if (prefs_allow_gamelimit()) { if (game_get_maxchar(game) <= game_get_currchar(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_GAME_FULL; } else if (conn_get_charinfo_level(c) > game_get_maxlevel(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_LEVEL_LIMIT; } else if (conn_get_charinfo_level(c) < game_get_minlevel(game)) { reply=D2CS_CLIENT_JOINGAMEREPLY_LEVEL_LIMIT; } else if (std::strcmp(d2cs_game_get_pass(game),gamepass)) { reply=D2CS_CLIENT_JOINGAMEREPLY_BAD_PASS; } else { reply=D2CS_CLIENT_JOINGAMEREPLY_SUCCEED; } } else { if (game_get_currchar(game) >= MAX_CHAR_PER_GAME) { reply=D2CS_CLIENT_JOINGAMEREPLY_GAME_FULL; } else { reply=D2CS_CLIENT_JOINGAMEREPLY_SUCCEED; } } return reply; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_d2cs.h000066400000000000000000000022331462640461400201410ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_D2CS_H #define INCLUDED_HANDLE_D2CS_H #include "common/packet.h" #include "connection.h" namespace pvpgn { namespace d2cs { extern int d2cs_handle_d2cs_packet(t_connection * c, t_packet * packet); extern int d2cs_handle_client_creategame(t_connection * c, t_packet * packet); extern int d2cs_send_client_creategamewait(t_connection * c, unsigned int position); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_d2gs.cpp000066400000000000000000000461001462640461400205010ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "handle_d2gs.h" #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "common/eventlog.h" #include "common/addr.h" #include "common/trans.h" #include "d2gs.h" #include "serverqueue.h" #include "game.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { DECLARE_PACKET_HANDLER(on_d2gs_authreply) DECLARE_PACKET_HANDLER(on_d2gs_setgsinfo) DECLARE_PACKET_HANDLER(on_d2gs_echoreply) DECLARE_PACKET_HANDLER(on_d2gs_creategamereply) DECLARE_PACKET_HANDLER(on_d2gs_joingamereply) DECLARE_PACKET_HANDLER(on_d2gs_updategameinfo) DECLARE_PACKET_HANDLER(on_d2gs_closegame) static t_packet_handle_table d2gs_packet_handle_table[]={ /* 0x00 */ { 0, conn_state_none, NULL }, /* 0x01 */ { 0, conn_state_none, NULL }, /* 0x02 */ { 0, conn_state_none, NULL }, /* 0x03 */ { 0, conn_state_none, NULL }, /* 0x04 */ { 0, conn_state_none, NULL }, /* 0x05 */ { 0, conn_state_none, NULL }, /* 0x06 */ { 0, conn_state_none, NULL }, /* 0x07 */ { 0, conn_state_none, NULL }, /* 0x08 */ { 0, conn_state_none, NULL }, /* 0x09 */ { 0, conn_state_none, NULL }, /* 0x0a */ { 0, conn_state_none, NULL }, /* 0x0b */ { 0, conn_state_none, NULL }, /* 0x0c */ { 0, conn_state_none, NULL }, /* 0x0d */ { 0, conn_state_none, NULL }, /* 0x0e */ { 0, conn_state_none, NULL }, /* 0x0f */ { 0, conn_state_none, NULL }, /* 0x10 */ { 0, conn_state_none, NULL }, /* FIXME: shouldn't these three be at 0x10-0x12? (But I'm pretty sure I preserved the padding) */ /* 0x11 */ { sizeof(t_d2gs_d2cs_authreply), conn_state_connected, on_d2gs_authreply }, /* 0x12 */ { sizeof(t_d2gs_d2cs_setgsinfo), conn_state_authed, on_d2gs_setgsinfo }, /* 0x13 */ { sizeof(t_d2gs_d2cs_echoreply), conn_state_any, on_d2gs_echoreply }, /* 0x14 */ { 0, conn_state_none, NULL }, /* 0x15 */ { 0, conn_state_none, NULL }, /* 0x16 */ { 0, conn_state_none, NULL }, /* 0x17 */ { 0, conn_state_none, NULL }, /* 0x18 */ { 0, conn_state_none, NULL }, /* 0x19 */ { 0, conn_state_none, NULL }, /* 0x1a */ { 0, conn_state_none, NULL }, /* 0x1b */ { 0, conn_state_none, NULL }, /* 0x1c */ { 0, conn_state_none, NULL }, /* 0x1d */ { 0, conn_state_none, NULL }, /* 0x1e */ { 0, conn_state_none, NULL }, /* 0x1f */ { 0, conn_state_none, NULL }, /* 0x20 */ { sizeof(t_d2gs_d2cs_creategamereply),conn_state_authed, on_d2gs_creategamereply }, /* 0x21 */ { sizeof(t_d2gs_d2cs_joingamereply), conn_state_authed, on_d2gs_joingamereply }, /* 0x22 */ { sizeof(t_d2gs_d2cs_updategameinfo), conn_state_authed, on_d2gs_updategameinfo }, /* 0x23 */ { sizeof(t_d2gs_d2cs_closegame), conn_state_authed, on_d2gs_closegame }, }; static void d2gs_send_init_info(t_d2gs * gs, t_connection * c) { t_packet * rpacket; if ((rpacket=packet_create(packet_class_d2gs))) { packet_set_size(rpacket,sizeof(t_d2cs_d2gs_setinitinfo)); packet_set_type(rpacket,D2CS_D2GS_SETINITINFO); bn_int_set(&rpacket->u.d2cs_d2gs_setinitinfo.h.seqno,0); bn_int_set(&rpacket->u.d2cs_d2gs_setinitinfo.time, std::time(NULL)); bn_int_set(&rpacket->u.d2cs_d2gs_setinitinfo.gs_id, d2gs_get_id(gs)); bn_int_set(&rpacket->u.d2cs_d2gs_setinitinfo.ac_version, 0); // packet_append_string(rpacket,prefs_get_d2gs_ac_checksum()); packet_append_string(rpacket,"bogus_ac_checksum"); // packet_append_string(rpacket,prefs_get_d2gs_ac_string()); packet_append_string(rpacket,"bogus_ac_string"); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } return; } static void d2gs_send_server_conffile(t_d2gs *gs, t_connection *c) { t_packet *rpacket; std::FILE *fp; char buff[256]; int rno; struct stat sfile; /* open d2gs server config file */ fp = std::fopen(prefs_get_d2gsconffile(), "rb"); if (!fp) goto err; /* get file size */ if (fstat(fileno(fp),&sfile)) { eventlog(eventlog_level_debug,__FUNCTION__,"failed fstat()"); goto err_fp; } rpacket = packet_create(packet_class_d2cs); packet_set_size(rpacket,sizeof(t_d2cs_d2gs_setconffile)); packet_set_type(rpacket,D2CS_D2GS_SETCONFFILE); bn_int_set(&rpacket->u.d2cs_d2gs_setconffile.h.seqno,0); bn_int_set(&rpacket->u.d2cs_d2gs_setconffile.size, sfile.st_size); bn_int_set(&rpacket->u.d2cs_d2gs_setconffile.reserved1, std::time(NULL)); while((rno = std::fread(buff,1,sizeof(buff),fp)) > 0) { if (packet_append_data(rpacket,buff,rno) < 0) { /* file too big */ eventlog(eventlog_level_debug,__FUNCTION__,"conffile too big for a single packet"); goto err_packet; } } conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); std::fclose(fp); return; err_packet: packet_del_ref(rpacket); err_fp: std::fclose(fp); err: return; } static int on_d2gs_authreply(t_connection * c, t_packet * packet) { t_d2gs * gs; t_packet * rpacket; unsigned int reply; unsigned int try_checksum, checksum; unsigned int version, conf_version; unsigned int randnum, signlen; unsigned char *sign; if (!(gs=d2gslist_find_gs(conn_get_d2gs_id(c)))) { eventlog(eventlog_level_error,__FUNCTION__,"game server {} not found",conn_get_d2gs_id(c)); return -1; } version=bn_int_get(packet->u.d2gs_d2cs_authreply.version); try_checksum=bn_int_get(packet->u.d2gs_d2cs_authreply.checksum); checksum=d2gs_calc_checksum(c); conf_version = prefs_get_d2gs_version(); randnum = bn_int_get(packet->u.d2gs_d2cs_authreply.randnum); signlen = bn_int_get(packet->u.d2gs_d2cs_authreply.signlen); sign = packet->u.d2gs_d2cs_authreply.sign; if (conf_version && conf_version != version) { eventlog(eventlog_level_warn,__FUNCTION__,"game server {} version mismatch 0x{:X} - 0x{:X}",conn_get_d2gs_id(c), version,conf_version); } if (conf_version && !MAJOR_VERSION_EQUAL(version, conf_version, D2GS_MAJOR_VERSION_MASK)) { eventlog(eventlog_level_error,__FUNCTION__,"game server {} major version mismatch 0x{:X} - 0x{:X}",conn_get_d2gs_id(c), version,conf_version); reply=D2CS_D2GS_AUTHREPLY_BAD_VERSION; } else if (prefs_get_d2gs_checksum() && try_checksum != checksum) { eventlog(eventlog_level_error,__FUNCTION__,"game server {} checksum mismach 0x{:X} - 0x{:X}",conn_get_d2gs_id(c),try_checksum,checksum); reply=D2CS_D2GS_AUTHREPLY_BAD_CHECKSUM; // } else if (license_verify_reply(c, randnum, sign, signlen)) { // eventlog(eventlog_level_error,__FUNCTION__,"game server {} std::signal mismach", conn_get_d2gs_id(c)); // reply=D2CS_D2GS_AUTHREPLY_BAD_CHECKSUM; } else { reply=D2CS_D2GS_AUTHREPLY_SUCCEED; } if (reply==D2CS_D2GS_AUTHREPLY_SUCCEED) { eventlog(eventlog_level_info,__FUNCTION__,"game server {} authed",addr_num_to_ip_str(d2cs_conn_get_addr(c))); d2cs_conn_set_state(c,conn_state_authed); d2gs_send_server_conffile(gs, c); d2gs_send_init_info(gs, c); d2gs_active(gs,c); } else { eventlog(eventlog_level_error,__FUNCTION__,"game server {} failed to auth",addr_num_to_ip_str(d2cs_conn_get_addr(c))); /* d2cs_conn_set_state(c,conn_state_destroy); */ } if ((rpacket=packet_create(packet_class_d2gs))) { packet_set_size(rpacket,sizeof(t_d2cs_d2gs_authreply)); packet_set_type(rpacket,D2CS_D2GS_AUTHREPLY); bn_int_set(&rpacket->u.d2cs_d2gs_authreply.h.seqno,0); bn_int_set(&rpacket->u.d2cs_d2gs_authreply.reply,reply); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } return 0; } static int on_d2gs_setgsinfo(t_connection * c, t_packet * packet) { t_d2gs * gs; t_packet * rpacket; unsigned int maxgame, prev_maxgame; unsigned int currgame, gameflag; if (!(gs=d2gslist_find_gs(conn_get_d2gs_id(c)))) { eventlog(eventlog_level_error,__FUNCTION__,"game server {} not found",conn_get_d2gs_id(c)); return -1; } maxgame=bn_int_get(packet->u.d2gs_d2cs_setgsinfo.maxgame); gameflag=bn_int_get(packet->u.d2gs_d2cs_setgsinfo.gameflag); prev_maxgame=d2gs_get_maxgame(gs); currgame = d2gs_get_gamenum(gs); eventlog(eventlog_level_info, __FUNCTION__, "change game server {} max game from {} to {} ({} current)",addr_num_to_ip_str(d2cs_conn_get_addr(c)),prev_maxgame, maxgame, currgame); d2gs_set_maxgame(gs,maxgame); if ((rpacket=packet_create(packet_class_d2gs))) { packet_set_size(rpacket,sizeof(t_d2cs_d2gs_setgsinfo)); packet_set_type(rpacket,D2CS_D2GS_SETGSINFO); bn_int_set(&rpacket->u.d2cs_d2gs_setgsinfo.h.seqno,0); bn_int_set(&rpacket->u.d2cs_d2gs_setgsinfo.maxgame,maxgame); bn_int_set(&rpacket->u.d2cs_d2gs_setgsinfo.gameflag,gameflag); conn_push_outqueue(c,rpacket); packet_del_ref(rpacket); } gqlist_check_creategame(maxgame - currgame); return 0; } static int on_d2gs_echoreply(t_connection * c, t_packet * packet) { if (!c || !packet) return 0; return 0; } static int on_d2gs_creategamereply(t_connection * c, t_packet * packet) { t_packet * opacket, * rpacket; t_sq * sq; t_connection * client; t_game * game; int result; int reply; int seqno; t_elem * curr; seqno=bn_int_get(packet->u.d2cs_d2gs.h.seqno); if (!(sq=sqlist_find_sq(seqno))) { eventlog(eventlog_level_error,__FUNCTION__,"seqno {} not found",seqno); return 0; } if (!(client=d2cs_connlist_find_connection_by_sessionnum(sq_get_clientid(sq)))) { eventlog(eventlog_level_error,__FUNCTION__,"client {} not found",sq_get_clientid(sq)); sq_destroy(sq,&curr); return 0; } if (!(game=gamelist_find_game_by_id(sq_get_gameid(sq)))) { eventlog(eventlog_level_error,__FUNCTION__,"game {} not found",sq_get_gameid(sq)); sq_destroy(sq,&curr); return 0; } if (!(opacket=sq_get_packet(sq))) { eventlog(eventlog_level_error,__FUNCTION__,"previous packet not found (seqno: {})",seqno); sq_destroy(sq,&curr); return 0; } result=bn_int_get(packet->u.d2gs_d2cs_creategamereply.result); if (result==D2GS_D2CS_CREATEGAME_SUCCEED) { game_set_d2gs_gameid(game,bn_int_get(packet->u.d2gs_d2cs_creategamereply.gameid)); game_set_created(game,1); eventlog(eventlog_level_info,__FUNCTION__,"game {} created on gs {}",d2cs_game_get_name(game),conn_get_d2gs_id(c)); reply=D2CS_CLIENT_CREATEGAMEREPLY_SUCCEED; } else { eventlog(eventlog_level_warn,__FUNCTION__,"failed to create game {} on gs {}",d2cs_game_get_name(game),conn_get_d2gs_id(c)); game_destroy(game,&curr); reply=D2CS_CLIENT_CREATEGAMEREPLY_FAILED; } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_creategamereply)); packet_set_type(rpacket,D2CS_CLIENT_CREATEGAMEREPLY); bn_short_set(&rpacket->u.d2cs_client_creategamereply.seqno, bn_short_get(opacket->u.client_d2cs_creategamereq.seqno)); bn_short_set(&rpacket->u.d2cs_client_creategamereply.gameid,1); bn_short_set(&rpacket->u.d2cs_client_creategamereply.u1,1); bn_int_set(&rpacket->u.d2cs_client_creategamereply.reply,reply); conn_push_outqueue(client,rpacket); packet_del_ref(rpacket); } sq_destroy(sq,&curr); return 0; } static int on_d2gs_joingamereply(t_connection * c, t_packet * packet) { t_sq * sq; t_d2gs * gs; t_connection * client; t_game * game; t_packet * opacket, * rpacket; int result; int reply; int seqno; unsigned int gsaddr; t_elem * curr; unsigned short gsport; seqno=bn_int_get(packet->u.d2cs_d2gs.h.seqno); if (!(sq=sqlist_find_sq(seqno))) { eventlog(eventlog_level_error,__FUNCTION__,"seqno {} not found",seqno); return 0; } if (!(client=d2cs_connlist_find_connection_by_sessionnum(sq_get_clientid(sq)))) { eventlog(eventlog_level_error,__FUNCTION__,"client {} not found",sq_get_clientid(sq)); sq_destroy(sq,&curr); return 0; } if (!(game=gamelist_find_game_by_id(sq_get_gameid(sq)))) { eventlog(eventlog_level_error,__FUNCTION__,"game {} not found",sq_get_gameid(sq)); sq_destroy(sq,&curr); return 0; } if (!(gs=game_get_d2gs(game))) { eventlog(eventlog_level_error,__FUNCTION__,"try join game without game server set"); sq_destroy(sq,&curr); return 0; } if (!(opacket=sq_get_packet(sq))) { eventlog(eventlog_level_error,__FUNCTION__,"previous packet not found (seqno: {})",seqno); sq_destroy(sq,&curr); return 0; } result=bn_int_get(packet->u.d2gs_d2cs_joingamereply.result); switch (result) { case D2GS_D2CS_JOINGAME_SUCCEED: eventlog(eventlog_level_info,__FUNCTION__,"added {} to game {} on gs {}",d2cs_conn_get_charname(client), d2cs_game_get_name(game),conn_get_d2gs_id(c)); reply=D2CS_CLIENT_JOINGAMEREPLY_SUCCEED; break; case D2GS_D2CS_JOINGAME_GAME_FULL: eventlog(eventlog_level_info,__FUNCTION__,"failed to add {} to game {} on gs {} (game full)",d2cs_conn_get_charname(client), d2cs_game_get_name(game),conn_get_d2gs_id(c)); reply=D2CS_CLIENT_JOINGAMEREPLY_GAME_FULL; break; default: eventlog(eventlog_level_info,__FUNCTION__,"failed to add {} to game {} on gs {}",d2cs_conn_get_charname(client), d2cs_game_get_name(game),conn_get_d2gs_id(c)); reply=D2CS_CLIENT_JOINGAMEREPLY_FAILED; } if ((rpacket=packet_create(packet_class_d2cs))) { packet_set_size(rpacket,sizeof(t_d2cs_client_joingamereply)); packet_set_type(rpacket,D2CS_CLIENT_JOINGAMEREPLY); bn_short_set(&rpacket->u.d2cs_client_joingamereply.seqno, bn_short_get(opacket->u.client_d2cs_joingamereq.seqno)); /* specific flag for anti-cheating support */ /* bn_short_set(&rpacket->u.d2cs_client_joingamereply.gameid,(unsigned short)game_get_d2gs_gameid(game)|0x8000); */ bn_short_set(&rpacket->u.d2cs_client_joingamereply.gameid,(unsigned short)game_get_d2gs_gameid(game)); bn_short_set(&rpacket->u.d2cs_client_joingamereply.u1,0); bn_int_set(&rpacket->u.d2cs_client_joingamereply.reply,reply); // if (reply == SERVER_JOINGAMEREPLY2_REPLY_OK) { if (reply == D2CS_CLIENT_JOINGAMEREPLY_SUCCEED) { bn_int_set(&rpacket->u.d2cs_client_joingamereply.token,sq_get_gametoken(sq)); gsaddr = d2gs_get_ip(gs); gsport = 4000; trans_net(d2cs_conn_get_addr(client), &gsaddr, &gsport); if(d2gs_get_ip(gs)!=gsaddr) { eventlog(eventlog_level_info,__FUNCTION__,"translated gameserver {} -> {}",addr_num_to_ip_str(d2gs_get_ip(gs)),addr_num_to_ip_str(gsaddr)); } else { eventlog(eventlog_level_info,__FUNCTION__,"no translation required for gamserver {}",addr_num_to_ip_str(gsaddr)); } bn_int_nset(&rpacket->u.d2cs_client_joingamereply.addr,gsaddr); } else { bn_int_set(&rpacket->u.d2cs_client_joingamereply.token,0); bn_int_set(&rpacket->u.d2cs_client_joingamereply.addr,0); } conn_push_outqueue(client,rpacket); packet_del_ref(rpacket); } sq_destroy(sq,&curr); return 0; } static int on_d2gs_updategameinfo(t_connection * c, t_packet * packet) { unsigned int flag; char const * charname; t_game * game; unsigned int charclass; unsigned int charlevel; unsigned int d2gs_gameid; unsigned int d2gs_id; if (!(charname=packet_get_str_const(packet,sizeof(t_d2gs_d2cs_updategameinfo),MAX_CHARNAME_LEN))) { eventlog(eventlog_level_error,__FUNCTION__,"got bad charname"); return 0; } d2gs_id=conn_get_d2gs_id(c); d2gs_gameid=bn_int_get(packet->u.d2gs_d2cs_updategameinfo.gameid); charclass=bn_int_get(packet->u.d2gs_d2cs_updategameinfo.charclass); charlevel=bn_int_get(packet->u.d2gs_d2cs_updategameinfo.charlevel); flag=bn_int_get(packet->u.d2gs_d2cs_updategameinfo.flag); if (!(game=gamelist_find_game_by_d2gs_and_id(d2gs_id,d2gs_gameid))) { eventlog(eventlog_level_error,__FUNCTION__,"game {} not found on gs {}",d2gs_gameid,d2gs_id); return -1; } if (flag==D2GS_D2CS_UPDATEGAMEINFO_FLAG_ENTER) { game_add_character(game,charname,charclass,charlevel); } else if (flag==D2GS_D2CS_UPDATEGAMEINFO_FLAG_LEAVE) { game_del_character(game,charname); } else if (flag==D2GS_D2CS_UPDATEGAMEINFO_FLAG_UPDATE) { game_add_character(game,charname,charclass,charlevel); } else { eventlog(eventlog_level_error,__FUNCTION__,"got bad updategameinfo flag {}",flag); } return 0; } static int on_d2gs_closegame(t_connection * c, t_packet * packet) { t_game * game; t_elem * curr; if (!(game=gamelist_find_game_by_d2gs_and_id(conn_get_d2gs_id(c), bn_int_get(packet->u.d2gs_d2cs_closegame.gameid)))) { return 0; } game_destroy(game, &curr); return 0; } extern int handle_d2gs_packet(t_connection * c, t_packet * packet) { conn_process_packet(c,packet,d2gs_packet_handle_table,NELEMS(d2gs_packet_handle_table)); return 0; } extern int handle_d2gs_init(t_connection * c) { t_packet * packet; if ((packet=packet_create(packet_class_d2gs))) { packet_set_size(packet,sizeof(t_d2cs_d2gs_authreq)); packet_set_type(packet,D2CS_D2GS_AUTHREQ); bn_int_set(&packet->u.d2cs_d2gs_authreq.h.seqno,0); bn_int_set(&packet->u.d2cs_d2gs_authreq.sessionnum,d2cs_conn_get_sessionnum(c)); bn_int_set(&packet->u.d2cs_d2gs_authreq.signlen, 0); packet_append_string(packet,prefs_get_realmname()); // packet_append_data(packet, sign, signlen); conn_push_outqueue(c,packet); packet_del_ref(packet); } eventlog(eventlog_level_info,__FUNCTION__,"sent init packet to d2gs {} (sessionnum={})",conn_get_d2gs_id(c),d2cs_conn_get_sessionnum(c)); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_d2gs.h000066400000000000000000000020501462640461400201420ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_D2GS_H #define INCLUDED_HANDLE_D2GS_H #include "connection.h" #include "common/packet.h" namespace pvpgn { namespace d2cs { extern int handle_d2gs_packet(t_connection * c, t_packet * packet); extern int handle_d2gs_init(t_connection * c); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_init.cpp000066400000000000000000000054751462640461400206170ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "handle_init.h" #include "common/eventlog.h" #include "common/addr.h" #include "common/init_protocol.h" #include "d2gs.h" #include "handle_d2gs.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static int on_d2gs_initconn(t_connection * c); static int on_d2cs_initconn(t_connection * c); extern int d2cs_handle_init_packet(t_connection * c, t_packet * packet) { int cclass; int retval; ASSERT(c,-1); ASSERT(packet,-1); cclass=bn_byte_get(packet->u.client_initconn.cclass); switch (cclass) { case CLIENT_INITCONN_CLASS_D2CS: retval=on_d2cs_initconn(c); break; case CLIENT_INITCONN_CLASS_D2GS: retval=on_d2gs_initconn(c); break; default: eventlog(eventlog_level_error,__FUNCTION__,"got bad connection class {}",cclass); retval=-1; break; } return retval; } static int on_d2gs_initconn(t_connection * c) { t_d2gs * gs; eventlog(eventlog_level_info,__FUNCTION__,"[{}] client initiated d2gs connection",d2cs_conn_get_socket(c)); if (!(gs=d2gslist_find_gs_by_ip(d2cs_conn_get_addr(c)))) { // reload list and see if any dns addy's has changed if (d2gslist_reload(prefs_get_d2gs_list())<0) { eventlog(eventlog_level_error,__FUNCTION__,"error reloading game server list,exitting"); return -1; } //recheck if (!(gs=d2gslist_find_gs_by_ip(d2cs_conn_get_addr(c)))) { eventlog(eventlog_level_error,__FUNCTION__,"d2gs connection from invalid ip address {}",addr_num_to_ip_str(d2cs_conn_get_addr(c))); return -1; } } d2cs_conn_set_class(c,conn_class_d2gs); d2cs_conn_set_state(c,conn_state_connected); conn_set_d2gs_id(c,d2gs_get_id(gs)); if (handle_d2gs_init(c)<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to init d2gs connection"); return -1; } return 0; } static int on_d2cs_initconn(t_connection * c) { eventlog(eventlog_level_info,__FUNCTION__,"[{}] client initiated d2cs connection",d2cs_conn_get_socket(c)); d2cs_conn_set_class(c,conn_class_d2cs); d2cs_conn_set_state(c,conn_state_connected); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_init.h000066400000000000000000000017741462640461400202620ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_INIT_H #define INCLUDED_HANDLE_INIT_H #include "connection.h" #include "common/packet.h" namespace pvpgn { namespace d2cs { extern int d2cs_handle_init_packet(t_connection * c, t_packet * packet); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_signal.cpp000066400000000000000000000130531462640461400211200ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "handle_signal.h" #include #include #include #include "common/eventlog.h" #include "common/trans.h" #include "common/xalloc.h" #include "prefs.h" #include "cmdline.h" #include "d2gs.h" #include "d2ladder.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static void on_signal(int s); static volatile struct { unsigned char do_quit; unsigned char cancel_quit; unsigned char reload_config; unsigned char reload_ladder; unsigned char restart_d2gs; time_t exit_time; } signal_data ={ 0, 0, 0, 0, 0, 0 }; extern int handle_signal(void) { std::time_t now; char const * levels; char * temp; char const * tok; if (signal_data.cancel_quit) { signal_data.cancel_quit=0; if (!signal_data.exit_time) { eventlog(eventlog_level_info,__FUNCTION__,"there is no previous shutdown to be canceled"); } else { signal_data.exit_time=0; eventlog(eventlog_level_info,__FUNCTION__,"shutdown was canceled due to std::signal"); } } if (signal_data.do_quit) { signal_data.do_quit=0; now=std::time(NULL); if (!signal_data.exit_time) { signal_data.exit_time=now+d2cs_prefs_get_shutdown_delay(); } else { signal_data.exit_time-=d2cs_prefs_get_shutdown_decr(); } eventlog(eventlog_level_info,__FUNCTION__,"the server is going to shutdown in {} minutes",(signal_data.exit_time-now)/60); } if (signal_data.exit_time) { now=std::time(NULL); if (now >= (signed)signal_data.exit_time) { signal_data.exit_time=0; eventlog(eventlog_level_info,__FUNCTION__,"shutdown server due to std::signal"); return -1; } } if (signal_data.reload_config) { signal_data.reload_config=0; eventlog(eventlog_level_info,__FUNCTION__,"reloading configuartion file due to std::signal"); if (prefs_reload(cmdline_get_preffile())<0) { eventlog(eventlog_level_error,__FUNCTION__,"error reload configuration file,exitting"); return -1; } if (d2gslist_reload(prefs_get_d2gs_list())<0) { eventlog(eventlog_level_error,__FUNCTION__,"error reloading game server list,exitting"); return -1; } if (trans_reload(d2cs_prefs_get_transfile(),TRANS_D2CS)<0) { eventlog(eventlog_level_error,__FUNCTION__,"could not reload trans list"); } eventlog_clear_level(); if ((levels = d2cs_prefs_get_loglevels())) { temp = xstrdup(levels); tok = std::strtok(temp,","); /* std::strtok modifies the string it is passed */ while (tok) { if (eventlog_add_level(tok)<0) eventlog(eventlog_level_error,__FUNCTION__,"could not add std::log level \"{}\"",tok); tok = std::strtok(NULL,","); } xfree(temp); } #ifdef DO_DAEMONIZE if (!cmdline_get_foreground()) #endif eventlog_open(d2cs_prefs_get_logfile()); } if (signal_data.reload_ladder) { signal_data.reload_ladder=0; eventlog(eventlog_level_info,__FUNCTION__,"reloading ladder data due to std::signal"); d2ladder_refresh(); } if (signal_data.restart_d2gs) { signal_data.restart_d2gs=0; eventlog(eventlog_level_info,__FUNCTION__,"restarting all game servers due to std::signal"); d2gs_restart_all_gs(); } return 0; } #ifdef WIN32 extern void signal_quit_wrapper(void) { signal_data.do_quit=1; } extern void signal_reload_config_wrapper(void) { signal_data.reload_config = 1; } extern void signal_load_ladder_wrapper(void) { signal_data.reload_ladder = 1; } extern void signal_exit_wrapper(void) { signal_data.exit_time = 1; } extern void signal_restart_d2gs_wrapper(void) { signal_data.restart_d2gs = 1; } #else extern int handle_signal_init(void) { std::signal(SIGINT,on_signal); std::signal(SIGTERM,on_signal); std::signal(SIGABRT,on_signal); std::signal(SIGHUP,on_signal); std::signal(SIGUSR1,on_signal); std::signal(SIGUSR2,on_signal); std::signal(SIGPIPE,on_signal); return 0; } static void on_signal(int s) { switch (s) { case SIGINT: eventlog(eventlog_level_debug,__FUNCTION__,"sigint received"); signal_data.do_quit=1; break; case SIGTERM: eventlog(eventlog_level_debug,__FUNCTION__,"sigint received"); signal_data.do_quit=1; break; case SIGABRT: eventlog(eventlog_level_debug,__FUNCTION__,"sigabrt received"); signal_data.cancel_quit=1; break; case SIGHUP: eventlog(eventlog_level_debug,__FUNCTION__,"sighup received"); signal_data.reload_config=1; break; case SIGUSR1: eventlog(eventlog_level_debug,__FUNCTION__,"sigusr1 received"); signal_data.reload_ladder=1; break; case SIGUSR2: eventlog(eventlog_level_debug,__FUNCTION__,"sigusr2 received"); signal_data.restart_d2gs=1; break; case SIGPIPE: eventlog(eventlog_level_debug,__FUNCTION__,"sigpipe received"); break; } std::signal(s,on_signal); } #endif } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/handle_signal.h000066400000000000000000000023131462640461400205620ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_SIGNAL_H #define INCLUDED_HANDLE_SIGNAL_H namespace pvpgn { namespace d2cs { #ifndef WIN32 extern int handle_signal_init(void); #else extern void signal_quit_wrapper(void); extern void signal_reload_config_wrapper(void); extern void signal_load_ladder_wrapper(void); extern void signal_exit_wrapper(void); extern void signal_restart_d2gs_wrapper(void); #endif extern int handle_signal(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/main.cpp000066400000000000000000000153151462640461400172570ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef WIN32 # include "win32/service.h" #endif #ifdef WIN32_GUI # include "win32/winmain.h" #endif #ifdef WIN32 # include "win32/windump.h" #endif #include "compat/stdfileno.h" #include "compat/pgetpid.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/trans.h" #include "common/fdwatch.h" #include "prefs.h" #include "connection.h" #include "d2gs.h" #include "serverqueue.h" #include "d2ladder.h" #include "cmdline.h" #include "game.h" #include "server.h" #include "version.h" #include "common/setup_after.h" using namespace pvpgn::d2cs; using namespace pvpgn; #ifdef WIN32 char serviceLongName[] = "d2cs service"; char serviceName[] = "d2cs"; char serviceDescription[] = "Diablo 2 Character Server"; int g_ServiceStatus = -1; #endif static int init(void); static int cleanup(void); static int config_init(int argc, char * * argv); static int config_cleanup(void); static int setup_daemon(void); static char * write_to_pidfile(void); #ifdef DO_DAEMONIZE static int setup_daemon(void) { int pid; if (chdir("/")<0) { eventlog(eventlog_level_error,__FUNCTION__,"can not change working directory to root directory (chdir: {})",std::strerror(errno)); return -1; } close(STDIN_FILENO); close(STDOUT_FILENO); if (!cmdline_get_foreground()) { close(STDERR_FILENO); } switch ((pid = fork())) { case 0: break; case -1: eventlog(eventlog_level_error,__FUNCTION__,"error create child process (fork: {})",std::strerror(errno)); return -1; default: return pid; } umask(0); setsid(); return 0; } #endif static char * write_to_pidfile(void) { char *pidfile = xstrdup(prefs_get_pidfile()); if (pidfile) { if (pidfile[0] == '\0') { xfree((void *)pidfile); /* avoid warning */ return NULL; } #ifdef HAVE_GETPID std::FILE * fp; if (!(fp = std::fopen(pidfile,"w"))) { eventlog(eventlog_level_error,__FUNCTION__,"unable to open pid file \"{}\" for writing (std::fopen: {})",pidfile,std::strerror(errno)); xfree((void *)pidfile); /* avoid warning */ return NULL; } else { std::fprintf(fp,"%u",(unsigned int)getpid()); if (std::fclose(fp)<0) eventlog(eventlog_level_error,__FUNCTION__,"could not close pid file \"{}\" after writing (std::fclose: {})",pidfile,std::strerror(errno)); } #else eventlog(eventlog_level_warn,__FUNCTION__,"no getpid() std::system call, disable pid file in d2cs.conf"); xfree((void *)pidfile); /* avoid warning */ return NULL; #endif } return pidfile; } static int init(void) { d2cs_connlist_create(); d2cs_gamelist_create(); sqlist_create(); d2gslist_create(); gqlist_create(); d2ladder_init(); if(trans_load(d2cs_prefs_get_transfile(),TRANS_D2CS)<0) eventlog(eventlog_level_error,__FUNCTION__,"could not load trans list"); fdwatch_init(prefs_get_max_connections()); return 0; } static int cleanup(void) { d2ladder_destroy(); d2cs_connlist_destroy(); d2cs_gamelist_destroy(); sqlist_destroy(); d2gslist_destroy(); gqlist_destroy(); trans_unload(); fdwatch_close(); return 0; } static int config_init(int argc, char * * argv) { char const * levels; char * temp; char const * tok; if (cmdline_load(argc, argv) != 1) { return -1; } #ifdef DO_DAEMONIZE int pid; if ((!cmdline_get_foreground())) { if (!((pid = setup_daemon()) == 0)) { return pid; } } #endif if (d2cs_prefs_load(cmdline_get_preffile())<0) { eventlog(eventlog_level_error,__FUNCTION__,"error loading configuration file {}",cmdline_get_preffile()); return -1; } eventlog_clear_level(); if ((levels = d2cs_prefs_get_loglevels())) { temp = xstrdup(levels); tok = std::strtok(temp,","); /* std::strtok modifies the string it is passed */ while (tok) { if (eventlog_add_level(tok)<0) eventlog(eventlog_level_error,__FUNCTION__,"could not add std::log level \"{}\"",tok); tok = std::strtok(NULL,","); } xfree(temp); } #ifdef WIN32_GUI if (cmdline_get_gui()){ eventlog_add_level(eventlog_get_levelname_str(eventlog_level_gui)); } #endif #ifdef DO_DAEMONIZE if (cmdline_get_foreground()) { eventlog_set(stderr); } else #endif { if (cmdline_get_logfile()) { if (eventlog_open(cmdline_get_logfile())<0) { eventlog(eventlog_level_error,__FUNCTION__,"error open eventlog file {}",cmdline_get_logfile()); return -1; } } else { if (eventlog_open(d2cs_prefs_get_logfile())<0) { eventlog(eventlog_level_error,__FUNCTION__,"error open eventlog file {}",d2cs_prefs_get_logfile()); return -1; } } } return 0; } static int config_cleanup(void) { d2cs_prefs_unload(); cmdline_unload(); return 0; } #ifdef WIN32_GUI extern int app_main(int argc, char ** argv) #else extern int main(int argc, char ** argv) #endif { int pid; #ifdef WIN32 // create a dump file whenever the gateway crashes SetUnhandledExceptionFilter(unhandled_handler); #endif eventlog_set(stderr); if (!((pid = config_init(argc, argv)) == 0)) { // if (pid==1) pid=0; return pid; } const char* const pidfile = write_to_pidfile(); eventlog(eventlog_level_info,__FUNCTION__,D2CS_VERSION); if (init()<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to init"); if (pidfile) xfree((void*)pidfile); return -1; } else { eventlog(eventlog_level_info,__FUNCTION__,"server initialized"); } if (d2cs_server_process()<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to run server"); if (pidfile) xfree((void*)pidfile); return -1; } cleanup(); if (pidfile) { if (std::remove(pidfile)<0) eventlog(eventlog_level_error,__FUNCTION__,"could not remove pid file \"{}\" (std::remove: {})",pidfile,std::strerror(errno)); xfree((void *)pidfile); /* avoid warning */ } config_cleanup(); eventlog_close(); return 0; } pvpgn-1.99.7.2.1+dfsg/src/d2cs/net.cpp000066400000000000000000000143131462640461400171160ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "net.h" #include #include #include "compat/strerror.h" #include "common/eventlog.h" #include "compat/psock.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { /* FIXME: use addr.h code to do this and make a separate error * return path so that 255.255.255.255 isn't an error. */ extern unsigned long int net_inet_addr(char const * host) { struct hostent * hp; if (std::isdigit((int)host[0])) { return inet_addr(host); } else { hp=gethostbyname(host); if (!hp || !(hp->h_addr_list)) { return ~0UL; } return *(unsigned long *)hp->h_addr_list[0]; } } extern int net_socket(int type) { int sock; int val; int ipproto; if (type==PSOCK_SOCK_STREAM) { ipproto = PSOCK_IPPROTO_TCP; } else { ipproto = PSOCK_IPPROTO_UDP; } if ((sock=psock_socket(PSOCK_PF_INET, type, ipproto))<0) { eventlog(eventlog_level_error,__FUNCTION__,"error creating socket (psock_socket: {})", pstrerror(psock_errno())); return -1; } val=1; if (psock_setsockopt(sock,PSOCK_SOL_SOCKET, PSOCK_SO_KEEPALIVE, &val, sizeof(val))<0) { eventlog(eventlog_level_error,__FUNCTION__,"error set socket option KEEPALIVE (psock_setsockopt: {})",pstrerror(psock_errno())); } if (psock_ctl(sock,PSOCK_NONBLOCK)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error set socket mode to non-block (psock_ctl: {})",pstrerror(psock_errno())); psock_close(sock); return -1; } return sock; } extern int net_check_connected(int sock) { int err; psock_t_socklen errlen; err = 0; errlen = sizeof(err); if (psock_getsockopt(sock,PSOCK_SOL_SOCKET, PSOCK_SO_ERROR, &err, &errlen)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error get socket option SO_ERROR (psock_getsockopt: {})",pstrerror(psock_errno())); return -1; } if (errlen && err) return -1; return 0; } extern int net_listen(unsigned int ip, unsigned int port, int type) { int sock; int val; struct sockaddr_in addr; int ipproto; if (type==PSOCK_SOCK_STREAM) { ipproto = PSOCK_IPPROTO_TCP; } else { ipproto = PSOCK_IPPROTO_UDP; } if ((sock=psock_socket(PSOCK_PF_INET, type, ipproto))<0) { eventlog(eventlog_level_error,__FUNCTION__,"error create listen socket"); return -1; } val=1; if (psock_setsockopt(sock,PSOCK_SOL_SOCKET,PSOCK_SO_REUSEADDR,&val,sizeof(int))<0) { eventlog(eventlog_level_error,__FUNCTION__,"error set socket option SO_REUSEADDR"); } std::memset(&addr,0,sizeof(addr)); addr.sin_family=PSOCK_AF_INET; addr.sin_port=htons(port); addr.sin_addr.s_addr=htonl(ip); if (psock_bind(sock,(struct sockaddr *)&addr, sizeof(addr))<0) { eventlog(eventlog_level_error,__FUNCTION__,"error bind listen socket"); psock_close(sock); return -1; } if (psock_listen(sock,LISTEN_QUEUE)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error listen socket"); psock_close(sock); return -1; } return sock; } extern int net_send_data(int sock, char * buff, int buffsize, int * pos, int * currsize) { int nsend; ASSERT(buff,-1); ASSERT(pos,-1); if (*pos>buffsize) { eventlog(eventlog_level_error,__FUNCTION__,"[{}] send buffer overflow pos={} buffsize={}",sock,*pos,buffsize); return -1; } if (*currsize>*pos) { eventlog(eventlog_level_error,__FUNCTION__,"[{}] send buffer error currsize={} pos={}",sock,*currsize,*pos); return -1; } nsend=psock_send(sock,buff+*pos-*currsize,*currsize,0); if (nsend==0) { eventlog(eventlog_level_error,__FUNCTION__,"[{}] no data sent (close connection)",sock); return -1; } if (nsend<0) { if ( #ifdef PSOCK_EINTR psock_errno()==PSOCK_EINTR || #endif #ifdef PSOCK_EAGAIN psock_errno()==PSOCK_EAGAIN || #endif #ifdef PSOCK_EWOULDBLOCK psock_errno()==PSOCK_EWOULDBLOCK || #endif #ifdef PSOCK_NOBUFS psock_errno()==PSOCK_ENOBUFS || #endif #ifdef PSOCK_ENOMEM psock_errno()==ENOMEM || #endif 0) return 0; eventlog(eventlog_level_error,__FUNCTION__,"[{}] error sent data (closing connection) (psock_send: {})",sock,pstrerror(psock_errno())); return -1; } *currsize -= nsend; return 1; } extern int net_recv_data(int sock, char * buff, int buffsize, int * pos, int * currsize) { int nrecv; ASSERT(buff,-1); ASSERT(pos,-1); if (*pos>buffsize) { eventlog(eventlog_level_error,__FUNCTION__,"[{}] recv buffer overflow pos={} buffsize={}",sock,*pos,buffsize); return -1; } if (*currsize>*pos) { eventlog(eventlog_level_error,__FUNCTION__,"[{}] recv buffer error currsize={} pos={}",sock,*currsize,*pos); return -1; } if (*pos==buffsize) { std::memmove(buff,buff+*pos,*currsize); *pos=0; } nrecv=psock_recv(sock,buff+*pos,buffsize-*pos,0); if (nrecv==0) { eventlog(eventlog_level_info,__FUNCTION__,"[{}] remote host closed connection",sock); return -1; } if (nrecv<0) { if ( #ifdef PSOCK_EINTR psock_errno()==PSOCK_EINTR || #endif #ifdef PSOCK_EAGAIN psock_errno()==PSOCK_EAGAIN || #endif #ifdef PSOCK_EWOULDBLOCK psock_errno()==PSOCK_EWOULDBLOCK || #endif #ifdef PSOCK_ENOMEM psock_errno()==ENOMEM || #endif 0) return 0; if ( #ifdef PSOCK_ENOTCONN psock_errno()==PSOCK_ENOTCONN || #endif #ifdef PSOCK_ECONNRESET psock_errno()==PSOCK_ECONNRESET || #endif 0) { eventlog(eventlog_level_error,__FUNCTION__,"[{}] remote host closed connection (psock_recv: {})",sock,pstrerror(psock_errno())); return -1; } eventlog(eventlog_level_error,__FUNCTION__,"[{}] recv error (closing connection) (psock_recv: {})",sock,pstrerror(psock_errno())); return -1; } * currsize += nrecv; return 1; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/net.h000066400000000000000000000023701462640461400165630ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_NET_H #define INCLUDED_NET_H namespace pvpgn { namespace d2cs { extern int net_socket(int type); extern unsigned long int net_inet_addr(char const * host); extern int net_check_connected(int sock); extern int net_listen(unsigned int ip, unsigned int port, int type); extern int net_send_data(int sock, char * buff, int buffsize, int * pos, int * currsize); extern int net_recv_data(int sock, char * buff, int buffsize, int * pos, int * currsize); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/prefs.cpp000066400000000000000000001041271462640461400174520ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "prefs.h" #include #include #include "common/conf.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static struct { char const * logfile; char const * loglevels; char const * servaddrs; char const * gameservlist; char const * bnetdaddr; char const * charsavedir; char const * charinfodir; char const * bak_charsavedir; char const * bak_charinfodir; char const * ladderdir; char const * newbiefile_amazon; char const * newbiefile_sorceress; char const * newbiefile_necromancer; char const * newbiefile_paladin; char const * newbiefile_barbarian; char const * newbiefile_druid; char const * newbiefile_assasin; char const * motd; char const * realmname; char const * d2gs_password; char const * transfile; char const * account_allowed_symbols; char const * d2gsconffile; char const * pidfile; unsigned int ladder_refresh_interval; unsigned int maxchar; unsigned int listpurgeinterval; unsigned int gqcheckinterval; unsigned int s2s_retryinterval; unsigned int s2s_timeout; unsigned int s2s_idletime; unsigned int sq_checkinterval; unsigned int sq_timeout; unsigned int maxgamelist; unsigned int max_game_idletime; unsigned int gamelist_showall; unsigned int hide_pass_games; unsigned int game_maxlifetime; unsigned int game_maxlevel; unsigned int allow_gamelimit; unsigned int allow_newchar; unsigned int idletime; unsigned int shutdown_delay; unsigned int shutdown_decr; unsigned int d2gs_checksum; unsigned int d2gs_version; unsigned int check_multilogin; unsigned int timeout_checkinterval; unsigned int s2s_keepalive_interval; unsigned int lod_realm; unsigned int allow_convert; unsigned int d2gs_restart_delay; std::time_t ladder_start_time; unsigned int char_expire_day; char const * charlist_sort; char const * charlist_sort_order; unsigned int max_connections; } prefs_conf; static int conf_set_logfile(const char* valstr); static int conf_setdef_logfile(void); static int conf_set_loglevels(const char* valstr); static int conf_setdef_loglevels(void); static int conf_set_servaddrs(const char* valstr); static int conf_setdef_servaddrs(void); static int conf_set_gameservlist(const char* valstr); static int conf_setdef_gameservlist(void); static int conf_set_bnetdaddr(const char* valstr); static int conf_setdef_bnetaddr(void); static int conf_set_charsavedir(const char* valstr); static int conf_setdef_charsavedir(void); static int conf_set_charinfodir(const char* valstr); static int conf_setdef_charinfodir(void); static int conf_set_bakcharsavedir(const char* valstr); static int conf_setdef_backcharsavedir(void); static int conf_set_bakcharinfodir(const char* valstr); static int conf_setdef_backcharinfodir(void); static int conf_set_ladderdir(const char* valstr); static int conf_setdef_ladderdir(void); static int conf_set_ladder_start_time(const char* valstr); static int conf_setdef_ladder_start_time(void); static int conf_set_ladder_refresh_interval(const char* valstr); static int conf_setdef_ladder_refresh_interval(void); static int conf_set_newbiefile_amazon(const char* valstr); static int conf_setdef_newbiefile_amazon(void); static int conf_set_newbiefile_sorceress(const char* valstr); static int conf_setdef_newbiefile_sorceress(void); static int conf_set_newbiefile_necromancer(const char* valstr); static int conf_setdef_newbiefile_necromancer(void); static int conf_set_newbiefile_paladin(const char* valstr); static int conf_setdef_newbiefile_paladin(void); static int conf_set_newbiefile_barbarian(const char* valstr); static int conf_setdef_newbiefile_barbarian(void); static int conf_set_newbiefile_druid(const char* valstr); static int conf_setdef_newbiefile_druid(void); static int conf_set_newbiefile_assasin(const char* valstr); static int conf_setdef_newbiefile_assasin(void); static int conf_set_transfile(const char* valstr); static int conf_setdef_transfile(void); static int conf_set_motd(const char* valstr); static int conf_setdef_motd(void); static int conf_set_realmname(const char* valstr); static int conf_setdef_realmname(void); static int conf_set_maxchar(const char* valstr); static int conf_setdef_maxchar(void); static int conf_set_listpurgeinterval(const char* valstr); static int conf_setdef_listpurgeinterval(void); static int conf_set_gqcheckinterval(const char* valstr); static int conf_setdef_gqcheckinterval(void); static int conf_set_maxgamelist(const char* valstr); static int conf_setdef_maxgamelist(void); static int conf_set_max_game_idletime(const char* valstr); static int conf_setdef_max_game_idletime(void); static int conf_set_gamelist_showall(const char* valstr); static int conf_setdef_gamelist_showall(void); static int conf_set_hide_pass_games(const char* valstr); static int conf_setdef_hide_pass_games(void); static int conf_set_game_maxlifetime(const char* valstr); static int conf_setdef_game_maxlifetime(void); static int conf_set_game_maxlevel(const char* valstr); static int conf_setdef_game_maxlevel(void); static int conf_set_allow_gamelimit(const char* valstr); static int conf_setdef_allow_gamelimit(void); static int conf_set_allow_newchar(const char* valstr); static int conf_setdef_allow_newchar(void); static int conf_set_idletime(const char* valstr); static int conf_setdef_idletime(void); static int conf_set_shutdown_delay(const char* valstr); static int conf_setdef_shutdown_delay(void); static int conf_set_shutdown_decr(const char* valstr); static int conf_setdef_shutdown_decr(void); static int conf_set_s2s_retryinterval(const char* valstr); static int conf_setdef_s2s_retryinterval(void); static int conf_set_s2s_timeout(const char* valstr); static int conf_setdef_s2s_timeout(void); static int conf_set_sq_checkinterval(const char* valstr); static int conf_setdef_sq_checkinterval(void); static int conf_set_sq_timeout(const char* valstr); static int conf_setdef_sq_timeout(void); static int conf_set_d2gs_checksum(const char* valstr); static int conf_setdef_d2gs_checksum(void); static int conf_set_d2gs_version(const char* valstr); static int conf_setdef_d2gs_version(void); static int conf_set_d2gs_password(const char* valstr); static int conf_setdef_d2gs_password(void); static int conf_set_check_multilogin(const char* valstr); static int conf_setdef_check_multilogin(void); static int conf_set_s2s_idletime(const char* valstr); static int conf_setdef_s2s_idletime(void); static int conf_set_s2s_keepalive_interval(const char* valstr); static int conf_setdef_s2s_keepalive_interval(void); static int conf_set_timeout_checkinterval(const char* valstr); static int conf_setdef_timeout_checkinterval(void); static int conf_set_lod_realm(const char* valstr); static int conf_setdef_lod_realm(void); static int conf_set_allow_convert(const char* valstr); static int conf_setdef_allow_convert(void); static int conf_set_account_allowed_symbols(const char* valstr); static int conf_setdef_account_allowed_symbols(void); static int conf_set_d2gs_restart_delay(const char* valstr); static int conf_setdef_d2gs_restart_delay(void); static int conf_set_char_expire_day(const char* valstr); static int conf_setdef_char_expire_day(void); static int conf_set_d2gsconffile(const char* valstr); static int conf_setdef_d2gsconffile(void); static int conf_set_charlist_sort(const char* valstr); static int conf_setdef_charlist_sort(void); static int conf_set_charlist_sort_order(const char* valstr); static int conf_setdef_charlist_sort_order(void); static int conf_set_max_connections(const char* valstr); static int conf_setdef_max_connections(void); static int conf_set_pidfile(const char* valstr); static int conf_setdef_pidfile(void); static t_conf_entry prefs_conf_table[]={ { "logfile", conf_set_logfile, NULL, conf_setdef_logfile }, { "loglevels", conf_set_loglevels, NULL, conf_setdef_loglevels}, { "servaddrs", conf_set_servaddrs, NULL, conf_setdef_servaddrs}, { "gameservlist", conf_set_gameservlist, NULL, conf_setdef_gameservlist}, { "bnetdaddr", conf_set_bnetdaddr, NULL, conf_setdef_bnetaddr}, { "charsavedir", conf_set_charsavedir, NULL, conf_setdef_charsavedir}, { "charinfodir", conf_set_charinfodir, NULL, conf_setdef_charinfodir}, { "bak_charsavedir", conf_set_bakcharsavedir, NULL, conf_setdef_backcharsavedir}, { "bak_charinfodir", conf_set_bakcharinfodir, NULL, conf_setdef_backcharinfodir}, { "ladderdir", conf_set_ladderdir, NULL, conf_setdef_ladderdir}, { "ladder_start_time", conf_set_ladder_start_time, NULL, conf_setdef_ladder_start_time}, { "ladder_refresh_interval",conf_set_ladder_refresh_interval,NULL, conf_setdef_ladder_refresh_interval}, { "newbiefile_amazon", conf_set_newbiefile_amazon, NULL, conf_setdef_newbiefile_amazon }, { "newbiefile_sorceress", conf_set_newbiefile_sorceress, NULL, conf_setdef_newbiefile_sorceress }, { "newbiefile_necromancer", conf_set_newbiefile_necromancer, NULL, conf_setdef_newbiefile_necromancer }, { "newbiefile_paladin", conf_set_newbiefile_paladin, NULL, conf_setdef_newbiefile_paladin }, { "newbiefile_barbarian", conf_set_newbiefile_barbarian, NULL, conf_setdef_newbiefile_barbarian }, { "newbiefile_druid", conf_set_newbiefile_druid, NULL, conf_setdef_newbiefile_druid }, { "newbiefile_assasin", conf_set_newbiefile_assasin, NULL, conf_setdef_newbiefile_assasin }, { "transfile", conf_set_transfile, NULL, conf_setdef_transfile}, { "pidfile", conf_set_pidfile, NULL, conf_setdef_pidfile}, { "motd", conf_set_motd, NULL, conf_setdef_motd}, { "realmname", conf_set_realmname, NULL, conf_setdef_realmname}, { "maxchar", conf_set_maxchar, NULL, conf_setdef_maxchar}, { "listpurgeinterval", conf_set_listpurgeinterval, NULL, conf_setdef_listpurgeinterval}, { "gqcheckinterval", conf_set_gqcheckinterval, NULL, conf_setdef_gqcheckinterval}, { "maxgamelist", conf_set_maxgamelist, NULL, conf_setdef_maxgamelist}, { "max_game_idletime", conf_set_max_game_idletime, NULL, conf_setdef_max_game_idletime}, { "gamelist_showall", conf_set_gamelist_showall, NULL, conf_setdef_gamelist_showall}, { "hide_pass_games", conf_set_hide_pass_games, NULL, conf_setdef_hide_pass_games}, { "game_maxlifetime", conf_set_game_maxlifetime, NULL, conf_setdef_game_maxlifetime}, { "game_maxlevel", conf_set_game_maxlevel, NULL, conf_setdef_game_maxlevel}, { "allow_gamelimit", conf_set_allow_gamelimit, NULL, conf_setdef_allow_gamelimit}, { "allow_newchar", conf_set_allow_newchar, NULL, conf_setdef_allow_newchar}, { "idletime", conf_set_idletime, NULL, conf_setdef_idletime}, { "shutdown_delay", conf_set_shutdown_delay, NULL, conf_setdef_shutdown_delay}, { "shutdown_decr", conf_set_shutdown_decr, NULL, conf_setdef_shutdown_decr}, { "s2s_retryinterval", conf_set_s2s_retryinterval, NULL, conf_setdef_s2s_retryinterval}, { "s2s_timeout", conf_set_s2s_timeout, NULL, conf_setdef_s2s_timeout}, { "sq_checkinterval", conf_set_sq_checkinterval, NULL, conf_setdef_sq_checkinterval}, { "sq_timeout", conf_set_sq_timeout, NULL, conf_setdef_sq_timeout}, { "d2gs_checksum", conf_set_d2gs_checksum, NULL, conf_setdef_d2gs_checksum}, { "d2gs_version", conf_set_d2gs_version, NULL, conf_setdef_d2gs_version}, { "d2gs_password", conf_set_d2gs_password, NULL, conf_setdef_d2gs_password}, { "check_multilogin", conf_set_check_multilogin, NULL, conf_setdef_check_multilogin}, { "s2s_idletime", conf_set_s2s_idletime, NULL, conf_setdef_s2s_idletime}, { "s2s_keepalive_interval", conf_set_s2s_keepalive_interval, NULL, conf_setdef_s2s_keepalive_interval}, { "timeout_checkinterval", conf_set_timeout_checkinterval, NULL, conf_setdef_timeout_checkinterval}, { "lod_realm", conf_set_lod_realm, NULL, conf_setdef_lod_realm}, { "allow_convert", conf_set_allow_convert, NULL, conf_setdef_allow_convert}, { "account_allowed_symbols",conf_set_account_allowed_symbols,NULL, conf_setdef_account_allowed_symbols}, { "d2gs_restart_delay", conf_set_d2gs_restart_delay, NULL, conf_setdef_d2gs_restart_delay}, { "char_expire_day", conf_set_char_expire_day, NULL, conf_setdef_char_expire_day}, { "d2gsconffile", conf_set_d2gsconffile, NULL, conf_setdef_d2gsconffile}, { "charlist_sort", conf_set_charlist_sort, NULL, conf_setdef_charlist_sort}, { "charlist_sort_order", conf_set_charlist_sort_order, NULL, conf_setdef_charlist_sort_order}, { "max_connections", conf_set_max_connections, NULL, conf_setdef_max_connections}, { NULL, NULL, NULL, NULL } }; extern int d2cs_prefs_load(char const * filename) { std::FILE *fd; if (!filename) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL filename"); return -1; } fd = std::fopen(filename,"rt"); if (!fd) { eventlog(eventlog_level_error,__FUNCTION__,"could not open file '{}'",filename); return -1; } if (conf_load_file(fd,prefs_conf_table)) { eventlog(eventlog_level_error,__FUNCTION__,"error loading config file '{}'",filename); std::fclose(fd); return -1; } std::fclose(fd); return 0; } extern int prefs_reload(char const * filename) { d2cs_prefs_unload(); if (d2cs_prefs_load(filename)<0) return -1; return 0; } extern int d2cs_prefs_unload(void) { conf_unload(prefs_conf_table); return 0; } extern char const * prefs_get_servaddrs(void) { return prefs_conf.servaddrs; } static int conf_set_servaddrs(const char* valstr) { return conf_set_str(&prefs_conf.servaddrs,valstr,NULL); } static int conf_setdef_servaddrs(void) { return conf_set_str(&prefs_conf.servaddrs,NULL,D2CS_SERVER_ADDRS); } extern char const * prefs_get_charsave_dir(void) { return prefs_conf.charsavedir; } static int conf_set_charsavedir(const char* valstr) { return conf_set_str(&prefs_conf.charsavedir,valstr,NULL); } static int conf_setdef_charsavedir(void) { return conf_set_str(&prefs_conf.charsavedir,NULL,D2CS_CHARSAVE_DIR); } extern char const * prefs_get_charinfo_dir(void) { return prefs_conf.charinfodir; } static int conf_set_charinfodir(const char* valstr) { return conf_set_str(&prefs_conf.charinfodir,valstr,NULL); } static int conf_setdef_charinfodir(void) { return conf_set_str(&prefs_conf.charinfodir,NULL,D2CS_CHARINFO_DIR); } extern char const * prefs_get_bak_charsave_dir(void) { return prefs_conf.bak_charsavedir; } static int conf_set_bakcharsavedir(const char* valstr) { return conf_set_str(&prefs_conf.bak_charsavedir,valstr,NULL); } static int conf_setdef_backcharsavedir(void) { return conf_set_str(&prefs_conf.bak_charsavedir,NULL,D2CS_BAK_CHARSAVE_DIR); } extern char const * prefs_get_bak_charinfo_dir(void) { return prefs_conf.bak_charinfodir; } static int conf_set_bakcharinfodir(const char* valstr) { return conf_set_str(&prefs_conf.bak_charinfodir,valstr,NULL); } static int conf_setdef_backcharinfodir(void) { return conf_set_str(&prefs_conf.bak_charinfodir,NULL,D2CS_BAK_CHARINFO_DIR); } /* newbiefile */ extern char const * prefs_get_charsave_newbie_amazon(void) { return prefs_conf.newbiefile_amazon; } static int conf_set_newbiefile_amazon(const char* valstr) { return conf_set_str(&prefs_conf.newbiefile_amazon, valstr, NULL); } static int conf_setdef_newbiefile_amazon(void) { return conf_set_str(&prefs_conf.newbiefile_amazon, NULL, D2CS_CHARSAVE_NEWBIE); } extern char const * prefs_get_charsave_newbie_sorceress(void) { return prefs_conf.newbiefile_sorceress; } static int conf_set_newbiefile_sorceress(const char* valstr) { return conf_set_str(&prefs_conf.newbiefile_sorceress, valstr, NULL); } static int conf_setdef_newbiefile_sorceress(void) { return conf_set_str(&prefs_conf.newbiefile_sorceress, NULL, D2CS_CHARSAVE_NEWBIE); } extern char const * prefs_get_charsave_newbie_necromancer(void) { return prefs_conf.newbiefile_necromancer; } static int conf_set_newbiefile_necromancer(const char* valstr) { return conf_set_str(&prefs_conf.newbiefile_necromancer, valstr, NULL); } static int conf_setdef_newbiefile_necromancer(void) { return conf_set_str(&prefs_conf.newbiefile_necromancer, NULL, D2CS_CHARSAVE_NEWBIE); } extern char const * prefs_get_charsave_newbie_paladin(void) { return prefs_conf.newbiefile_paladin; } static int conf_set_newbiefile_paladin(const char* valstr) { return conf_set_str(&prefs_conf.newbiefile_paladin, valstr, NULL); } static int conf_setdef_newbiefile_paladin(void) { return conf_set_str(&prefs_conf.newbiefile_paladin, NULL, D2CS_CHARSAVE_NEWBIE); } extern char const * prefs_get_charsave_newbie_barbarian(void) { return prefs_conf.newbiefile_barbarian; } static int conf_set_newbiefile_barbarian(const char* valstr) { return conf_set_str(&prefs_conf.newbiefile_barbarian, valstr, NULL); } static int conf_setdef_newbiefile_barbarian(void) { return conf_set_str(&prefs_conf.newbiefile_barbarian, NULL, D2CS_CHARSAVE_NEWBIE); } extern char const * prefs_get_charsave_newbie_druid(void) { return prefs_conf.newbiefile_druid; } static int conf_set_newbiefile_druid(const char* valstr) { return conf_set_str(&prefs_conf.newbiefile_druid, valstr, NULL); } static int conf_setdef_newbiefile_druid(void) { return conf_set_str(&prefs_conf.newbiefile_druid, NULL, D2CS_CHARSAVE_NEWBIE); } extern char const * prefs_get_charsave_newbie_assasin(void) { return prefs_conf.newbiefile_assasin; } static int conf_set_newbiefile_assasin(const char* valstr) { return conf_set_str(&prefs_conf.newbiefile_assasin, valstr, NULL); } static int conf_setdef_newbiefile_assasin(void) { return conf_set_str(&prefs_conf.newbiefile_assasin, NULL, D2CS_CHARSAVE_NEWBIE); } extern char const * prefs_get_motd(void) { return prefs_conf.motd; } static int conf_set_motd(const char* valstr) { return conf_set_str(&prefs_conf.motd,valstr,NULL); } static int conf_setdef_motd(void) { return conf_set_str(&prefs_conf.motd,NULL,D2CS_MOTD); } extern char const * prefs_get_d2gs_list(void) { return prefs_conf.gameservlist; } static int conf_set_gameservlist(const char* valstr) { return conf_set_str(&prefs_conf.gameservlist,valstr,NULL); } static int conf_setdef_gameservlist(void) { return conf_set_str(&prefs_conf.gameservlist,NULL,D2GS_SERVER_LIST); } extern unsigned int prefs_get_maxchar(void) { return (prefs_conf.maxchar>MAX_MAX_CHAR_PER_ACCT)?MAX_MAX_CHAR_PER_ACCT:prefs_conf.maxchar; } static int conf_set_maxchar(const char* valstr) { return conf_set_int(&prefs_conf.maxchar,valstr,0); } static int conf_setdef_maxchar(void) { return conf_set_int(&prefs_conf.maxchar,NULL,MAX_CHAR_PER_ACCT); } extern unsigned int prefs_get_list_purgeinterval(void) { return prefs_conf.listpurgeinterval; } static int conf_set_listpurgeinterval(const char* valstr) { return conf_set_int(&prefs_conf.listpurgeinterval,valstr,0); } static int conf_setdef_listpurgeinterval(void) { return conf_set_int(&prefs_conf.listpurgeinterval,NULL,LIST_PURGE_INTERVAL); } extern unsigned int prefs_get_gamequeue_checkinterval(void) { return prefs_conf.gqcheckinterval; } static int conf_set_gqcheckinterval(const char* valstr) { return conf_set_int(&prefs_conf.gqcheckinterval,valstr,0); } static int conf_setdef_gqcheckinterval(void) { return conf_set_int(&prefs_conf.gqcheckinterval,NULL,GAMEQUEUE_CHECK_INTERVAL); } extern unsigned int prefs_get_maxgamelist(void) { return prefs_conf.maxgamelist; } static int conf_set_maxgamelist(const char* valstr) { return conf_set_int(&prefs_conf.maxgamelist,valstr,0); } static int conf_setdef_maxgamelist(void) { return conf_set_int(&prefs_conf.maxgamelist,NULL,MAX_GAME_LIST); } extern unsigned int prefs_allow_newchar(void) { return prefs_conf.allow_newchar; } static int conf_set_allow_newchar(const char* valstr) { return conf_set_bool(&prefs_conf.allow_newchar,valstr,0); } static int conf_setdef_allow_newchar(void) { return conf_set_bool(&prefs_conf.allow_newchar,NULL,1); } extern unsigned int prefs_get_idletime(void) { return prefs_conf.idletime; } static int conf_set_idletime(const char* valstr) { return conf_set_int(&prefs_conf.idletime,valstr,0); } static int conf_setdef_idletime(void) { return conf_set_int(&prefs_conf.idletime,NULL,MAX_CLIENT_IDLETIME); } extern char const * d2cs_prefs_get_logfile(void) { return prefs_conf.logfile; } static int conf_set_logfile(const char* valstr) { return conf_set_str(&prefs_conf.logfile,valstr,NULL); } static int conf_setdef_logfile(void) { return conf_set_str(&prefs_conf.logfile,NULL,DEFAULT_LOG_FILE); } extern unsigned int d2cs_prefs_get_shutdown_delay(void) { return prefs_conf.shutdown_delay; } static int conf_set_shutdown_delay(const char* valstr) { return conf_set_int(&prefs_conf.shutdown_delay,valstr,0); } static int conf_setdef_shutdown_delay(void) { return conf_set_int(&prefs_conf.shutdown_delay,NULL,DEFAULT_SHUTDOWN_DELAY); } extern unsigned int d2cs_prefs_get_shutdown_decr(void) { return prefs_conf.shutdown_decr; } static int conf_set_shutdown_decr(const char* valstr) { return conf_set_int(&prefs_conf.shutdown_decr,valstr,0); } static int conf_setdef_shutdown_decr(void) { return conf_set_int(&prefs_conf.shutdown_decr,NULL,DEFAULT_SHUTDOWN_DECR); } extern char const * prefs_get_bnetdaddr(void) { return prefs_conf.bnetdaddr; } static int conf_set_bnetdaddr(const char* valstr) { return conf_set_str(&prefs_conf.bnetdaddr,valstr,NULL); } static int conf_setdef_bnetaddr(void) { return conf_set_str(&prefs_conf.bnetdaddr,NULL,BNETD_SERVER_LIST); } extern char const * prefs_get_realmname(void) { return prefs_conf.realmname; } static int conf_set_realmname(const char* valstr) { return conf_set_str(&prefs_conf.realmname,valstr,NULL); } static int conf_setdef_realmname(void) { return conf_set_str(&prefs_conf.realmname,NULL,DEFAULT_REALM_NAME); } extern unsigned int prefs_get_s2s_retryinterval(void) { return prefs_conf.s2s_retryinterval; } static int conf_set_s2s_retryinterval(const char* valstr) { return conf_set_int(&prefs_conf.s2s_retryinterval,valstr,0); } static int conf_setdef_s2s_retryinterval(void) { return conf_set_int(&prefs_conf.s2s_retryinterval,NULL,DEFAULT_S2S_RETRYINTERVAL); } extern unsigned int prefs_get_s2s_timeout(void) { return prefs_conf.s2s_timeout; } static int conf_set_s2s_timeout(const char* valstr) { return conf_set_int(&prefs_conf.s2s_timeout,valstr,0); } static int conf_setdef_s2s_timeout(void) { return conf_set_int(&prefs_conf.s2s_timeout,NULL,DEFAULT_S2S_TIMEOUT); } extern unsigned int prefs_get_sq_timeout(void) { return prefs_conf.sq_timeout; } static int conf_set_sq_timeout(const char* valstr) { return conf_set_int(&prefs_conf.sq_timeout,valstr,0); } static int conf_setdef_sq_timeout(void) { return conf_set_int(&prefs_conf.sq_timeout,NULL,DEFAULT_SQ_TIMEOUT); } extern unsigned int prefs_get_sq_checkinterval(void) { return prefs_conf.sq_checkinterval; } static int conf_set_sq_checkinterval(const char* valstr) { return conf_set_int(&prefs_conf.sq_checkinterval,valstr,0); } static int conf_setdef_sq_checkinterval(void) { return conf_set_int(&prefs_conf.sq_checkinterval,NULL,DEFAULT_SQ_CHECKINTERVAL); } extern unsigned int prefs_get_d2gs_checksum(void) { return prefs_conf.d2gs_checksum; } static int conf_set_d2gs_checksum(const char* valstr) { return conf_set_int(&prefs_conf.d2gs_checksum,valstr,0); } static int conf_setdef_d2gs_checksum(void) { return conf_set_int(&prefs_conf.d2gs_checksum,NULL,0); } extern unsigned int prefs_get_d2gs_version(void) { return prefs_conf.d2gs_version; } static int conf_set_d2gs_version(const char* valstr) { return conf_set_int(&prefs_conf.d2gs_version,valstr,0); } static int conf_setdef_d2gs_version(void) { return conf_set_int(&prefs_conf.d2gs_version,NULL,0); } extern unsigned int prefs_get_ladderlist_count(void) { return 0x10; } extern unsigned int prefs_get_d2ladder_refresh_interval(void) { return prefs_conf.ladder_refresh_interval; } static int conf_set_ladder_refresh_interval(const char* valstr) { return conf_set_int(&prefs_conf.ladder_refresh_interval,valstr,0); } static int conf_setdef_ladder_refresh_interval(void) { return conf_set_int(&prefs_conf.ladder_refresh_interval,NULL,3600); } extern unsigned int prefs_get_game_maxlifetime(void) { return prefs_conf.game_maxlifetime; } static int conf_set_game_maxlifetime(const char* valstr) { return conf_set_int(&prefs_conf.game_maxlifetime,valstr,0); } static int conf_setdef_game_maxlifetime(void) { return conf_set_int(&prefs_conf.game_maxlifetime,NULL,0); } extern unsigned int prefs_get_game_maxlevel(void) { return prefs_conf.game_maxlevel; } static int conf_set_game_maxlevel(const char* valstr) { return conf_set_int(&prefs_conf.game_maxlevel,valstr,0); } static int conf_setdef_game_maxlevel(void) { return conf_set_int(&prefs_conf.game_maxlevel,NULL,0xff); } extern char const * prefs_get_ladder_dir(void) { return prefs_conf.ladderdir; } static int conf_set_ladderdir(const char* valstr) { return conf_set_str(&prefs_conf.ladderdir,valstr,NULL); } static int conf_setdef_ladderdir(void) { return conf_set_str(&prefs_conf.ladderdir,NULL,D2CS_LADDER_DIR); } extern char const * d2cs_prefs_get_loglevels(void) { return prefs_conf.loglevels; } static int conf_set_loglevels(const char* valstr) { return conf_set_str(&prefs_conf.loglevels,valstr,NULL); } static int conf_setdef_loglevels(void) { return conf_set_str(&prefs_conf.loglevels,NULL,DEFAULT_LOG_LEVELS); } extern unsigned int prefs_allow_gamelist_showall(void) { return prefs_conf.gamelist_showall; } static int conf_set_gamelist_showall(const char* valstr) { return conf_set_bool(&prefs_conf.gamelist_showall,valstr,0); } static int conf_setdef_gamelist_showall(void) { return conf_set_bool(&prefs_conf.gamelist_showall,NULL,0); } extern unsigned int prefs_hide_pass_games(void) { return prefs_conf.hide_pass_games; } static int conf_set_hide_pass_games(const char* valstr) { return conf_set_bool(&prefs_conf.hide_pass_games,valstr,0); } static int conf_setdef_hide_pass_games(void) { return conf_set_bool(&prefs_conf.hide_pass_games,NULL,0); } extern unsigned int prefs_allow_gamelimit(void) { return prefs_conf.allow_gamelimit; } static int conf_set_allow_gamelimit(const char* valstr) { return conf_set_bool(&prefs_conf.allow_gamelimit,valstr,0); } static int conf_setdef_allow_gamelimit(void) { return conf_set_bool(&prefs_conf.allow_gamelimit,NULL,1); } extern unsigned int prefs_check_multilogin(void) { return prefs_conf.check_multilogin; } static int conf_set_check_multilogin(const char* valstr) { return conf_set_bool(&prefs_conf.check_multilogin,valstr,0); } static int conf_setdef_check_multilogin(void) { return conf_set_bool(&prefs_conf.check_multilogin,NULL,1); } extern char const * prefs_get_d2gs_password(void) { return prefs_conf.d2gs_password; } static int conf_set_d2gs_password(const char* valstr) { return conf_set_str(&prefs_conf.d2gs_password,valstr,NULL); } static int conf_setdef_d2gs_password(void) { return conf_set_str(&prefs_conf.d2gs_password,NULL,""); } extern unsigned int prefs_get_s2s_idletime(void) { return prefs_conf.s2s_idletime; } static int conf_set_s2s_idletime(const char* valstr) { return conf_set_int(&prefs_conf.s2s_idletime,valstr,0); } static int conf_setdef_s2s_idletime(void) { return conf_set_int(&prefs_conf.s2s_idletime,NULL,DEFAULT_S2S_IDLETIME); } extern unsigned int prefs_get_s2s_keepalive_interval(void) { return prefs_conf.s2s_keepalive_interval; } static int conf_set_s2s_keepalive_interval(const char* valstr) { return conf_set_int(&prefs_conf.s2s_keepalive_interval,valstr,0); } static int conf_setdef_s2s_keepalive_interval(void) { return conf_set_int(&prefs_conf.s2s_keepalive_interval,NULL,DEFAULT_S2S_KEEPALIVE_INTERVAL); } extern unsigned int prefs_get_timeout_checkinterval(void) { return prefs_conf.timeout_checkinterval; } static int conf_set_timeout_checkinterval(const char* valstr) { return conf_set_int(&prefs_conf.timeout_checkinterval,valstr,0); } static int conf_setdef_timeout_checkinterval(void) { return conf_set_int(&prefs_conf.timeout_checkinterval,NULL,DEFAULT_TIMEOUT_CHECKINTERVAL); } extern unsigned int prefs_get_max_game_idletime(void) { return prefs_conf.max_game_idletime; } static int conf_set_max_game_idletime(const char* valstr) { return conf_set_int(&prefs_conf.max_game_idletime,valstr,0); } static int conf_setdef_max_game_idletime(void) { return conf_set_int(&prefs_conf.max_game_idletime,NULL,MAX_GAME_IDLE_TIME); } extern unsigned int prefs_get_lod_realm(void) { return prefs_conf.lod_realm; } static int conf_set_lod_realm(const char* valstr) { return conf_set_int(&prefs_conf.lod_realm,valstr,0); } static int conf_setdef_lod_realm(void) { return conf_set_int(&prefs_conf.lod_realm,NULL,2); } extern unsigned int prefs_get_allow_convert(void) { return prefs_conf.allow_convert; } static int conf_set_allow_convert(const char* valstr) { return conf_set_bool(&prefs_conf.allow_convert,valstr,0); } static int conf_setdef_allow_convert(void) { return conf_set_bool(&prefs_conf.allow_convert,NULL,0); } extern char const * d2cs_prefs_get_transfile(void) { return prefs_conf.transfile; } static int conf_set_transfile(const char* valstr) { return conf_set_str(&prefs_conf.transfile,valstr,NULL); } static int conf_setdef_transfile(void) { return conf_set_str(&prefs_conf.transfile,NULL,D2CS_TRANS_FILE); } extern char const * prefs_get_d2cs_account_allowed_symbols(void) { return prefs_conf.account_allowed_symbols; } static int conf_set_account_allowed_symbols(const char* valstr) { return conf_set_str(&prefs_conf.account_allowed_symbols,valstr,NULL); } static int conf_setdef_account_allowed_symbols(void) { return conf_set_str(&prefs_conf.account_allowed_symbols,NULL,DEFAULT_ACC_ALLOWED_SYMBOLS); } extern std::time_t prefs_get_ladder_start_time() { return prefs_conf.ladder_start_time; } static int conf_set_ladder_start_time(const char* valstr) { return conf_set_timestr(&prefs_conf.ladder_start_time,valstr,0); } static int conf_setdef_ladder_start_time(void) { return conf_set_timestr(&prefs_conf.ladder_start_time,NULL,0); } extern unsigned int prefs_get_d2gs_restart_delay(void) { return prefs_conf.d2gs_restart_delay; } static int conf_set_d2gs_restart_delay(const char* valstr) { return conf_set_int(&prefs_conf.d2gs_restart_delay,valstr,0); } static int conf_setdef_d2gs_restart_delay(void) { return conf_set_int(&prefs_conf.d2gs_restart_delay,NULL,DEFAULT_D2GS_RESTART_DELAY); } extern unsigned int prefs_get_char_expire_time(void) { return prefs_conf.char_expire_day * 3600 * 24; } static int conf_set_char_expire_day(const char* valstr) { return conf_set_int(&prefs_conf.char_expire_day,valstr,0); } static int conf_setdef_char_expire_day(void) { return conf_set_int(&prefs_conf.char_expire_day,NULL,0); } extern char const * prefs_get_d2gsconffile(void) { return prefs_conf.d2gsconffile; } static int conf_set_d2gsconffile(const char* valstr) { return conf_set_str(&prefs_conf.d2gsconffile,valstr,NULL); } static int conf_setdef_d2gsconffile(void) { return conf_set_str(&prefs_conf.d2gsconffile,NULL,""); } extern char const * prefs_get_charlist_sort(void) { return prefs_conf.charlist_sort; } static int conf_set_charlist_sort(const char* valstr) { return conf_set_str(&prefs_conf.charlist_sort,valstr,NULL); } static int conf_setdef_charlist_sort(void) { return conf_set_str(&prefs_conf.charlist_sort,NULL,"none"); } extern char const * prefs_get_charlist_sort_order(void) { return prefs_conf.charlist_sort_order; } static int conf_set_charlist_sort_order(const char* valstr) { return conf_set_str(&prefs_conf.charlist_sort_order,valstr,NULL); } static int conf_setdef_charlist_sort_order(void) { return conf_set_str(&prefs_conf.charlist_sort_order,NULL,"ASC"); } extern unsigned int prefs_get_max_connections(void) { return prefs_conf.max_connections; } static int conf_set_max_connections(const char* valstr) { return conf_set_int(&prefs_conf.max_connections,valstr,0); } static int conf_setdef_max_connections(void) { return conf_set_int(&prefs_conf.max_connections,NULL,BNETD_MAX_SOCKETS); } extern char const * prefs_get_pidfile(void) { return prefs_conf.pidfile; } static int conf_set_pidfile(const char* valstr) { return conf_set_str(&prefs_conf.pidfile,valstr,NULL); } static int conf_setdef_pidfile(void) { return conf_set_str(&prefs_conf.pidfile,NULL,""); } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/prefs.h000066400000000000000000000102461462640461400171150ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2005 Dizzy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_PREFS_H #define INCLUDED_PREFS_H #include namespace pvpgn { namespace d2cs { extern int d2cs_prefs_load(char const * filename); extern int prefs_reload(char const * filename); extern int d2cs_prefs_unload(void); extern char const * d2cs_prefs_get_transfile(void); extern char const * d2cs_prefs_get_logfile(void); extern char const * prefs_get_servaddrs(void); extern char const * prefs_get_charsave_dir(void); extern char const * prefs_get_charinfo_dir(void); extern char const * prefs_get_bak_charsave_dir(void); extern char const * prefs_get_bak_charinfo_dir(void); extern char const * prefs_get_charsave_newbie_amazon(void); extern char const * prefs_get_charsave_newbie_sorceress(void); extern char const * prefs_get_charsave_newbie_necromancer(void); extern char const * prefs_get_charsave_newbie_paladin(void); extern char const * prefs_get_charsave_newbie_barbarian(void); extern char const * prefs_get_charsave_newbie_druid(void); extern char const * prefs_get_charsave_newbie_assasin(void); extern char const * prefs_get_motd(void); extern char const * prefs_get_realmname(void); extern char const * prefs_get_d2gs_list(void); extern unsigned int prefs_get_maxchar(void); extern unsigned int prefs_get_list_purgeinterval(void); extern unsigned int prefs_get_maxgamelist(void); extern unsigned int prefs_allow_newchar(void); extern unsigned int prefs_get_gamequeue_checkinterval(void); extern unsigned int prefs_get_idletime(void); extern unsigned int d2cs_prefs_get_shutdown_delay(void); extern unsigned int d2cs_prefs_get_shutdown_decr(void); extern char const * prefs_get_bnetdaddr(void); extern unsigned int prefs_get_s2s_retryinterval(void); extern unsigned int prefs_get_s2s_timeout(void); extern unsigned int prefs_get_sq_timeout(void); extern unsigned int prefs_get_sq_checkinterval(void); extern unsigned int prefs_get_d2gs_checksum(void); extern unsigned int prefs_get_d2gs_version(void); extern unsigned int prefs_get_ladderlist_count(void); extern unsigned int prefs_get_d2ladder_refresh_interval(void); extern unsigned int prefs_get_game_maxlifetime(void); extern unsigned int prefs_get_game_maxlevel(void); extern char const * prefs_get_ladder_dir(void); extern char const * d2cs_prefs_get_loglevels(void); extern unsigned int prefs_allow_gamelist_showall(void); extern unsigned int prefs_hide_pass_games(void); extern unsigned int prefs_allow_gamelimit(void); extern unsigned int prefs_check_multilogin(void); extern char const * prefs_get_d2gs_password(void); extern unsigned int prefs_get_s2s_idletime(void); extern unsigned int prefs_get_s2s_keepalive_interval(void); extern unsigned int prefs_get_timeout_checkinterval(void); extern unsigned int prefs_get_max_game_idletime(void); extern unsigned int prefs_get_lod_realm(void); extern unsigned int prefs_get_allow_convert(void); extern char const * prefs_get_d2cs_account_allowed_symbols(void); extern unsigned int prefs_get_d2gs_restart_delay(void); extern std::time_t prefs_get_ladder_start_time(); extern unsigned int prefs_get_char_expire_time(void); extern char const * prefs_get_d2gsconffile(void); extern char const * prefs_get_charlist_sort(void); extern char const * prefs_get_charlist_sort_order(void); extern unsigned int prefs_get_max_connections(void); extern char const * prefs_get_pidfile(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/s2s.cpp000066400000000000000000000102751462640461400170420ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "s2s.h" #include #include #include "compat/psock.h" #include "compat/strerror.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "bnetd.h" #include "net.h" #include "server.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { extern int s2s_check(void) { bnetd_check(); return 0; } extern int s2s_init(void) { bnetd_init(); return 0; } extern t_connection * s2s_create(char const * server, unsigned short def_port, t_conn_class cclass) { struct sockaddr_in addr, laddr; psock_t_socklen laddr_len; unsigned int ip; unsigned short port; int sock, connected; t_connection * c; char * p, * tserver; ASSERT(server,NULL); tserver=xstrdup(server); p=std::strchr(tserver,':'); if (p) { port=(unsigned short)std::strtoul(p+1,NULL,10); *p='\0'; } else { port=def_port; } if ((sock=net_socket(PSOCK_SOCK_STREAM))<0) { eventlog(eventlog_level_error,__FUNCTION__,"error creating s2s socket"); xfree(tserver); return NULL; } std::memset(&addr,0,sizeof(addr)); addr.sin_family = PSOCK_AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr= net_inet_addr(tserver); xfree(tserver); eventlog(eventlog_level_info,__FUNCTION__,"try make s2s connection to {}",server); if (psock_connect(sock,(struct sockaddr *)&addr,sizeof(addr))<0) { if (psock_errno()!=PSOCK_EWOULDBLOCK && psock_errno() != PSOCK_EINPROGRESS) { eventlog(eventlog_level_error,__FUNCTION__,"error connecting to {} (psock_connect: {})",server,pstrerror(psock_errno())); psock_close(sock); return NULL; } connected=0; eventlog(eventlog_level_info,__FUNCTION__,"connection to s2s server {} is in progress",server); } else { connected=1; eventlog(eventlog_level_info,__FUNCTION__,"connected to s2s server {}",server); } laddr_len=sizeof(laddr); std::memset(&laddr,0,sizeof(laddr)); ip=port=0; if (psock_getsockname(sock,(struct sockaddr *)&laddr,&laddr_len)<0) { eventlog(eventlog_level_error,__FUNCTION__,"unable to get local socket info"); } else { if (laddr.sin_family != PSOCK_AF_INET) { eventlog(eventlog_level_error,__FUNCTION__,"got bad socket family {}",laddr.sin_family); } else { ip=ntohl(laddr.sin_addr.s_addr); port=ntohs(laddr.sin_port); } } if (!(c=d2cs_conn_create(sock,ip,port, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port)))) { eventlog(eventlog_level_error,__FUNCTION__,"error create s2s connection"); psock_close(sock); return NULL; } if (connected) { if (conn_add_fd(c,fdwatch_type_read, d2cs_server_handle_tcp)<0) { eventlog(eventlog_level_error, __FUNCTION__, "error adding socket {} to fdwatch pool (max sockets?)",sock); d2cs_conn_set_state(c,conn_state_destroy); return NULL; } d2cs_conn_set_state(c,conn_state_init); } else { if (conn_add_fd(c, fdwatch_type_write, d2cs_server_handle_tcp)<0) { eventlog(eventlog_level_error, __FUNCTION__, "error adding socket {} to fdwatch pool (max sockets?)",sock); d2cs_conn_set_state(c,conn_state_destroy); return NULL; } d2cs_conn_set_state(c,conn_state_connecting); } d2cs_conn_set_class(c,cclass); return c; } extern int s2s_destroy(t_connection * c) { ASSERT(c,-1); switch (d2cs_conn_get_class(c)) { case conn_class_bnetd: bnetd_destroy(c); break; default: eventlog(eventlog_level_error,__FUNCTION__,"got bad s2s connection class {}",d2cs_conn_get_class(c)); return -1; } return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/s2s.h000066400000000000000000000021261462640461400165030ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_S2S_H #define INCLUDED_S2S_H #include "connection.h" namespace pvpgn { namespace d2cs { extern int s2s_init(void); extern int s2s_check(void); extern t_connection * s2s_create(char const * server, unsigned short def_port, t_conn_class cclass); extern int s2s_destroy(t_connection * s2s); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/server.cpp000066400000000000000000000206671462640461400176470ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "server.h" #include #include #ifdef WIN32 # include #endif #include "compat/psock.h" #include "compat/strerror.h" #include "common/addr.h" #include "common/eventlog.h" #include "common/list.h" #include "prefs.h" #include "net.h" #include "connection.h" #include "serverqueue.h" #include "gamequeue.h" #include "d2gs.h" #include "s2s.h" #include "handle_signal.h" #include "game.h" #include "d2ladder.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" #ifdef WIN32 extern int g_ServiceStatus; #endif namespace pvpgn { namespace d2cs { static int server_listen(void); static int server_accept(int sock); static int server_handle_timed_event(void); static int server_handle_socket(void); static int server_loop(void); static int server_cleanup(void); t_addrlist * server_listen_addrs; static int server_listen(void) { t_addr * curr_laddr; t_addr_data laddr_data; int sock; if (!(server_listen_addrs=addrlist_create(prefs_get_servaddrs(),INADDR_ANY,D2CS_SERVER_PORT))) { eventlog(eventlog_level_error,__FUNCTION__,"error create listening address list"); return -1; } BEGIN_LIST_TRAVERSE_DATA(server_listen_addrs,curr_laddr,t_addr) { sock=net_listen(addr_get_ip(curr_laddr),addr_get_port(curr_laddr),PSOCK_SOCK_STREAM); if (sock<0) { eventlog(eventlog_level_error,__FUNCTION__,"error listen socket"); return -1; } if (psock_ctl(sock,PSOCK_NONBLOCK)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error set listen socket in non-blocking mode"); } laddr_data.i = sock; addr_set_data(curr_laddr,laddr_data); if (fdwatch_add_fd(sock, fdwatch_type_read, d2cs_server_handle_accept, curr_laddr)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error adding socket {} to fdwatch pool (max sockets?)",sock); psock_close(sock); return -1; } eventlog(eventlog_level_info,__FUNCTION__,"listen on {}", addr_num_to_addr_str(addr_get_ip(curr_laddr),addr_get_port(curr_laddr))); } END_LIST_TRAVERSE_DATA() return 0; } static int server_accept(int sock) { int csock; struct sockaddr_in caddr, raddr; psock_t_socklen caddr_len, raddr_len; int val; unsigned int ip; unsigned short port; t_connection *cc; caddr_len=sizeof(caddr); std::memset(&caddr,0,sizeof(caddr)); csock=psock_accept(sock,(struct sockaddr *)&caddr,&caddr_len); if (csock<0) { eventlog(eventlog_level_error,__FUNCTION__,"error accept new connection"); return -1; } else { char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(caddr.sin_addr), addrstr, sizeof(addrstr)); eventlog(eventlog_level_info, __FUNCTION__, "accept connection from {}", addrstr); } val=1; if (psock_setsockopt(csock, PSOCK_SOL_SOCKET, PSOCK_SO_KEEPALIVE, &val,sizeof(val))<0) { eventlog(eventlog_level_warn,__FUNCTION__,"error set sock option keep alive"); } if (psock_ctl(csock, PSOCK_NONBLOCK)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error set socket to non-blocking mode"); psock_close(csock); return -1; } raddr_len=sizeof(raddr); std::memset(&raddr,0,sizeof(raddr)); ip=port=0; if (psock_getsockname(csock,(struct sockaddr *)&raddr,&raddr_len)<0) { eventlog(eventlog_level_warn,__FUNCTION__,"unable to get local socket info"); } else { if (raddr.sin_family!=PSOCK_AF_INET) { eventlog(eventlog_level_warn,__FUNCTION__,"got bad socket family {}",raddr.sin_family); } else { ip=ntohl(raddr.sin_addr.s_addr); port=ntohs(raddr.sin_port); } } if (!(cc = d2cs_conn_create(csock,ip,port,ntohl(caddr.sin_addr.s_addr),ntohs(caddr.sin_port)))) { eventlog(eventlog_level_error,__FUNCTION__,"error create new connection"); psock_close(csock); return -1; } if (conn_add_fd(cc, fdwatch_type_read, d2cs_server_handle_tcp)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error adding socket {} to fdwatch pool (max sockets?)",csock); d2cs_conn_set_state(cc,conn_state_destroy); return -1; } return 0; } static int server_handle_timed_event(void) { static std::time_t prev_list_purgetime=0; static std::time_t prev_gamequeue_checktime=0; static std::time_t prev_s2s_checktime=0; static std::time_t prev_sq_checktime=0; static std::time_t prev_d2ladder_refresh_time=0; static std::time_t prev_s2s_keepalive_time=0; static std::time_t prev_timeout_checktime; std::time_t now; now=std::time(NULL); if (now-prev_list_purgetime>(signed)prefs_get_list_purgeinterval()) { hashtable_purge(d2cs_connlist()); d2cs_gamelist_check_voidgame(); prev_list_purgetime=now; } if (now-prev_gamequeue_checktime>(signed)prefs_get_gamequeue_checkinterval()) { gqlist_update_all_clients(); prev_gamequeue_checktime=now; } if (now-prev_s2s_checktime>(signed)prefs_get_s2s_retryinterval()) { s2s_check(); prev_s2s_checktime=now; } if (now-prev_sq_checktime>(signed)prefs_get_sq_checkinterval()) { sqlist_check_timeout(); prev_sq_checktime=now; } if (now-prev_d2ladder_refresh_time>(signed)prefs_get_d2ladder_refresh_interval()) { d2ladder_refresh(); prev_d2ladder_refresh_time=now; } if (now-prev_s2s_keepalive_time>(signed)prefs_get_s2s_keepalive_interval()) { d2gs_keepalive(); prev_s2s_keepalive_time=now; } if (now-prev_timeout_checktime>(signed)prefs_get_timeout_checkinterval()) { connlist_check_timeout(); prev_timeout_checktime=now; } return 0; } extern int d2cs_server_handle_accept(void *data, t_fdwatch_type rw) { int sock; sock = addr_get_data((t_addr *)data).i; server_accept(sock); return 0; } extern int d2cs_server_handle_tcp(void *data, t_fdwatch_type rw) { t_connection *c = (t_connection *)data; if (rw & fdwatch_type_read) conn_add_socket_flag(c,SOCKET_FLAG_READ); if (rw & fdwatch_type_write) conn_add_socket_flag(c,SOCKET_FLAG_WRITE); if (conn_handle_socket(c)<0) d2cs_conn_set_state(c, conn_state_destroy); return 0; } static int server_handle_socket(void) { switch (fdwatch(BNETD_POLL_INTERVAL)) { case -1: if ( #ifdef PSOCK_EINTR psock_errno()!=PSOCK_EINTR && #endif 1) { eventlog(eventlog_level_error,__FUNCTION__,"select failed (select: {})",pstrerror(psock_errno())); return -1; } /* fall through */ case 0: return 0; default: break; } fdwatch_handle(); return 0; } static int server_loop(void) { unsigned int count; count=0; while (1) { #ifdef WIN32 if (g_ServiceStatus<0 && kbhit() && getch()=='q') signal_quit_wrapper(); if (g_ServiceStatus == 0) signal_quit_wrapper(); while (g_ServiceStatus == 2) Sleep(1000); #endif if (handle_signal()<0) break; if (++count>=(1000/BNETD_POLL_INTERVAL)) { server_handle_timed_event(); count=0; } server_handle_socket(); d2cs_connlist_reap(); } return 0; } static int server_cleanup(void) { t_addr * curr_laddr; int sock; BEGIN_LIST_TRAVERSE_DATA(server_listen_addrs,curr_laddr,t_addr) { sock=addr_get_data(curr_laddr).i; psock_close(sock); } END_LIST_TRAVERSE_DATA() addrlist_destroy(server_listen_addrs); return 0; } extern int d2cs_server_process(void) { #ifndef WIN32 handle_signal_init(); #endif if (psock_init()<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to init network"); return -1; } eventlog(eventlog_level_info,__FUNCTION__,"network initialized"); if (s2s_init()<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to init s2s connection"); return -1; } if (server_listen()<0) { eventlog(eventlog_level_error,__FUNCTION__,"failed to setup listen socket"); return -1; } eventlog(eventlog_level_info,__FUNCTION__,"entering server loop"); server_loop(); eventlog(eventlog_level_info,__FUNCTION__,"std::exit from server loop,cleanup"); server_cleanup(); return 0; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/server.h000066400000000000000000000020631462640461400173020ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SERVER_H #define INCLUDED_SERVER_H #include "common/fdwatch.h" namespace pvpgn { namespace d2cs { extern int d2cs_server_process(void); extern int d2cs_server_handle_tcp(void*, t_fdwatch_type); extern int d2cs_server_handle_accept(void*, t_fdwatch_type); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/serverqueue.cpp000066400000000000000000000065531462640461400207120ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "serverqueue.h" #include #include "common/eventlog.h" #include "common/xalloc.h" /* #include "common/packet.h" #include "common/list.h" */ #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2cs { static t_list * sqlist_head=NULL; static unsigned int sqlist_seqno=0; extern t_list * sqlist(void) { return sqlist_head; } extern int sqlist_create(void) { sqlist_head=list_create(); return 0; } extern int sqlist_destroy(void) { t_sq * sq; BEGIN_LIST_TRAVERSE_DATA_CONST(sqlist_head,sq,t_sq) { sq_destroy(sq,(t_elem **)curr_elem_); } END_LIST_TRAVERSE_DATA_CONST() if (list_destroy(sqlist_head)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error destroy server queue list"); return -1; } sqlist_head=NULL; return 0; } extern int sqlist_check_timeout(void) { t_sq * sq; std::time_t now; now=std::time(NULL); BEGIN_LIST_TRAVERSE_DATA(sqlist_head, sq, t_sq) { if (now - sq->ctime > prefs_get_sq_timeout()) { eventlog(eventlog_level_info,__FUNCTION__,"destroying expired server queue {}",sq->seqno); sq_destroy(sq,&curr_elem_); } } END_LIST_TRAVERSE_DATA() return 0; } extern t_sq * sqlist_find_sq(unsigned int seqno) { t_sq * sq; BEGIN_LIST_TRAVERSE_DATA_CONST(sqlist_head,sq,t_sq) { if (sq->seqno==seqno) return sq; } END_LIST_TRAVERSE_DATA_CONST() return NULL; } extern t_sq * sq_create(unsigned int clientid, t_packet * packet,unsigned int gameid ) { t_sq * sq; sq=(t_sq*)xmalloc(sizeof(t_sq)); sq->seqno=++sqlist_seqno; sq->ctime=std::time(NULL); sq->clientid=clientid; sq->gameid=gameid; sq->packet=packet; sq->gametoken=0; if (packet) packet_add_ref(packet); list_append_data(sqlist_head,sq); return sq; } extern int sq_destroy(t_sq * sq,t_elem ** curr) { ASSERT(sq,-1); if (list_remove_data(sqlist_head,sq,curr)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error remove server queue from list"); return -1; } if (sq->packet) packet_del_ref(sq->packet); xfree(sq); return 0; } extern unsigned int sq_get_clientid(t_sq const * sq) { ASSERT(sq,0); return sq->clientid; } extern t_packet * sq_get_packet(t_sq const * sq) { ASSERT(sq,NULL); return sq->packet; } extern unsigned int sq_get_gameid(t_sq const * sq) { ASSERT(sq,0); return sq->gameid; } extern unsigned int sq_get_seqno(t_sq const * sq) { ASSERT(sq,0); return sq->seqno; } extern int sq_set_gametoken(t_sq * sq, unsigned int gametoken) { ASSERT(sq,-1); sq->gametoken=gametoken; return 0; } extern unsigned int sq_get_gametoken(t_sq const * sq) { ASSERT(sq,0); return sq->gametoken; } } } pvpgn-1.99.7.2.1+dfsg/src/d2cs/serverqueue.h000066400000000000000000000034311462640461400203470ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_SERVERQUEUE_H #define INCLUDED_SERVERQUEUE_H #include "common/list.h" #include "common/packet.h" namespace pvpgn { namespace d2cs { typedef struct serverqueue { unsigned int seqno; unsigned int ctime; unsigned int clientid; t_packet * packet; unsigned int gameid; unsigned int gametoken; } t_sq; extern t_list * sqlist(void); extern int sqlist_create(void); extern int sqlist_destroy(void); extern int sq_destroy(t_sq * sq, t_elem ** curr); extern int sqlist_check_timeout(void); extern t_sq * sqlist_find_sq(unsigned int seqno); extern t_sq * sq_create(unsigned int clientid, t_packet * packet, unsigned int gameid); extern unsigned int sq_get_clientid(t_sq const * sq); extern unsigned int sq_get_gameid(t_sq const * sq); extern unsigned int sq_get_seqno(t_sq const * sq); extern int sq_set_gametoken(t_sq * sq, unsigned int gametoken); extern unsigned int sq_get_gametoken(t_sq const * sq); extern t_packet * sq_get_packet(t_sq const * sq); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/setup.h000066400000000000000000000101641462640461400171350ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2CS_SETUP_H #define INCLUDED_D2CS_SETUP_H #define tf(a) ((a)?1:0) #define strcmp_charname strcasecmp #define strncmp_charname strncasencmp #define BEGIN_LIST_TRAVERSE_DATA(list,data,type) \ {\ t_elem * curr_elem_; \ for (curr_elem_ = list_get_first(list); curr_elem_ && (data = (type*)elem_get_data(curr_elem_)); \ curr_elem_ = elem_get_next(list, curr_elem_)) #define END_LIST_TRAVERSE_DATA() \ } #define BEGIN_LIST_TRAVERSE_DATA_CONST(list,data,type)\ {\ t_elem const * curr_elem_; \ for (curr_elem_ = list_get_first_const(list); curr_elem_ && (data = (type*)elem_get_data(curr_elem_)); \ curr_elem_ = elem_get_next_const(list, curr_elem_)) #define END_LIST_TRAVERSE_DATA_CONST() \ } #define BEGIN_HASHTABLE_TRAVERSE_DATA(hashtable,data,type)\ {\ t_entry * curr_entry_; \ for (curr_entry_ = hashtable_get_first(hashtable); curr_entry_ && (data = (type*)entry_get_data(curr_entry_)); \ curr_entry_ = entry_get_next(curr_entry_)) #define END_HASHTABLE_TRAVERSE_DATA() \ } #define BEGIN_HASHTABLE_TRAVERSE_MATCHING_DATA(hashtable,data,hash,type)\ {\ t_entry * curr_entry_; \ for (curr_entry_ = hashtable_get_first_matching(hashtable, hash); \ curr_entry_ && (data = (type*)entry_get_data(curr_entry_)); \ curr_entry_ = entry_get_next_matching(curr_entry_)) #define END_HASHTABLE_TRAVERSE_DATA() \ } #define CASE(condition,func) case condition:\ func; \ break; \ #define ASSERT(var,retval) if (!var) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL " #var); return retval; } #define DECLARE_PACKET_HANDLER(handler) static int handler(t_connection *, t_packet *); #define NELEMS(s) sizeof(s)/sizeof(s[0]) #define MAX_SAVEFILE_SIZE 32 * 1024 #define MAX_CHAR_PER_GAME 8 #define D2CS_SERVER_PORT 6113 #define MAX_GAME_IDLE_TIME 0 #define DEFAULT_S2S_RETRYINTERVAL 60 #define DEFAULT_S2S_TIMEOUT 60 #define DEFAULT_SQ_CHECKINTERVAL 300 #define DEFAULT_SQ_TIMEOUT 300 #define DEFAULT_REALM_NAME "D2CS" #define DEFAULT_SHUTDOWN_DELAY 300 #define DEFAULT_SHUTDOWN_DECR 60 #define DEFAULT_S2S_IDLETIME 300 #define DEFAULT_S2S_KEEPALIVE_INTERVAL 60 #define DEFAULT_TIMEOUT_CHECKINTERVAL 60 #define DEFAULT_ACC_ALLOWED_SYMBOLS "-_[]" #define DEFAULT_D2GS_RESTART_DELAY 300 #define MAJOR_VERSION_EQUAL(v1,v2,mask) (((v1) & (mask)) == ((v2) & (mask))) #ifndef D2CS_DEFAULT_CONF_FILE # define D2CS_DEFAULT_CONF_FILE "conf/d2cs.conf" #endif #define DEFAULT_LOG_FILE "/usr/local/var/d2cs.std::log" #define DEFAULT_LOG_LEVELS "info,warn,error" #define DEFAULT_MEMLOG_FILE "/tmp/d2cs-mem.std::log" #define D2CS_SERVER_ADDRS "0.0.0.0" #define D2GS_SERVER_LIST "192.168.0.1" #define BNETD_SERVER_LIST "192.168.0.1" #define MAX_D2GAME_NUMBER 30 #define MAX_CHAR_PER_ACCT 8 // MAX_MAX_CHAR_PER_ACCT is needed cause D2 client can at worst case only handle 18 chars #define MAX_MAX_CHAR_PER_ACCT 18 #define MAX_CLIENT_IDLETIME 30 * 60 #define D2CS_CHARINFO_DIR "/usr/local/var/charinfo" #define D2CS_CHARSAVE_DIR "/usr/local/var/charsave" #define D2CS_BAK_CHARINFO_DIR "/usr/local/var/bak/charinfo" #define D2CS_BAK_CHARSAVE_DIR "/usr/local/var/bak/charsave" #define D2CS_CHARSAVE_NEWBIE "/usr/local/var/files/newbie.save" #define D2CS_TRANS_FILE "/usr/local/etc/address_translation.conf" #define D2CS_LADDER_DIR "/usr/local/var/ladders" #define D2CS_MOTD "No MOTD yet" #define LIST_PURGE_INTERVAL 300 #define GAMEQUEUE_CHECK_INTERVAL 60 #define MAX_GAME_LIST 20 #endif pvpgn-1.99.7.2.1+dfsg/src/d2cs/version.h000066400000000000000000000017521462640461400174650ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_VERSION_H #define INCLUDED_VERSION_H #define D2CS_VERSION_NUMBER 0x010A0101 #define D2CS_VERSION_STRING "1.10.1.1" #define D2CS_VERSION "D2CS Version " D2CS_VERSION_STRING " Built " __DATE__ #endif pvpgn-1.99.7.2.1+dfsg/src/d2dbs/000077500000000000000000000000001462640461400157655ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/d2dbs/CMakeLists.txt000066400000000000000000000011371462640461400205270ustar00rootroot00000000000000set(D2DBS_SOURCES charlock.cpp charlock.h cmdline.cpp cmdline.h d2ladder.cpp d2ladder.h dbsdupecheck.cpp dbsdupecheck.h dbserver.cpp dbserver.h dbspacket.cpp dbspacket.h handle_signal.cpp handle_signal.h main.cpp prefs.cpp prefs.h setup.h version.h ../win32/d2dbs_winmain.cpp ../win32/d2dbs_resource.h ../win32/d2dbs_resource.rc) if(WITH_WIN32_GUI) add_executable(d2dbs WIN32 ${D2DBS_SOURCES}) else(WITH_WIN32_GUI) add_executable(d2dbs ${D2DBS_SOURCES}) endif(WITH_WIN32_GUI) target_link_libraries(d2dbs common compat win32 ${NETWORK_LIBRARIES}) install(TARGETS d2dbs DESTINATION ${SBINDIR}) pvpgn-1.99.7.2.1+dfsg/src/d2dbs/charlock.cpp000066400000000000000000000142621462640461400202640ustar00rootroot00000000000000/* * Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "charlock.h" #include #include #include #include "compat/strcasecmp.h" #include "common/xalloc.h" #include "common/introtate.h" #include "common/xstring.h" #include "common/setup_after.h" namespace pvpgn { namespace d2dbs { /* FIXME: for simplification, no multiple realm support now */ /* local functions */ static int cl_insert_to_gsq_list(unsigned int gsid, t_charlockinfo *pcl); static int cl_delete_from_gsq_list(t_charlockinfo *pcl); static unsigned int string_hash(char const *string); /* variables */ static unsigned int clitbl_len = 0; static unsigned int gsqtbl_len = 0; static t_charlockinfo * * clitbl = NULL; static t_charlockinfo * * gsqtbl = NULL; int cl_init(unsigned int tbllen, unsigned int maxgs) { if (!tbllen || !maxgs) return -1; cl_destroy(); clitbl = (t_charlockinfo**)xmalloc(tbllen*sizeof(t_charlockinfo**)); gsqtbl = (t_charlockinfo**)xmalloc(maxgs*sizeof(t_charlockinfo**)); std::memset(clitbl, 0, tbllen*sizeof(t_charlockinfo**)); std::memset(gsqtbl, 0, maxgs*sizeof(t_charlockinfo**)); clitbl_len = tbllen; gsqtbl_len = maxgs; return 0; } int cl_destroy(void) { unsigned int i; t_charlockinfo * ptl, *ptmp; if (clitbl) { for (i = 0; i < clitbl_len; i++) { ptl = clitbl[i]; while (ptl) { ptmp = ptl; ptl = ptl->next; xfree(ptmp); } } xfree(clitbl); } if (gsqtbl) xfree(gsqtbl); clitbl = gsqtbl = NULL; clitbl_len = gsqtbl_len = 0; return 0; } int cl_query_charlock_status(unsigned char *charname, unsigned char *realmname, unsigned int *gsid) { t_charlockinfo *pcl; unsigned int hashval; if (!charname || !realmname) return -1; if (!clitbl_len || !gsqtbl) return -1; if (std::strlen((char*)charname) >= MAX_CHARNAME_LEN) return -1; hashval = string_hash((char*)charname) % clitbl_len; pcl = clitbl[hashval]; while (pcl) { if (strcasecmp((char*)pcl->charname, (char*)charname) == 0) { *gsid = pcl->gsid; return 1; /* found the char, it is locked */ } pcl = pcl->next; } return 0; /* not found, it is unlocked */ } int cl_lock_char(unsigned char *charname, unsigned char *realmname, unsigned int gsid) { t_charlockinfo *pcl, *ptmp; unsigned int hashval; if (!charname || !realmname) return -1; if (!clitbl_len || !gsqtbl) return -1; if (std::strlen((char*)charname) >= MAX_CHARNAME_LEN) return -1; hashval = string_hash((char*)charname) % clitbl_len; pcl = clitbl[hashval]; ptmp = NULL; while (pcl) { if (strcasecmp((char*)pcl->charname, (char*)charname) == 0) return 0; /* found the char is already locked */ ptmp = pcl; pcl = pcl->next; } /* not found, locked it */ pcl = (t_charlockinfo*)xmalloc(sizeof(t_charlockinfo)); std::memset(pcl, 0, sizeof(t_charlockinfo)); std::strncpy((char*)pcl->charname, (char*)charname, MAX_CHARNAME_LEN - 1); std::strncpy((char*)pcl->realmname, (char*)realmname, MAX_REALMNAME_LEN - 1); pcl->gsid = gsid; /* add to hash table link list */ if (ptmp) ptmp->next = pcl; else clitbl[hashval] = pcl; /* add to gs specified link list */ cl_insert_to_gsq_list(gsid, pcl); return 0; } int cl_unlock_char(unsigned char *charname, unsigned char *realmname, unsigned int gsid) { t_charlockinfo *pcl, *ptmp; unsigned int hashval; if (!charname || !realmname) return -1; if (!clitbl_len || !gsqtbl) return 0; if (std::strlen((char*)charname) >= MAX_CHARNAME_LEN) return -1; hashval = string_hash((char*)charname) % clitbl_len; pcl = clitbl[hashval]; ptmp = NULL; while (pcl) { if ((strcasecmp((char*)pcl->charname, (char*)charname) == 0) && (pcl->gsid == gsid)) { cl_delete_from_gsq_list(pcl); if (ptmp) ptmp->next = pcl->next; else clitbl[hashval] = pcl->next; xfree(pcl); return 0; } ptmp = pcl; pcl = pcl->next; } return 0; } int cl_unlock_all_char_by_gsid(unsigned int gsid) { unsigned int index_pos; t_charlockinfo *pcl, *pnext; index_pos = gsid % gsqtbl_len; pcl = gsqtbl[index_pos]; while (pcl) { pnext = pcl->gsqnext; cl_unlock_char(pcl->charname, pcl->realmname, gsid); pcl = pnext; } return 0; } static int cl_insert_to_gsq_list(unsigned int gsid, t_charlockinfo *pcl) { unsigned int index_pos; t_charlockinfo *ptmp; if (!pcl) return -1; index_pos = gsid % gsqtbl_len; ptmp = gsqtbl[index_pos]; gsqtbl[index_pos] = pcl; if (ptmp) { pcl->gsqnext = ptmp; ptmp->gsqprev = pcl; } return 0; } static int cl_delete_from_gsq_list(t_charlockinfo *pcl) { unsigned int index_pos; t_charlockinfo *pprev, *pnext; if (!pcl) return -1; index_pos = (pcl->gsid) % gsqtbl_len; pprev = pcl->gsqprev; pnext = pcl->gsqnext; if (pprev) pprev->gsqnext = pnext; else gsqtbl[index_pos] = pnext; if (pnext) pnext->gsqprev = pprev; return 0; } static unsigned int string_hash(char const *string) { unsigned int i; unsigned int pos; unsigned int hash; unsigned int ch; if (!string) return 0; for (hash = 0, pos = 0, i = 0; i < std::strlen(string); i++) { ch = safe_tolower(string[i]); hash ^= ROTL(ch, pos, (std::numeric_limits::digits)); pos += (std::numeric_limits::digits) - 1; } return hash; } } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/charlock.h000066400000000000000000000033121462640461400177230ustar00rootroot00000000000000/* * Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_CHARLOCK_H #define INCLUDED_CHARLOCK_H #define DEFAULT_HASHTBL_LEN 65000 #include "common/field_sizes.h" namespace pvpgn { namespace d2dbs { /* char locking info */ typedef struct raw_charlockinfo { unsigned char charname[MAX_CHARNAME_LEN]; unsigned char realmname[MAX_REALMNAME_LEN]; unsigned int gsid; struct raw_charlockinfo *next; struct raw_charlockinfo *gsqnext; struct raw_charlockinfo *gsqprev; } t_charlockinfo; /* functions */ int cl_init(unsigned int tbllen, unsigned int maxgs); int cl_destroy(void); int cl_query_charlock_status(unsigned char *charname, unsigned char *realmnamei, unsigned int *gsid); int cl_lock_char(unsigned char *charname, unsigned char *realmname, unsigned int gsid); int cl_unlock_char(unsigned char *charname, unsigned char *realmname, unsigned int gsid); int cl_unlock_all_char_by_gsid(unsigned int gsid); } } #endif /* INCLUDED_CHARLOCK_H */ pvpgn-1.99.7.2.1+dfsg/src/d2dbs/cmdline.cpp000066400000000000000000000204101462640461400201010ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * Copyright (C) 2005,2007 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "cmdline.h" #include #ifdef WIN32 # include "win32/service.h" #endif #include "compat/strcasecmp.h" #include "common/xalloc.h" #include "common/conf.h" #include "common/eventlog.h" #include "version.h" #include "common/setup_after.h" namespace pvpgn { namespace d2dbs { static struct { #ifdef DO_DAEMONIZE unsigned foreground; #endif const char *preffile; const char *logfile; unsigned debug; #ifdef WIN32_GUI unsigned console; unsigned gui; #endif } cmdline_config; static unsigned exitflag; static const char *progname; static int conf_set_preffile(const char *valstr); static int conf_setdef_preffile(void); static int conf_set_logfile(const char * valstr); static int conf_setdef_logfile(void); static int conf_set_help(const char * valstr); static int conf_setdef_help(void); static int conf_set_version(const char * valstr); static int conf_setdef_version(void); static int conf_set_foreground(const char * valstr); static int conf_setdef_foreground(void); static int conf_set_debug(const char * valstr); static int conf_setdef_debug(void); #ifdef WIN32 static int conf_set_service(char const * valstr); static int conf_setdef_service(void); static int conf_set_servaction(const char * valstr); static int conf_setdef_servaction(void); #endif #ifdef WIN32_GUI static int conf_set_console(char const * valstr); static int conf_setdef_console(void); static int conf_set_gui(char const * valstr); static int conf_setdef_gui(void); #endif static t_conf_entry conftab[] = { { "c", conf_set_preffile, NULL, conf_setdef_preffile }, { "config", conf_set_preffile, NULL, conf_setdef_preffile }, { "l", conf_set_logfile, NULL, conf_setdef_logfile }, { "log", conf_set_logfile, NULL, conf_setdef_logfile }, { "h", conf_set_help, NULL, conf_setdef_help }, { "help", conf_set_help, NULL, conf_setdef_help }, { "usage", conf_set_help, NULL, conf_setdef_help }, { "v", conf_set_version, NULL, conf_setdef_version }, { "version", conf_set_version, NULL, conf_setdef_version }, #ifdef DO_DAEMONIZE { "f", conf_set_foreground, NULL, conf_setdef_foreground }, { "foreground", conf_set_foreground, NULL, conf_setdef_foreground }, #endif { "D", conf_set_debug, NULL, conf_setdef_debug }, { "debug", conf_set_debug, NULL, conf_setdef_debug }, #ifdef WIN32 { "service", conf_set_service, NULL, conf_setdef_service }, { "s", conf_set_servaction, NULL, conf_setdef_servaction }, #endif #ifdef WIN32_GUI { "console", conf_set_console, NULL, conf_setdef_console }, { "gui", conf_set_gui, NULL, conf_setdef_gui }, #endif { NULL, NULL, NULL, NULL } }; extern int cmdline_load(int argc, char** argv) { int res; if (argc < 1 || !argv || !argv[0]) { std::fprintf(stderr, "bad arguments\n"); return -1; } exitflag = 0; progname = argv[0]; res = conf_load_cmdline(argc, argv, conftab); if (res < 0) return -1; return exitflag ? 0 : 1; } extern void cmdline_unload(void) { conf_unload(conftab); } static void usage(void) { std::fprintf(stderr, "Usage: %s []\n" " -c FILE, --config=FILE use FILE as configuration file (default is " D2DBS_DEFAULT_CONF_FILE ")\n" " -l FILE, --log=FILE set log to FILE\n" #ifdef DO_DAEMONIZE " -f, --foreground: don't daemonize\n" #endif " -D, --debug run in debug mode (run in foreground and log to stdout)\n" " -h, --help, --usage show this information and exit\n" " -v, --version print version number and exit\n" #ifdef WIN32 " Running as service functions:\n" " --service run as service\n" " -s install install service\n" " -s uninstall uninstall service\n" #endif #ifdef WIN32GUI " -console run in console mode\n" #endif "\n" "Notes:\n" " 1.You should always use absolute path here for all FILE names\n\n", progname); } #ifdef DO_DAEMONIZE extern int cmdline_get_foreground(void) { return cmdline_config.foreground; } static int conf_set_foreground(const char *valstr) { return conf_set_bool(&cmdline_config.foreground, valstr, 0); } static int conf_setdef_foreground(void) { return conf_set_bool(&cmdline_config.foreground, NULL, 0); } #endif extern const char* cmdline_get_preffile(void) { return cmdline_config.preffile; } static int conf_set_preffile(const char *valstr) { return conf_set_str(&cmdline_config.preffile, valstr, NULL); } static int conf_setdef_preffile(void) { return conf_set_str(&cmdline_config.preffile, NULL, D2DBS_DEFAULT_CONF_FILE); } extern const char* cmdline_get_logfile(void) { return cmdline_config.logfile; } static int conf_set_logfile(const char *valstr) { return conf_set_str(&cmdline_config.logfile, valstr, NULL); } static int conf_setdef_logfile(void) { return conf_set_str(&cmdline_config.logfile, NULL, NULL); } static int conf_set_debug(const char *valstr) { conf_set_bool(&cmdline_config.debug, valstr, 0); if (cmdline_config.debug) eventlog_set_debugmode(1); #ifdef DO_DAEMONIZE cmdline_config.foreground = 1; #endif return 0; } static int conf_setdef_debug(void) { return conf_set_bool(&cmdline_config.debug, NULL, 0); } static int conf_set_help(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { usage(); exitflag = 1; } return 0; } static int conf_setdef_help(void) { return 0; } static int conf_set_version(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { std::printf(D2DBS_VERSION"\n"); exitflag = 1; } return 0; } static int conf_setdef_version(void) { return 0; } #ifdef WIN32 static int conf_set_service(const char *valstr) { unsigned tmp = 0; conf_set_bool(&tmp, valstr, 0); if (tmp) { Win32_ServiceRun(); exitflag = 1; } return 0; } static int conf_setdef_service(void) { return 0; } static int conf_set_servaction(const char *valstr) { const char* tmp = NULL; conf_set_str(&tmp, valstr, NULL); if (tmp) { if (!strcasecmp(tmp, "install")) { std::fprintf(stderr, "Installing service"); Win32_ServiceInstall(); } else if (!strcasecmp(tmp, "uninstall")) { std::fprintf(stderr, "Uninstalling service"); Win32_ServiceUninstall(); } else { std::fprintf(stderr, "Unknown service action '%s'\n", tmp); } exitflag = 1; xfree((void *)tmp); } return 0; } static int conf_setdef_servaction(void) { return 0; } #endif #ifdef WIN32_GUI static int conf_set_console(const char *valstr) { conf_set_bool(&cmdline_config.console, valstr, 0); if (cmdline_config.console){ conf_set_bool(&cmdline_config.gui, NULL, 0); } return 0; } static int conf_setdef_console(void) { return conf_set_bool(&cmdline_config.console, NULL, 0); } extern unsigned cmdline_get_console(void){ return cmdline_config.console; } static int conf_set_gui(const char *valstr) { conf_set_bool(&cmdline_config.gui, valstr, 0); if (cmdline_config.gui){ } return 0; } static int conf_setdef_gui(void) { return conf_set_bool(&cmdline_config.gui, NULL, 1); } extern unsigned cmdline_get_gui(void){ return cmdline_config.gui; } #endif } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/cmdline.h000066400000000000000000000025601462640461400175540ustar00rootroot00000000000000/* * Copyright (C) 2005 Dizzy * Copyright (C) 2005 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __DDBS_CMDLINE_H_PROTOS__ #define __D2DBS_CMDLINE_H_PROTOS__ namespace pvpgn { namespace d2dbs { extern int cmdline_load(int argc, char * * argv); extern void cmdline_unload(void); /* options exported by cmdline */ #ifdef DO_DAEMONIZE extern int cmdline_get_foreground(void); #endif extern const char* cmdline_get_preffile(void); extern const char* cmdline_get_logfile(void); #ifdef WIN32_GUI extern unsigned cmdline_get_console(void); extern unsigned cmdline_get_gui(void); #endif } } #endif /* __D2DBS_CMDLINE_H_PROTOS__ */ pvpgn-1.99.7.2.1+dfsg/src/d2dbs/d2ladder.cpp000066400000000000000000000675241462640461400201700ustar00rootroot00000000000000/* * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "d2ladder.h" #include #include #include #include "compat/strncasecmp.h" #include "compat/rename.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/tag.h" #include "prefs.h" #include "common/setup_after.h" namespace pvpgn { namespace d2dbs { char * d2ladder_ladder_file = NULL; char * d2ladder_backup_file = NULL; t_d2ladderlist * d2ladder_list = NULL; unsigned long d2ladder_maxtype; int d2ladder_change_count = 0; int d2ladder_need_rebuild = 0; const char * XMLname = "d2ladder.xml"; int d2ladderlist_init(void); int d2ladderlist_destroy(void); int d2ladder_empty(void); int d2ladder_initladderfile(void); t_d2ladder * d2ladderlist_find_type(unsigned int type); int d2ladder_check(void); int d2ladder_readladder(void); int d2ladder_insert(t_d2ladder * d2ladder, t_d2ladder_info * pcharladderinfo); int d2ladder_find_char_all(t_d2ladder * d2ladder, t_d2ladder_info * info); int d2ladder_find_pos(t_d2ladder * d2ladder, t_d2ladder_info * info); int d2ladder_update_info_and_pos(t_d2ladder * d2ladder, t_d2ladder_info * info, int oldpos, int newpos); int d2ladder_checksum(unsigned char const * data, unsigned int len, unsigned int offset); int d2ladder_checksum_set(void); int d2ladder_checksum_check(void); extern int d2ladder_update(t_d2ladder_info * pcharladderinfo) { t_d2ladder * d2ladder; unsigned short hardcore, expansion, status; unsigned char chclass; unsigned int ladder_overall_type, ladder_class_type; if (!pcharladderinfo->charname[0]) return 0; chclass = pcharladderinfo->chclass; status = pcharladderinfo->status; if (prefs_get_ladder_chars_only() && (!charstatus_get_ladder(status))) return -1; hardcore = charstatus_get_hardcore(status); expansion = charstatus_get_expansion(status); ladder_overall_type = 0; if (!expansion && chclass > D2CHAR_CLASS_MAX) return -1; if (expansion && chclass > D2CHAR_EXP_CLASS_MAX) return -1; if (hardcore && expansion) { ladder_overall_type = D2LADDER_EXP_HC_OVERALL; } else if (!hardcore && expansion) { ladder_overall_type = D2LADDER_EXP_STD_OVERALL; } else if (hardcore && !expansion) { ladder_overall_type = D2LADDER_HC_OVERALL; } else if (!hardcore && !expansion) { ladder_overall_type = D2LADDER_STD_OVERALL; } ladder_class_type = ladder_overall_type + chclass + 1; d2ladder = d2ladderlist_find_type(ladder_overall_type); if (d2ladder_insert(d2ladder, pcharladderinfo) == 1) { d2ladder_change_count++; } d2ladder = d2ladderlist_find_type(ladder_class_type); if (d2ladder_insert(d2ladder, pcharladderinfo) == 1) { d2ladder_change_count++; } return 0; } int d2ladder_initladderfile(void) { std::FILE * fdladder; t_d2ladderfile_ladderindex lhead[D2LADDER_MAXTYPE]; t_d2ladderfile_header fileheader; int start; unsigned long maxtype; t_d2ladderfile_ladderinfo emptydata; unsigned int i, j, number; maxtype = D2LADDER_MAXTYPE; start = sizeof(t_d2ladderfile_header)+sizeof(lhead); for (i = 0; iD2LADDER_HC_OVERALL && i <= D2LADDER_HC_OVERALL + D2CHAR_CLASS_MAX + 1) || (i > D2LADDER_STD_OVERALL && i <= D2LADDER_STD_OVERALL + D2CHAR_CLASS_MAX + 1) || (i > D2LADDER_EXP_HC_OVERALL && i <= D2LADDER_EXP_HC_OVERALL + D2CHAR_EXP_CLASS_MAX + 1) || (i > D2LADDER_EXP_STD_OVERALL && i <= D2LADDER_EXP_STD_OVERALL + D2CHAR_EXP_CLASS_MAX + 1)) { number = D2LADDER_MAXNUM; } else { number = 0; } bn_int_set(&lhead[i].number, number); start += number*sizeof(emptydata); } std::memset(&emptydata, 0, sizeof(emptydata)); if (!d2ladder_ladder_file) return -1; fdladder = std::fopen(d2ladder_ladder_file, "wb"); if (fdladder) { bn_int_set(&fileheader.maxtype, maxtype); bn_int_set(&fileheader.checksum, 0); std::fwrite(&fileheader, 1, sizeof(fileheader), fdladder); std::fwrite(lhead, 1, sizeof(lhead), fdladder); for (i = 0; i < maxtype; i++) { for (j = 0; j < bn_int_get(lhead[i].number); j++) { std::fwrite(&emptydata, 1, sizeof(emptydata), fdladder); } } std::fclose(fdladder); d2ladder_checksum_set(); } else { eventlog(eventlog_level_error, __FUNCTION__, "error open ladder file {}", d2ladder_ladder_file); return -1; } return 0; } int d2ladder_find_pos(t_d2ladder * d2ladder, t_d2ladder_info * info) { int i; if (!d2ladder || !info) return -1; // only allow if the experience threshold is reached if (info->experience < prefs_get_ladderupdate_threshold()) return -1; i = d2ladder->len; while (i--) { if (d2ladder->info[i].experience > info->experience) { if (strncasecmp(d2ladder->info[i].charname, info->charname, MAX_CHARNAME_LEN)) { i++; } break; } if (i <= 0) break; } return i; } int d2ladder_insert(t_d2ladder * d2ladder, t_d2ladder_info * info) { int oldpos, newpos; newpos = d2ladder_find_pos(d2ladder, info); /* we currectly do nothing when character is being kick out of ladder for simple */ /* if (newpos<0 || newpos >= d2ladder->len) return 0; */ oldpos = d2ladder_find_char_all(d2ladder, info); return d2ladder_update_info_and_pos(d2ladder, info, oldpos, newpos); } int d2ladder_find_char_all(t_d2ladder * d2ladder, t_d2ladder_info * info) { int i; t_d2ladder_info * ladderdata; if (!d2ladder || !info) return -1; ladderdata = d2ladder->info; if (!ladderdata) return -1; i = d2ladder->len; while (i--) { if (!strncasecmp(ladderdata[i].charname, info->charname, MAX_CHARNAME_LEN)) return i; } return -1; } int d2ladder_update_info_and_pos(t_d2ladder * d2ladder, t_d2ladder_info * info, int oldpos, int newpos) { int i; int direction; int outflag; t_d2ladder_info * ladderdata; if (!d2ladder || !info) return -1; ladderdata = d2ladder->info; if (!ladderdata) return -1; /* character not in ladder before */ outflag = 0; if (oldpos < 0 || oldpos >= (signed)d2ladder->len) { oldpos = d2ladder->len - 1; } if (newpos < 0 || newpos >= (signed)d2ladder->len) { newpos = d2ladder->len - 1; outflag = 1; } if ((oldpos == (signed)d2ladder->len - 1) && outflag) { return 0; } if (newpos > oldpos && !outflag) newpos--; direction = (newpos > oldpos) ? 1 : -1; for (i = oldpos; i != newpos; i += direction) { ladderdata[i] = ladderdata[i + direction]; } ladderdata[i] = *info; return 1; } extern int d2ladder_rebuild(void) { d2ladder_empty(); d2ladder_need_rebuild = 0; return 0; } int d2ladder_check(void) { if (!d2ladder_ladder_file) return -1; if (!d2ladder_backup_file) return -1; if (d2ladder_checksum_check() != 1) { eventlog(eventlog_level_error, __FUNCTION__, "ladder file checksum error,try to use backup file"); if (p_rename(d2ladder_backup_file, d2ladder_ladder_file) == -1) { eventlog(eventlog_level_error, __FUNCTION__, "error std::rename {} to {}", d2ladder_backup_file, d2ladder_ladder_file); } if (d2ladder_checksum_check() != 1) { eventlog(eventlog_level_error, __FUNCTION__, "ladder backup file checksum error,rebuild ladder"); if (d2ladder_initladderfile() < 0) return -1; else { d2ladder_need_rebuild = 1; return 0; } } } return 1; } t_d2ladder * d2ladderlist_find_type(unsigned int type) { t_d2ladder * d2ladder; t_elem const * elem; if (!d2ladder_list) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL d2ladder_list"); return NULL; } LIST_TRAVERSE_CONST(d2ladder_list, elem) { if (!(d2ladder = (t_d2ladder*)elem_get_data(elem))) continue; if (d2ladder->type == type) return d2ladder; } eventlog(eventlog_level_error, __FUNCTION__, "could not find type {} in d2ladder_list", type); return NULL; } extern int d2dbs_d2ladder_init(void) { d2ladder_change_count = 0; d2ladder_maxtype = 0; d2ladder_ladder_file = (char*)xmalloc(std::strlen(d2dbs_prefs_get_ladder_dir()) + 1 + std::strlen(LADDER_FILE_PREFIX) + 1 + std::strlen(CLIENTTAG_DIABLO2DV) + 1 + 10); d2ladder_backup_file = (char*)xmalloc(std::strlen(d2dbs_prefs_get_ladder_dir()) + 1 + std::strlen(LADDER_BACKUP_PREFIX) + 1 + std::strlen(CLIENTTAG_DIABLO2DV) + 1 + 10); std::sprintf(d2ladder_ladder_file, "%s/%s.%s", d2dbs_prefs_get_ladder_dir(), \ LADDER_FILE_PREFIX, CLIENTTAG_DIABLO2DV); std::sprintf(d2ladder_backup_file, "%s/%s.%s", d2dbs_prefs_get_ladder_dir(), \ LADDER_BACKUP_PREFIX, CLIENTTAG_DIABLO2DV); if (d2ladderlist_init() < 0) { return -1; } if (d2ladder_check() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "ladder file checking error"); return -1; } if (d2ladder_readladder() < 0) { return -1; } if (d2ladder_need_rebuild) d2ladder_rebuild(); d2ladder_saveladder(); return 0; } int d2ladderlist_init(void) { t_d2ladder * d2ladder; unsigned int i; if (!d2ladder_ladder_file) return -1; d2ladder_list = list_create(); d2ladder_maxtype = D2LADDER_MAXTYPE; for (i = 0; i < d2ladder_maxtype; i++) { d2ladder = (t_d2ladder*)xmalloc(sizeof(t_d2ladder)); d2ladder->type = i; d2ladder->info = NULL; d2ladder->len = 0; list_append_data(d2ladder_list, d2ladder); } return 0; } int d2ladder_readladder(void) { t_d2ladder * d2ladder; t_d2ladderfile_header fileheader; std::FILE * fdladder; t_d2ladderfile_ladderindex * lhead; t_d2ladderfile_ladderinfo * ldata; t_d2ladder_info * info; t_d2ladder_info temp; long leftsize, blocksize; unsigned int laddertype; unsigned int tempmaxtype; int readlen; unsigned int i, number; if (!d2ladder_ladder_file) return -1; fdladder = std::fopen(d2ladder_ladder_file, "rb"); if (!fdladder) { eventlog(eventlog_level_error, __FUNCTION__, "canot open ladder file"); return -1; } std::fseek(fdladder, 0, SEEK_END); leftsize = std::ftell(fdladder); std::rewind(fdladder); blocksize = sizeof(fileheader); if (leftsize < blocksize) { eventlog(eventlog_level_error, __FUNCTION__, "file size error"); std::fclose(fdladder); return -1; } readlen = std::fread(&fileheader, 1, sizeof(fileheader), fdladder); if (readlen <= 0) { eventlog(eventlog_level_error, __FUNCTION__, "file {} read error(read:{})", d2ladder_ladder_file, std::strerror(errno)); std::fclose(fdladder); return -1; } tempmaxtype = bn_int_get(fileheader.maxtype); leftsize -= blocksize; if (tempmaxtype > D2LADDER_MAXTYPE) { eventlog(eventlog_level_error, __FUNCTION__, "ladder type > D2LADDER_MAXTYPE error"); std::fclose(fdladder); return -1; } d2ladder_maxtype = tempmaxtype; blocksize = d2ladder_maxtype*sizeof(*lhead); if (leftsize < blocksize) { eventlog(eventlog_level_error, __FUNCTION__, "file size error"); std::fclose(fdladder); return -1; } lhead = (t_d2ladderfile_ladderindex*)xmalloc(blocksize); readlen = std::fread(lhead, 1, d2ladder_maxtype*sizeof(*lhead), fdladder); if (readlen <= 0) { eventlog(eventlog_level_error, __FUNCTION__, "file {} read error(read:{})", d2ladder_ladder_file, std::strerror(errno)); xfree(lhead); std::fclose(fdladder); return -1; } leftsize -= blocksize; blocksize = 0; for (i = 0; i < d2ladder_maxtype; i++) { blocksize += bn_int_get(lhead[i].number)*sizeof(*ldata); } if (leftsize < blocksize) { eventlog(eventlog_level_error, __FUNCTION__, "file size error"); xfree(lhead); std::fclose(fdladder); return -1; } for (laddertype = 0; laddertype < d2ladder_maxtype; laddertype++) { number = bn_int_get(lhead[laddertype].number); if (number <= 0) continue; d2ladder = d2ladderlist_find_type(laddertype); if (!d2ladder) { eventlog(eventlog_level_error, __FUNCTION__, "could not find ladder type {}", laddertype); continue; } ldata = (t_d2ladderfile_ladderinfo*)xmalloc(number*sizeof(*ldata)); info = (t_d2ladder_info*)xmalloc(number * sizeof(*info)); std::memset(info, 0, number * sizeof(*info)); std::fseek(fdladder, bn_int_get(lhead[laddertype].offset), SEEK_SET); readlen = std::fread(ldata, 1, number*sizeof(*ldata), fdladder); if (readlen <= 0) { eventlog(eventlog_level_error, __FUNCTION__, "file {} read error(read:{})", d2ladder_ladder_file, std::strerror(errno)); xfree(ldata); xfree(info); continue; } d2ladder->info = info; d2ladder->len = number; for (i = 0; i < number; i++) { if (!ldata[i].charname[0]) continue; temp.experience = bn_int_get(ldata[i].experience); temp.status = bn_short_get(ldata[i].status); temp.level = bn_byte_get(ldata[i].level); temp.chclass = bn_byte_get(ldata[i].chclass); std::strncpy(temp.charname, ldata[i].charname, sizeof(info[i].charname)); if (d2ladder_update_info_and_pos(d2ladder, &temp, d2ladder_find_char_all(d2ladder, &temp), d2ladder_find_pos(d2ladder, &temp)) == 1) { d2ladder_change_count++; } } xfree(ldata); } leftsize -= blocksize; xfree(lhead); std::fclose(fdladder); return 0; } extern int d2dbs_d2ladder_destroy(void) { unsigned int i; t_d2ladder * d2ladder; d2ladder_saveladder(); for (i = 0; i < d2ladder_maxtype; i++) { d2ladder = d2ladderlist_find_type(i); if (d2ladder) { if (d2ladder->info) xfree(d2ladder->info); d2ladder->info = NULL; d2ladder->len = 0; } } d2ladderlist_destroy(); if (d2ladder_ladder_file) { xfree(d2ladder_ladder_file); d2ladder_ladder_file = NULL; } if (d2ladder_backup_file) { xfree(d2ladder_backup_file); d2ladder_backup_file = NULL; } return 0; } int d2ladderlist_destroy(void) { t_d2ladder * d2ladder; t_elem * elem; if (!d2ladder_list) return -1; LIST_TRAVERSE(d2ladder_list, elem) { if (!(d2ladder = (t_d2ladder*)elem_get_data(elem))) continue; xfree(d2ladder); list_remove_elem(d2ladder_list, &elem); } list_destroy(d2ladder_list); return 0; } int d2ladder_empty(void) { unsigned int i; t_d2ladder * d2ladder; for (i = 0; i < d2ladder_maxtype; i++) { d2ladder = d2ladderlist_find_type(i); if (d2ladder) { std::memset(d2ladder->info, 0, d2ladder->len * sizeof(*d2ladder->info)); } } return 0; } const char * get_prefix(int type, int status, int chclass) { int difficulty; static char prefix[4][4][2][16] = { { { "", "" }, { "", "" }, { "", "" }, { "", "" } }, { { "Count", "Countess" }, { "Sir", "Dame" }, { "Destroyer", "Destroyer" }, { "Slayer", "Slayer" } }, { { "Duke", "Duchess" }, { "Lord", "Lady" }, { "Conqueror", "Conqueror" }, { "Champion", "Champion" } }, { { "King", "Queen" }, { "Baron", "Baroness" }, { "Guardian", "Guardian" }, { "Patriarch", "Matriarch" } } }; static int sex[11] = { 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }; if (type == 0 || type == 1) // D2 difficulty = charstatus_get_difficulty(status); else if (type == 2 || type == 3) // D2XP difficulty = charstatus_get_difficulty_expansion(status); return prefix[difficulty][type][sex[chclass]]; } int d2ladder_print_XML(std::FILE *ladderstrm) { // modified version of d2ladder_print - changes done by jfro with a little help of aaron t_d2ladder * d2ladder; t_d2ladder_info * ldata; int overalltype, classtype; unsigned int i, type; char laddermode[4][20] = { "Hardcore", "Standard", "Expansion HC", "Expansion" }; char charclass[11][12] = { "OverAll", "Amazon", "Sorceress", "Necromancer", "Paladin", \ "Barbarian", "Druid", "Assassin", "", "", "" }; std::fprintf(ladderstrm, "\n\n"); for (type = 0; type < d2ladder_maxtype; type++) { d2ladder = d2ladderlist_find_type(type); if (!d2ladder) continue; if (d2ladder->len <= 0) continue; ldata = d2ladder->info; overalltype = 0; classtype = 0; if (type <= D2LADDER_HC_OVERALL + D2CHAR_CLASS_MAX + 1) { overalltype = 0; classtype = type - D2LADDER_HC_OVERALL; } else if (type >= D2LADDER_STD_OVERALL && type <= D2LADDER_STD_OVERALL + D2CHAR_CLASS_MAX + 1) { overalltype = 1; classtype = type - D2LADDER_STD_OVERALL; } else if (type >= D2LADDER_EXP_HC_OVERALL && type <= D2LADDER_EXP_HC_OVERALL + D2CHAR_EXP_CLASS_MAX + 1) { overalltype = 2; classtype = type - D2LADDER_EXP_HC_OVERALL; } else if (type >= D2LADDER_EXP_STD_OVERALL && type <= D2LADDER_EXP_STD_OVERALL + D2CHAR_EXP_CLASS_MAX + 1) { overalltype = 3; classtype = type - D2LADDER_EXP_STD_OVERALL; } std::fprintf(ladderstrm, "\n\t%d\n\t%s\n\t%s\n", type, laddermode[overalltype], charclass[classtype]); for (i = 0; i < d2ladder->len; i++) { if ((ldata[i].charname != NULL) && (ldata[i].charname[0] != '\0')) { std::fprintf(ladderstrm, "\t\n\t\t%2d\n\t\t%s\n\t\t%2d\n", i + 1, ldata[i].charname, ldata[i].level); std::fprintf(ladderstrm, "\t\t%u\n\t\t%s\n", ldata[i].experience, charclass[ldata[i].chclass + 1]); std::fprintf(ladderstrm, "\t\t%s\n", get_prefix(overalltype, ldata[i].status, ldata[i].chclass + 1)); if (((ldata[i].status) & (D2CHARINFO_STATUS_FLAG_DEAD | D2CHARINFO_STATUS_FLAG_HARDCORE)) == (D2CHARINFO_STATUS_FLAG_DEAD | D2CHARINFO_STATUS_FLAG_HARDCORE)) std::fprintf(ladderstrm, "\t\tdead\n\t\n"); else std::fprintf(ladderstrm, "\t\talive\n\t\n"); } } std::fprintf(ladderstrm, "\n"); std::fflush(ladderstrm); } std::fprintf(ladderstrm, "\n"); return 0; } extern int d2ladder_saveladder(void) { t_d2ladderfile_ladderindex lhead[D2LADDER_MAXTYPE]; t_d2ladderfile_header fileheader; std::FILE * fdladder; int start; unsigned int i, j, number; t_d2ladder * d2ladder; t_d2ladderfile_ladderinfo * ldata; std::FILE * XMLfile; /* if(!d2ladder_change_count) { eventlog(eventlog_level_debug,__FUNCTION__,"ladder data unchanged, skip saving"); return 0; } */ start = sizeof(fileheader)+sizeof(lhead); for (i = 0; i < D2LADDER_MAXTYPE; i++) { d2ladder = d2ladderlist_find_type(i); bn_int_set(&lhead[i].type, d2ladder->type); bn_int_set(&lhead[i].offset, start); bn_int_set(&lhead[i].number, d2ladder->len); start += d2ladder->len*sizeof(*ldata); } if (!d2ladder_ladder_file) return -1; if (!d2ladder_backup_file) return -1; if (d2ladder_checksum_check() == 1) { eventlog(eventlog_level_info, __FUNCTION__, "backup ladder file"); if (p_rename(d2ladder_ladder_file, d2ladder_backup_file) == -1) { eventlog(eventlog_level_warn, __FUNCTION__, "error std::rename {} to {}", d2ladder_ladder_file, d2ladder_backup_file); } } fdladder = std::fopen(d2ladder_ladder_file, "wb"); if (!fdladder) { eventlog(eventlog_level_error, __FUNCTION__, "error open ladder file {}", d2ladder_ladder_file); return -1; } // aaron: add extra output for XML ladder here ---> if (d2dbs_prefs_get_XML_output_ladder()) { std::string xml_filename(d2dbs_prefs_get_ladder_dir() + std::string("/") + XMLname); XMLfile = std::fopen(xml_filename.c_str(), "w"); if (XMLfile) { d2ladder_print_XML(XMLfile); std::fclose(XMLfile); } else { eventlog(eventlog_level_error, __FUNCTION__, "could not open XML ladder file \"%s\" for output", xml_filename); } } // <--- bn_int_set(&fileheader.maxtype, d2ladder_maxtype); bn_int_set(&fileheader.checksum, 0); std::fwrite(&fileheader, 1, sizeof(fileheader), fdladder); std::fwrite(lhead, 1, sizeof(lhead), fdladder); for (i = 0; i < d2ladder_maxtype; i++) { number = bn_int_get(lhead[i].number); if (number <= 0) continue; d2ladder = d2ladderlist_find_type(i); ldata = (t_d2ladderfile_ladderinfo*)xmalloc(number * sizeof(*ldata)); std::memset(ldata, 0, number * sizeof(*ldata)); for (j = 0; j < number; j++) { bn_int_set(&ldata[j].experience, d2ladder->info[j].experience); bn_short_set(&ldata[j].status, d2ladder->info[j].status); bn_byte_set(&ldata[j].level, d2ladder->info[j].level); bn_byte_set(&ldata[j].chclass, d2ladder->info[j].chclass); std::strncpy(ldata[j].charname, d2ladder->info[j].charname, sizeof(ldata[j].charname)); } std::fwrite(ldata, 1, number*sizeof(*ldata), fdladder); xfree(ldata); } std::fclose(fdladder); d2ladder_checksum_set(); eventlog(eventlog_level_info, __FUNCTION__, "ladder file saved ({} changes)", d2ladder_change_count); d2ladder_change_count = 0; return 0; } int d2ladder_print(std::FILE *ladderstrm) { t_d2ladder * d2ladder; t_d2ladder_info * ldata; unsigned int i, type; int overalltype, classtype; char laddermode[4][20] = { "Hardcore", "Standard", "Expansion HC", "Expansion" }; char charclass[11][12] = { "OverAll", "Amazon", "Sorceress", "Necromancer", "Paladin", \ "Barbarian", "Druid", "Assassin", "", "", "" }; for (type = 0; type < d2ladder_maxtype; type++) { d2ladder = d2ladderlist_find_type(type); if (!d2ladder) continue; if (d2ladder->len <= 0) continue; ldata = d2ladder->info; overalltype = 0; classtype = 0; if (type <= D2LADDER_HC_OVERALL + D2CHAR_CLASS_MAX + 1) { overalltype = 0; classtype = type - D2LADDER_HC_OVERALL; } else if (type >= D2LADDER_STD_OVERALL && type <= D2LADDER_STD_OVERALL + D2CHAR_CLASS_MAX + 1) { overalltype = 1; classtype = type - D2LADDER_STD_OVERALL; } else if (type >= D2LADDER_EXP_HC_OVERALL && type <= D2LADDER_EXP_HC_OVERALL + D2CHAR_EXP_CLASS_MAX + 1) { overalltype = 2; classtype = type - D2LADDER_EXP_HC_OVERALL; } else if (type >= D2LADDER_EXP_STD_OVERALL && type <= D2LADDER_EXP_STD_OVERALL + D2CHAR_EXP_CLASS_MAX + 1) { overalltype = 3; classtype = type - D2LADDER_EXP_STD_OVERALL; } std::fprintf(ladderstrm, "ladder type %d %s %s\n", type, laddermode[overalltype], charclass[classtype]); std::fprintf(ladderstrm, "************************************************************************\n"); std::fprintf(ladderstrm, "No character name level std::exp status title class \n"); for (i = 0; i < d2ladder->len; i++) { std::fprintf(ladderstrm, "NO.%2d %-16s %2d %10d %2X %1X %s\n", i + 1, ldata[i].charname, ldata[i].level, ldata[i].experience, ldata[i].status, 1, charclass[ldata[i].chclass + 1]); } std::fprintf(ladderstrm, "************************************************************************\n"); std::fflush(ladderstrm); } return 0; } int d2ladder_checksum(unsigned char const * data, unsigned int len, unsigned int offset) { int checksum; unsigned int i; unsigned int ch; if (!data) return 0; checksum = 0; for (i = 0; i < len; i++) { ch = data[i]; if (i >= offset && i < offset + sizeof(int)) ch = 0; ch += (checksum < 0); checksum = 2 * checksum + ch; } return checksum; } int d2ladder_checksum_set(void) { std::FILE * fdladder; long filesize; int curlen, readlen, len; unsigned char * buffer; bn_int checksum; if (!d2ladder_ladder_file) return -1; fdladder = std::fopen(d2ladder_ladder_file, "r+b"); if (!fdladder) { eventlog(eventlog_level_error, __FUNCTION__, "error open ladder file {}", d2ladder_ladder_file); return -1; } std::fseek(fdladder, 0, SEEK_END); filesize = std::ftell(fdladder); std::rewind(fdladder); if (filesize == -1) { eventlog(eventlog_level_error, __FUNCTION__, "lseek() error in ladder file {}", d2ladder_ladder_file); std::fclose(fdladder); return -1; } if (filesize < (signed)sizeof(t_d2ladderfile_header)) { eventlog(eventlog_level_error, __FUNCTION__, "ladder file size error :{}", d2ladder_ladder_file); std::fclose(fdladder); return -1; } buffer = (unsigned char*)xmalloc(filesize); curlen = 0; while (curlen 2000) len = 2000; else len = filesize - curlen; readlen = std::fread(buffer + curlen, 1, len, fdladder); if (readlen <= 0) { xfree(buffer); std::fclose(fdladder); eventlog(eventlog_level_error, __FUNCTION__, "got bad save file or read error(read:{})", std::strerror(errno)); return -1; } curlen += readlen; } bn_int_set(&checksum, d2ladder_checksum(buffer, filesize, LADDERFILE_CHECKSUM_OFFSET)); std::fseek(fdladder, LADDERFILE_CHECKSUM_OFFSET, SEEK_SET); std::fwrite(&checksum, 1, sizeof(checksum), fdladder); xfree(buffer); std::fclose(fdladder); return 0; } int d2ladder_checksum_check(void) { std::FILE * fdladder; long filesize; int curlen, readlen, len; unsigned char * buffer; int checksum, oldchecksum; t_d2ladderfile_header * header; if (!d2ladder_ladder_file) return -1; fdladder = std::fopen(d2ladder_ladder_file, "rb"); if (!fdladder) { eventlog(eventlog_level_error, __FUNCTION__, "error open ladder file {}", d2ladder_ladder_file); return -1; } std::fseek(fdladder, 0, SEEK_END); filesize = std::ftell(fdladder); std::rewind(fdladder); if (filesize == -1) { eventlog(eventlog_level_error, __FUNCTION__, "lseek() error in ladder file {}", d2ladder_ladder_file); std::fclose(fdladder); return -1; } if (filesize < (signed)sizeof(t_d2ladderfile_header)) { eventlog(eventlog_level_error, __FUNCTION__, "ladder file size error :{}", d2ladder_ladder_file); std::fclose(fdladder); return -1; } buffer = (unsigned char*)xmalloc(filesize); header = (t_d2ladderfile_header *)buffer; curlen = 0; while (curlen 2000) len = 2000; else len = filesize - curlen; readlen = std::fread(buffer + curlen, 1, len, fdladder); if (readlen <= 0) { xfree(buffer); std::fclose(fdladder); eventlog(eventlog_level_error, __FUNCTION__, "got bad save file or read error(read:{})", std::strerror(errno)); return -1; } curlen += readlen; } std::fclose(fdladder); oldchecksum = bn_int_get(header->checksum); checksum = d2ladder_checksum(buffer, filesize, LADDERFILE_CHECKSUM_OFFSET); xfree(buffer); if (oldchecksum == checksum) { eventlog(eventlog_level_info, __FUNCTION__, "ladder file check pass (checksum=0x{:X})", checksum); return 1; } else { eventlog(eventlog_level_debug, __FUNCTION__, "ladder file checksum mismatch 0x{:X} - 0x{:X}", oldchecksum, checksum); return 0; } } } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/d2ladder.h000066400000000000000000000036141462640461400176230ustar00rootroot00000000000000/* * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_D2LADDER_H #define INCLUDED_D2LADDER_H #include #include #include "common/list.h" #include "common/d2cs_d2dbs_ladder.h" #define JUST_NEED_TYPES #include "d2cs/d2charfile.h" #undef JUST_NEED_TYPES #define LADDER_BACKUP_PREFIX "ladderbk" #define DEFAULT_LADDER_DIR "var/ladders" #define LADDERFILE_CHECKSUM_OFFSET offsetof(t_d2ladderfile_header,checksum) namespace pvpgn { namespace d2dbs { typedef struct { unsigned int experience; unsigned short status; unsigned char level; unsigned char chclass; char charname[MAX_CHARNAME_LEN]; } t_d2ladder_info; typedef struct { unsigned int type; t_d2ladder_info * info; unsigned int len; } t_d2ladder; typedef t_list t_d2ladderlist; #define D2LADDER_MAXNUM 200 #define D2LADDER_OVERALL_MAXNUM 1000 #define D2LADDER_MAXTYPE 35 extern int d2dbs_d2ladder_init(void); extern int d2dbs_d2ladder_destroy(void); extern int d2ladder_rebuild(void); extern int d2ladder_update(t_d2ladder_info * pcharladderinfo); extern int d2ladder_print(std::FILE * ladderstrm); extern int d2ladder_saveladder(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2dbs/dbsdupecheck.cpp000066400000000000000000000054411462640461400211210ustar00rootroot00000000000000/* * Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "dbsdupecheck.h" #include "common/bn_type.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { namespace d2dbs { const char * delimiter = "JM"; int is_delimit(char * data) { if ((data[0] == delimiter[0]) && (data[1] == delimiter[1])) return 1; else return 0; } void * find_delimiter(char * data, unsigned int datalen) { char * datap = data; unsigned int count; for (count = 1; count < datalen; count++) { if (is_delimit(datap)) return datap; } return NULL; } #define SKIPLEN 750 extern int dbsdupecheck(char * data, unsigned int datalen) { char * pointer, *datap; unsigned int restlen; unsigned short itemcount, counter; unsigned long uid; // skip characters that have never played yet if (datalen < SKIPLEN) return DBSDUPECHECK_CONTAINS_NO_DUPE; // we skip the first SKIPLEN bytes containing various char infos wo don't care about // this a) speeds things up // and b) prevents us from detecting our magic index in a bad charname ;-) datap = data + SKIPLEN; restlen = datalen - SKIPLEN; do { pointer = (char*)find_delimiter(datap, restlen); restlen -= (pointer - datap); datap = pointer; } while ((is_delimit(datap) != 1) || (is_delimit(datap + 4) != 1)); // now we should have found "JMxxJM" itemcount = bn_short_get((bn_basic*)&datap[2]); datap += 4; restlen -= 4; for (counter = 0; counter < itemcount; counter++) { if ((datap[4] & 0x20) == 0x20) { eventlog(eventlog_level_info, __FUNCTION__, "simple item"); datap += 14; restlen -= 14; } else { eventlog(eventlog_level_info, __FUNCTION__, "extended item"); uid = bn_int_get((bn_basic*)&datap[14]); eventlog(eventlog_level_info, __FUNCTION__, "unique ID: {}", uid); pointer = (char*)find_delimiter(datap, restlen); restlen -= (pointer - datap); datap = pointer; } } return DBSDUPECHECK_CONTAINS_NO_DUPE; } } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/dbsdupecheck.h000066400000000000000000000016621462640461400205670ustar00rootroot00000000000000/* * Copyright (C) 2003 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define DBSDUPECHECK_CONTAINS_DUPE 0 #define DBSDUPECHECK_CONTAINS_NO_DUPE 1 namespace pvpgn { namespace d2dbs { extern int dbsdupecheck(char * data, unsigned int datalen); } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/dbserver.cpp000066400000000000000000000354451462640461400203200ustar00rootroot00000000000000/* * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "dbserver.h" #include #include #ifdef WIN32 # include #endif #include "compat/psock.h" #include "compat/strerror.h" #include "common/eventlog.h" #include "common/addr.h" #include "common/xalloc.h" #include "common/network.h" #include "d2ladder.h" #include "prefs.h" #include "charlock.h" #include "dbspacket.h" #include "handle_signal.h" #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_WS2TCPIP_H # include #endif #include "common/setup_after.h" #ifdef WIN32 extern int g_ServiceStatus; #endif namespace pvpgn { namespace d2dbs { static int dbs_packet_gs_id = 0; static t_preset_d2gsid *preset_d2gsid_head = NULL; t_list * dbs_server_connection_list = NULL; int dbs_server_listen_socket = -1; /* dbs_server_main * The module's driver function -- we just call other functions and * interpret their results. */ static int dbs_handle_timed_events(void); static void dbs_on_exit(void); int dbs_server_init(void); void dbs_server_loop(int ListeningSocket); int dbs_server_setup_fdsets(fd_set * pReadFDs, fd_set * pWriteFDs, fd_set * pExceptFDs, int ListeningSocket); bool dbs_server_read_data(t_d2dbs_connection* conn); bool dbs_server_write_data(t_d2dbs_connection* conn); int dbs_server_list_add_socket(int sd, unsigned int ipaddr); static int setsockopt_keepalive(int sock); static unsigned int get_preset_d2gsid(unsigned int ipaddr); int dbs_server_main(void) { eventlog(eventlog_level_info, __FUNCTION__, "establishing the listener..."); dbs_server_listen_socket = dbs_server_init(); if (dbs_server_listen_socket < 0) { eventlog(eventlog_level_error, __FUNCTION__, "dbs_server_init error "); return 3; } eventlog(eventlog_level_info, __FUNCTION__, "waiting for connections..."); dbs_server_loop(dbs_server_listen_socket); dbs_on_exit(); return 0; } /* dbs_server_init * Sets up a listener on the given interface and port, returning the * listening socket if successful; if not, returns -1. */ /* FIXME: No it doesn't! pcAddress is not ever referenced in this * function. * CreepLord: Fixed much better way (will accept dns hostnames) */ int dbs_server_init(void) { int sd; struct sockaddr_in sinInterface; int val; t_addr * servaddr; dbs_server_connection_list = list_create(); if (d2dbs_d2ladder_init() == -1) { eventlog(eventlog_level_error, __FUNCTION__, "d2ladder_init() failed"); return -1; } if (cl_init(DEFAULT_HASHTBL_LEN, DEFAULT_GS_MAX) == -1) { eventlog(eventlog_level_error, __FUNCTION__, "cl_init() failed"); return -1; } if (psock_init() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "psock_init() failed"); return -1; } sd = psock_socket(PSOCK_PF_INET, PSOCK_SOCK_STREAM, PSOCK_IPPROTO_TCP); if (sd == -1) { eventlog(eventlog_level_error, __FUNCTION__, "psock_socket() failed : {}", pstrerror(psock_errno())); return -1; } val = 1; if (psock_setsockopt(sd, PSOCK_SOL_SOCKET, PSOCK_SO_REUSEADDR, &val, sizeof(val)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "psock_setsockopt() failed : {}", pstrerror(psock_errno())); } if (!(servaddr = addr_create_str(d2dbs_prefs_get_servaddrs(), INADDR_ANY, DEFAULT_LISTEN_PORT))) { eventlog(eventlog_level_error, __FUNCTION__, "could not get servaddr"); return -1; } sinInterface.sin_family = PSOCK_AF_INET; sinInterface.sin_addr.s_addr = htonl(addr_get_ip(servaddr)); sinInterface.sin_port = htons(addr_get_port(servaddr)); if (psock_bind(sd, (struct sockaddr*)&sinInterface, (psock_t_socklen)sizeof(struct sockaddr_in)) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "psock_bind() failed : {}", pstrerror(psock_errno())); return -1; } if (psock_listen(sd, LISTEN_QUEUE) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "psock_listen() failed : {}", pstrerror(psock_errno())); return -1; } addr_destroy(servaddr); return sd; } /* dbs_server_setup_fdsets * Set up the three FD sets used with select() with the sockets in the * connection list. Also add one for the listener socket, if we have * one. */ int dbs_server_setup_fdsets(t_psock_fd_set * pReadFDs, t_psock_fd_set * pWriteFDs, t_psock_fd_set * pExceptFDs, int lsocket) { t_elem const * elem; t_d2dbs_connection* it; int highest_fd; PSOCK_FD_ZERO(pReadFDs); PSOCK_FD_ZERO(pWriteFDs); PSOCK_FD_ZERO(pExceptFDs); /* FIXME: don't check these... remove this code */ /* Add the listener socket to the read and except FD sets, if there is one. */ if (lsocket >= 0) { PSOCK_FD_SET(lsocket, pReadFDs); PSOCK_FD_SET(lsocket, pExceptFDs); } highest_fd = lsocket; LIST_TRAVERSE_CONST(dbs_server_connection_list, elem) { if (!(it = (t_d2dbs_connection*)elem_get_data(elem))) continue; if (it->nCharsInReadBuffer < (kBufferSize - kMaxPacketLength)) { /* There's space in the read buffer, so pay attention to incoming data. */ PSOCK_FD_SET(it->sd, pReadFDs); } if (it->nCharsInWriteBuffer > 0) { PSOCK_FD_SET(it->sd, pWriteFDs); } PSOCK_FD_SET(it->sd, pExceptFDs); if (highest_fd < it->sd) highest_fd = it->sd; } return highest_fd; } /* dbs_server_read_data * Data came in on a client socket, so read it into the buffer. Returns * false on failure, or when the client closes its half of the * connection. (EAGAIN doesn't count as a failure.) */ bool dbs_server_read_data(t_d2dbs_connection* conn) { int nBytes; nBytes = net_recv(conn->sd, conn->ReadBuf + conn->nCharsInReadBuffer, kBufferSize - conn->nCharsInReadBuffer); if (nBytes < 0) return false; conn->nCharsInReadBuffer += nBytes; return true; } /* dbs_server_write_data * The connection is writable, so send any pending data. Returns * false on failure. (EAGAIN doesn't count as a failure.) */ bool dbs_server_write_data(t_d2dbs_connection* conn) { int nBytes; nBytes = net_send(conn->sd, conn->WriteBuf, conn->nCharsInWriteBuffer > kMaxPacketLength ? kMaxPacketLength : conn->nCharsInWriteBuffer); if (nBytes < 0) return false; conn->nCharsInWriteBuffer -= nBytes; if (conn->nCharsInWriteBuffer) std::memmove(conn->WriteBuf, conn->WriteBuf + nBytes, conn->nCharsInWriteBuffer); return true; } int dbs_server_list_add_socket(int sd, unsigned int ipaddr) { t_d2dbs_connection *it; struct in_addr in; it = (t_d2dbs_connection*)xmalloc(sizeof(t_d2dbs_connection)); std::memset(it, 0, sizeof(t_d2dbs_connection)); it->sd = sd; it->ipaddr = ipaddr; it->major = 0; it->minor = 0; it->type = 0; it->stats = 0; it->verified = 0; it->serverid = get_preset_d2gsid(ipaddr); it->last_active = std::time(NULL); it->nCharsInReadBuffer = 0; it->nCharsInWriteBuffer = 0; list_append_data(dbs_server_connection_list, it); in.s_addr = htonl(ipaddr); char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(in), addrstr, sizeof(addrstr)); std::strncpy((char*)it->serverip, addrstr, sizeof(it->serverip) - 1); return 1; } static int dbs_handle_timed_events(void) { static std::time_t prev_ladder_save_time = 0; static std::time_t prev_keepalive_save_time = 0; static std::time_t prev_timeout_checktime = 0; std::time_t now; now = std::time(NULL); if (now - prev_ladder_save_time > (signed)prefs_get_laddersave_interval()) { d2ladder_saveladder(); prev_ladder_save_time = now; } if (now - prev_keepalive_save_time > (signed)prefs_get_keepalive_interval()) { dbs_keepalive(); prev_keepalive_save_time = now; } if (now - prev_timeout_checktime > (signed)d2dbs_prefs_get_timeout_checkinterval()) { dbs_check_timeout(); prev_timeout_checktime = now; } return 0; } void dbs_server_loop(int lsocket) { struct sockaddr_in sinRemote; int sd; fd_set ReadFDs, WriteFDs, ExceptFDs; t_elem * elem; t_d2dbs_connection* it; bool bOK; const char* pcErrorType; struct timeval tv; int highest_fd; psock_t_socklen nAddrSize = sizeof(sinRemote); while (1) { #ifdef WIN32 if (g_ServiceStatus < 0 && kbhit() && getch() == 'q') d2dbs_signal_quit_wrapper(); if (g_ServiceStatus == 0) d2dbs_signal_quit_wrapper(); while (g_ServiceStatus == 2) Sleep(1000); #endif if (d2dbs_handle_signal() < 0) break; dbs_handle_timed_events(); highest_fd = dbs_server_setup_fdsets(&ReadFDs, &WriteFDs, &ExceptFDs, lsocket); tv.tv_sec = 0; tv.tv_usec = SELECT_TIME_OUT; switch (psock_select(highest_fd + 1, &ReadFDs, &WriteFDs, &ExceptFDs, &tv)) { case -1: eventlog(eventlog_level_error, __FUNCTION__, "psock_select() failed : {}", pstrerror(psock_errno())); continue; case 0: continue; default: break; } if (PSOCK_FD_ISSET(lsocket, &ReadFDs)) { sd = psock_accept(lsocket, (struct sockaddr*)&sinRemote, &nAddrSize); if (sd == -1) { eventlog(eventlog_level_error, __FUNCTION__, "psock_accept() failed : {}", pstrerror(psock_errno())); return; } char addrstr[INET_ADDRSTRLEN] = { 0 }; inet_ntop(AF_INET, &(sinRemote.sin_addr), addrstr, sizeof(addrstr)); eventlog(eventlog_level_info, __FUNCTION__, "accepted connection from {}:{} , socket {} .", addrstr, ntohs(sinRemote.sin_port), sd); eventlog_step(prefs_get_logfile_gs(), eventlog_level_info, __FUNCTION__, "accepted connection from %s:%d , socket %d .", addrstr, ntohs(sinRemote.sin_port), sd); setsockopt_keepalive(sd); dbs_server_list_add_socket(sd, ntohl(sinRemote.sin_addr.s_addr)); if (psock_ctl(sd, PSOCK_NONBLOCK) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "could not set TCP socket [{}] to non-blocking mode (closing connection) (psock_ctl: {})", sd, pstrerror(psock_errno())); psock_close(sd); } } else if (PSOCK_FD_ISSET(lsocket, &ExceptFDs)) { eventlog(eventlog_level_error, __FUNCTION__, "exception on listening socket"); /* FIXME: exceptions are not errors with TCP, they are out-of-band data */ return; } LIST_TRAVERSE(dbs_server_connection_list, elem) { bOK = true; pcErrorType = 0; if (!(it = (t_d2dbs_connection*)elem_get_data(elem))) continue; if (PSOCK_FD_ISSET(it->sd, &ExceptFDs)) { bOK = false; pcErrorType = "General socket error"; /* FIXME: no no no no no */ PSOCK_FD_CLR(it->sd, &ExceptFDs); } else { if (PSOCK_FD_ISSET(it->sd, &ReadFDs)) { bOK = dbs_server_read_data(it); pcErrorType = "Read error"; PSOCK_FD_CLR(it->sd, &ReadFDs); } if (PSOCK_FD_ISSET(it->sd, &WriteFDs)) { bOK = dbs_server_write_data(it); pcErrorType = "Write error"; PSOCK_FD_CLR(it->sd, &WriteFDs); } } if (!bOK) { int err, errno2; psock_t_socklen errlen; err = 0; errlen = sizeof(err); errno2 = psock_errno(); if (psock_getsockopt(it->sd, PSOCK_SOL_SOCKET, PSOCK_SO_ERROR, &err, &errlen) == 0) { if (errlen && err != 0) { err = err ? err : errno2; eventlog(eventlog_level_error, __FUNCTION__, "data socket error : {}({})", pstrerror(err), err); } } dbs_server_shutdown_connection(it); list_remove_elem(dbs_server_connection_list, &elem); } else { if (dbs_packet_handle(it) == -1) { eventlog(eventlog_level_error, __FUNCTION__, "dbs_packet_handle() failed"); dbs_server_shutdown_connection(it); list_remove_elem(dbs_server_connection_list, &elem); } } } } } static void dbs_on_exit(void) { t_elem * elem; t_d2dbs_connection * it; if (dbs_server_listen_socket >= 0) psock_close(dbs_server_listen_socket); dbs_server_listen_socket = -1; LIST_TRAVERSE(dbs_server_connection_list, elem) { if (!(it = (t_d2dbs_connection*)elem_get_data(elem))) continue; dbs_server_shutdown_connection(it); list_remove_elem(dbs_server_connection_list, &elem); } cl_destroy(); d2dbs_d2ladder_destroy(); list_destroy(dbs_server_connection_list); if (preset_d2gsid_head) { t_preset_d2gsid * curr; t_preset_d2gsid * next; for (curr = preset_d2gsid_head; curr; curr = next) { next = curr->next; xfree(curr); } } eventlog(eventlog_level_info, __FUNCTION__, "dbserver stopped"); } int dbs_server_shutdown_connection(t_d2dbs_connection* conn) { psock_shutdown(conn->sd, PSOCK_SHUT_RDWR); psock_close(conn->sd); if (conn->verified && conn->type == CONNECT_CLASS_D2GS_TO_D2DBS) { eventlog(eventlog_level_info, __FUNCTION__, "unlock all characters on gs {}({})", conn->serverip, conn->serverid); eventlog_step(prefs_get_logfile_gs(), eventlog_level_info, __FUNCTION__, "unlock all characters on gs %s(%d)", conn->serverip, conn->serverid); eventlog_step(prefs_get_logfile_gs(), eventlog_level_info, __FUNCTION__, "close connection to gs on socket %d", conn->sd); cl_unlock_all_char_by_gsid(conn->serverid); } xfree(conn); return 1; } static int setsockopt_keepalive(int sock) { int optval; psock_t_socklen optlen; optval = 1; optlen = sizeof(optval); if (psock_setsockopt(sock, PSOCK_SOL_SOCKET, PSOCK_SO_KEEPALIVE, &optval, optlen)) { eventlog(eventlog_level_info, __FUNCTION__, "failed set KEEPALIVE for socket {}, errno={}", sock, psock_errno()); return -1; } else { eventlog(eventlog_level_info, __FUNCTION__, "set KEEPALIVE option for socket {}", sock); return 0; } } static unsigned int get_preset_d2gsid(unsigned int ipaddr) { t_preset_d2gsid *pgsid; pgsid = preset_d2gsid_head; while (pgsid) { if (pgsid->ipaddr == ipaddr) return pgsid->d2gsid; pgsid = pgsid->next; } /* not found, build a new item */ pgsid = (t_preset_d2gsid*)xmalloc(sizeof(t_preset_d2gsid)); pgsid->ipaddr = ipaddr; pgsid->d2gsid = ++dbs_packet_gs_id; /* add to list */ pgsid->next = preset_d2gsid_head; preset_d2gsid_head = pgsid; return preset_d2gsid_head->d2gsid; } } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/dbserver.h000066400000000000000000000030671462640461400177600ustar00rootroot00000000000000/* * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_DBSERVER_H #define INCLUDED_DBSERVER_H #include "common/list.h" namespace pvpgn { namespace d2dbs { typedef struct { int sd; unsigned int ipaddr; unsigned char major; unsigned char minor; unsigned char type; unsigned char stats; unsigned int serverid; unsigned int verified; unsigned char serverip[16]; int last_active; int nCharsInReadBuffer; int nCharsInWriteBuffer; char ReadBuf[kBufferSize]; char WriteBuf[kBufferSize]; } t_d2dbs_connection; typedef struct raw_preset_d2gsid { unsigned int ipaddr; unsigned int d2gsid; struct raw_preset_d2gsid *next; } t_preset_d2gsid; int dbs_server_main(void); int dbs_server_shutdown_connection(t_d2dbs_connection* conn); extern t_list * dbs_server_connection_list; } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2dbs/dbspacket.cpp000066400000000000000000000772111462640461400204410ustar00rootroot00000000000000/* * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "dbspacket.h" #include #include #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "compat/strsep.h" #include "compat/mkdir.h" #include "compat/rename.h" #include "compat/access.h" #include "compat/statmacros.h" #include "compat/psock.h" #include "common/xstring.h" #include "common/eventlog.h" #include "common/d2cs_d2gs_character.h" #include "common/d2char_checksum.h" #include "common/xalloc.h" #include "common/addr.h" #include "prefs.h" #include "charlock.h" #include "d2ladder.h" #include "common/setup_after.h" namespace pvpgn { namespace d2dbs { static unsigned int dbs_packet_savedata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen); static unsigned int dbs_packet_savedata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen); static unsigned int dbs_packet_getdata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, long bufsize); static unsigned int dbs_packet_getdata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int bufsize); static unsigned int dbs_packet_echoreply(t_d2dbs_connection* conn); static int dbs_packet_getdata(t_d2dbs_connection* conn); static int dbs_packet_savedata(t_d2dbs_connection* conn); static int dbs_packet_charlock(t_d2dbs_connection* conn); static int dbs_packet_updateladder(t_d2dbs_connection* conn); static int dbs_verify_ipaddr(char const * addrlist, t_d2dbs_connection * c); static int dbs_packet_fix_charinfo(t_d2dbs_connection * conn, char * AccountName, char * CharName, char * charsave); static void dbs_packet_set_charinfo_level(char * CharName, char * charinfo); static unsigned int dbs_packet_savedata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen) { char filename[MAX_PATH]; char savefile[MAX_PATH]; char bakfile[MAX_PATH]; std::FILE * fd; int checksum_header; int checksum_calc; strtolower(AccountName); strtolower(CharName); //check if checksum is ok checksum_header = bn_int_get((bn_basic*)&data[D2CHARSAVE_CHECKSUM_OFFSET]); checksum_calc = d2charsave_checksum((unsigned char *)data, datalen, D2CHARSAVE_CHECKSUM_OFFSET); if (checksum_header != checksum_calc) { eventlog(eventlog_level_error, __FUNCTION__, "received ({}) and calculated({}) checksum do not match - discarding charsave", checksum_header, checksum_calc); return 0; } std::sprintf(filename, "%s/.%s.tmp", d2dbs_prefs_get_charsave_dir(), CharName); fd = std::fopen(filename, "wb"); if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename); return 0; } std::size_t curlen = 0; std::size_t leftlen = datalen; while (curlen 2000 ? 2000 : leftlen; std::size_t readlen = std::fwrite(data + curlen, 1, writelen, fd); if (readlen <= 0) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "write() failed error : {}", std::strerror(errno)); return 0; } curlen += readlen; leftlen -= readlen; } std::fclose(fd); std::sprintf(bakfile, "%s/%s", prefs_get_charsave_bak_dir(), CharName); std::sprintf(savefile, "%s/%s", d2dbs_prefs_get_charsave_dir(), CharName); if (p_rename(savefile, bakfile) == -1) { eventlog(eventlog_level_warn, __FUNCTION__, "error std::rename {} to {}", savefile, bakfile); } if (p_rename(filename, savefile) == -1) { eventlog(eventlog_level_error, __FUNCTION__, "error std::rename {} to {}", filename, savefile); return 0; } eventlog(eventlog_level_info, __FUNCTION__, "saved charsave {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid); return datalen; } static unsigned int dbs_packet_savedata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen) { char savefile[MAX_PATH]; char bakfile[MAX_PATH]; char filepath[MAX_PATH]; char filename[MAX_PATH]; std::FILE * fd; struct stat statbuf; strtolower(AccountName); strtolower(CharName); std::sprintf(filepath, "%s/%s", prefs_get_charinfo_bak_dir(), AccountName); if (stat(filepath, &statbuf) == -1) { p_mkdir(filepath, S_IRWXU | S_IRWXG | S_IRWXO); eventlog(eventlog_level_info, __FUNCTION__, "created charinfo directory: {}", filepath); } std::sprintf(filename, "%s/%s/.%s.tmp", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName); fd = std::fopen(filename, "wb"); if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename); return 0; } std::size_t curlen = 0; std::size_t leftlen = datalen; while (curlen < datalen) { std::size_t writelen = leftlen > 2000 ? 2000 : leftlen; std::size_t readlen = std::fwrite(data + curlen, 1, writelen, fd); if (readlen <= 0) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "write() failed error : {}", std::strerror(errno)); return 0; } curlen += readlen; leftlen -= readlen; } std::fclose(fd); std::sprintf(bakfile, "%s/%s/%s", prefs_get_charinfo_bak_dir(), AccountName, CharName); std::sprintf(savefile, "%s/%s/%s", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName); if (p_rename(savefile, bakfile) == -1) { eventlog(eventlog_level_info, __FUNCTION__, "error std::rename {} to {}", savefile, bakfile); } if (p_rename(filename, savefile) == -1) { eventlog(eventlog_level_error, __FUNCTION__, "error std::rename {} to {}", filename, savefile); return 0; } eventlog(eventlog_level_info, __FUNCTION__, "saved charinfo {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid); return datalen; } static unsigned int dbs_packet_getdata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, long bufsize) { char filename[MAX_PATH]; char filename_d2closed[MAX_PATH]; std::FILE * fd; strtolower(AccountName); strtolower(CharName); std::sprintf(filename, "%s/%s", d2dbs_prefs_get_charsave_dir(), CharName); std::sprintf(filename_d2closed, "%s/%s.d2s", d2dbs_prefs_get_charsave_dir(), CharName); if ((access(filename, F_OK) < 0) && (access(filename_d2closed, F_OK) == 0)) { std::rename(filename_d2closed, filename); } fd = std::fopen(filename, "rb"); if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename); return 0; } std::fseek(fd, 0, SEEK_END); long filesize = std::ftell(fd); if (filesize == -1L) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "ftell() failed"); return 0; } std::rewind(fd); if (bufsize < filesize) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "not enough buffer"); return 0; } long curlen = 0; std::size_t leftlen = filesize; while (curlen < filesize) { std::size_t writelen = leftlen > 2000 ? 2000 : leftlen; std::size_t readlen = std::fread(data + curlen, 1, writelen, fd); if (readlen <= 0) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "read() failed error : {}", std::strerror(errno)); return 0; } leftlen -= readlen; curlen += readlen; } std::fclose(fd); eventlog(eventlog_level_info, __FUNCTION__, "loaded charsave {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid); return filesize; } static unsigned int dbs_packet_getdata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int bufsize) { char filename[MAX_PATH]; std::FILE * fd; strtolower(AccountName); strtolower(CharName); std::sprintf(filename, "%s/%s/%s", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName); fd = std::fopen(filename, "rb"); if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename); return 0; } std::fseek(fd, 0, SEEK_END); long filesize = std::ftell(fd); std::rewind(fd); if (filesize == -1) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "lseek() failed"); return 0; } if ((signed)bufsize < filesize) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "not enough buffer"); return 0; } std::size_t curlen = 0; std::size_t leftlen = filesize; while (curlen < filesize) { std::size_t writelen = leftlen > 2000 ? 2000 : leftlen; std::size_t readlen = std::fread(data + curlen, 1, writelen, fd); if (readlen <= 0) { std::fclose(fd); eventlog(eventlog_level_error, __FUNCTION__, "read() failed error : {}", std::strerror(errno)); return 0; } leftlen -= readlen; curlen += readlen; } std::fclose(fd); eventlog(eventlog_level_info, __FUNCTION__, "loaded charinfo {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid); return filesize; } static int dbs_packet_savedata(t_d2dbs_connection * conn) { unsigned short datatype; unsigned short datalen; unsigned int result; char AccountName[MAX_USERNAME_LEN]; char CharName[MAX_CHARNAME_LEN]; char RealmName[MAX_REALMNAME_LEN]; t_d2gs_d2dbs_save_data_request * savecom; t_d2dbs_d2gs_save_data_reply * saveret; char * readpos; unsigned char * writepos; readpos = conn->ReadBuf; savecom = (t_d2gs_d2dbs_save_data_request *)readpos; datatype = bn_short_get(savecom->datatype); datalen = bn_short_get(savecom->datalen); readpos += sizeof(*savecom); std::strncpy(AccountName, readpos, MAX_USERNAME_LEN); if (AccountName[MAX_USERNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max acccount name length exceeded"); return -1; } readpos += std::strlen(AccountName) + 1; std::strncpy(CharName, readpos, MAX_CHARNAME_LEN); if (CharName[MAX_CHARNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded"); return -1; } readpos += std::strlen(CharName) + 1; std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN); if (RealmName[MAX_REALMNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded"); return -1; } readpos += std::strlen(RealmName) + 1; if (readpos + datalen != conn->ReadBuf + bn_short_get(savecom->h.size)) { eventlog(eventlog_level_error, __FUNCTION__, "request packet size error"); return -1; } if (datatype == D2GS_DATA_CHARSAVE) { if (dbs_packet_savedata_charsave(conn, AccountName, CharName, readpos, datalen) > 0 && dbs_packet_fix_charinfo(conn, AccountName, CharName, readpos)) { result = D2DBS_SAVE_DATA_SUCCESS; } else { datalen = 0; result = D2DBS_SAVE_DATA_FAILED; } } else if (datatype == D2GS_DATA_PORTRAIT) { /* if level is > 255 , sets level to 255 */ dbs_packet_set_charinfo_level(CharName, readpos); if (dbs_packet_savedata_charinfo(conn, AccountName, CharName, readpos, datalen) > 0) { result = D2DBS_SAVE_DATA_SUCCESS; } else { datalen = 0; result = D2DBS_SAVE_DATA_FAILED; } } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown data type {}", datatype); return -1; } std::size_t writelen = sizeof(*saveret) + std::strlen(CharName) + 1; if (writelen > kBufferSize - conn->nCharsInWriteBuffer) return 0; writepos = (unsigned char*)(conn->WriteBuf + conn->nCharsInWriteBuffer); saveret = (t_d2dbs_d2gs_save_data_reply *)writepos; bn_short_set(&saveret->h.type, D2DBS_D2GS_SAVE_DATA_REPLY); bn_short_set(&saveret->h.size, writelen); bn_int_set(&saveret->h.seqno, bn_int_get(savecom->h.seqno)); bn_short_set(&saveret->datatype, bn_short_get(savecom->datatype)); bn_int_set(&saveret->result, result); writepos += sizeof(*saveret); std::strncpy((char*)writepos, CharName, MAX_CHARNAME_LEN); conn->nCharsInWriteBuffer += writelen; return 1; } static unsigned int dbs_packet_echoreply(t_d2dbs_connection * conn) { conn->last_active = std::time(NULL); return 1; } static int dbs_packet_getdata(t_d2dbs_connection * conn) { unsigned short writelen; unsigned short datatype; unsigned short datalen; unsigned int result; char AccountName[MAX_USERNAME_LEN]; char CharName[MAX_CHARNAME_LEN]; char RealmName[MAX_REALMNAME_LEN]; t_d2gs_d2dbs_get_data_request * getcom; t_d2dbs_d2gs_get_data_reply * getret; char * readpos; char * writepos; char databuf[kBufferSize]; t_d2charinfo_file charinfo; unsigned short charinfolen; unsigned int gsid; readpos = conn->ReadBuf; getcom = (t_d2gs_d2dbs_get_data_request *)readpos; datatype = bn_short_get(getcom->datatype); readpos += sizeof(*getcom); std::strncpy(AccountName, readpos, MAX_USERNAME_LEN); if (AccountName[MAX_USERNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max account name length exceeded"); return -1; } readpos += std::strlen(AccountName) + 1; std::strncpy(CharName, readpos, MAX_CHARNAME_LEN); if (CharName[MAX_CHARNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded"); return -1; } readpos += std::strlen(CharName) + 1; std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN); if (RealmName[MAX_REALMNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded"); return -1; } readpos += std::strlen(RealmName) + 1; if (readpos != conn->ReadBuf + bn_short_get(getcom->h.size)) { eventlog(eventlog_level_error, __FUNCTION__, "request packet size error"); return -1; } writepos = conn->WriteBuf + conn->nCharsInWriteBuffer; getret = (t_d2dbs_d2gs_get_data_reply *)writepos; datalen = 0; if (datatype == D2GS_DATA_CHARSAVE) { if (cl_query_charlock_status((unsigned char*)CharName, (unsigned char*)RealmName, &gsid) != 0) { eventlog(eventlog_level_warn, __FUNCTION__, "char {}(*{})@{} is already locked on gs {}", CharName, AccountName, RealmName, gsid); result = D2DBS_GET_DATA_CHARLOCKED; } else if (cl_lock_char((unsigned char*)CharName, (unsigned char*)RealmName, conn->serverid) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to lock char {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid); result = D2DBS_GET_DATA_CHARLOCKED; } else { eventlog(eventlog_level_info, __FUNCTION__, "lock char {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid); datalen = dbs_packet_getdata_charsave(conn, AccountName, CharName, databuf, kBufferSize); if (datalen > 0) { result = D2DBS_GET_DATA_SUCCESS; charinfolen = dbs_packet_getdata_charinfo(conn, AccountName, CharName, (char *)&charinfo, sizeof(charinfo)); if (charinfolen > 0) { result = D2DBS_GET_DATA_SUCCESS; } else { result = D2DBS_GET_DATA_FAILED; if (cl_unlock_char((unsigned char*)CharName, (unsigned char*)RealmName, gsid) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to unlock char {}(*{})@{} for gs {}({})", CharName, \ AccountName, RealmName, conn->serverip, conn->serverid); } else { eventlog(eventlog_level_info, __FUNCTION__, "unlock char {}(*{})@{} for gs {}({})", CharName, \ AccountName, RealmName, conn->serverip, conn->serverid); } } } else { datalen = 0; result = D2DBS_GET_DATA_FAILED; if (cl_unlock_char((unsigned char*)CharName, (unsigned char*)RealmName, gsid) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "faled to unlock char {}(*{})@{} for gs {}({})", CharName, \ AccountName, RealmName, conn->serverip, conn->serverid); } else { eventlog(eventlog_level_info, __FUNCTION__, "unlock char {}(*{})@{} for gs {}({})", CharName, \ AccountName, RealmName, conn->serverip, conn->serverid); } } } if (result == D2DBS_GET_DATA_SUCCESS) { bn_int_set(&getret->charcreatetime, bn_int_get(charinfo.header.create_time)); /* FIXME: this should be rewritten to support string formatted std::time */ if (bn_int_get(charinfo.header.create_time) >= prefs_get_ladderinit_time()) { bn_int_set(&getret->allowladder, 1); } else { bn_int_set(&getret->allowladder, 0); } } else { bn_int_set(&getret->charcreatetime, 0); bn_int_set(&getret->allowladder, 0); } } else if (datatype == D2GS_DATA_PORTRAIT) { datalen = dbs_packet_getdata_charinfo(conn, AccountName, CharName, databuf, kBufferSize); if (datalen > 0) result = D2DBS_GET_DATA_SUCCESS; else { datalen = 0; result = D2DBS_GET_DATA_FAILED; } } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown data type {}", datatype); return -1; } writelen = datalen + sizeof(*getret) + std::strlen(CharName) + 1; if (writelen > kBufferSize - conn->nCharsInWriteBuffer) return 0; bn_short_set(&getret->h.type, D2DBS_D2GS_GET_DATA_REPLY); bn_short_set(&getret->h.size, writelen); bn_int_set(&getret->h.seqno, bn_int_get(getcom->h.seqno)); bn_short_set(&getret->datatype, bn_short_get(getcom->datatype)); bn_int_set(&getret->result, result); bn_short_set(&getret->datalen, datalen); writepos += sizeof(*getret); std::strncpy(writepos, CharName, MAX_CHARNAME_LEN); writepos += std::strlen(CharName) + 1; if (datalen) std::memcpy(writepos, databuf, datalen); conn->nCharsInWriteBuffer += writelen; return 1; } static int dbs_packet_updateladder(t_d2dbs_connection * conn) { char CharName[MAX_CHARNAME_LEN]; char RealmName[MAX_REALMNAME_LEN]; t_d2gs_d2dbs_update_ladder * updateladder; char * readpos; t_d2ladder_info charladderinfo; readpos = conn->ReadBuf; updateladder = (t_d2gs_d2dbs_update_ladder *)readpos; readpos += sizeof(*updateladder); std::strncpy(CharName, readpos, MAX_CHARNAME_LEN); if (CharName[MAX_CHARNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded"); return -1; } readpos += std::strlen(CharName) + 1; std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN); if (RealmName[MAX_REALMNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded"); return -1; } readpos += std::strlen(RealmName) + 1; if (readpos != conn->ReadBuf + bn_short_get(updateladder->h.size)) { eventlog(eventlog_level_error, __FUNCTION__, "request packet size error"); return -1; } std::strcpy(charladderinfo.charname, CharName); charladderinfo.experience = bn_int_get(updateladder->charexplow); charladderinfo.level = bn_int_get(updateladder->charlevel); charladderinfo.status = bn_short_get(updateladder->charstatus); charladderinfo.chclass = bn_short_get(updateladder->charclass); eventlog(eventlog_level_info, __FUNCTION__, "update ladder for {}@{} for gs {}({})", CharName, RealmName, conn->serverip, conn->serverid); d2ladder_update(&charladderinfo); return 1; } static int dbs_packet_charlock(t_d2dbs_connection * conn) { char CharName[MAX_CHARNAME_LEN]; char AccountName[MAX_USERNAME_LEN]; char RealmName[MAX_REALMNAME_LEN]; t_d2gs_d2dbs_char_lock * charlock; char * readpos; readpos = conn->ReadBuf; charlock = (t_d2gs_d2dbs_char_lock*)readpos; readpos += sizeof(*charlock); std::strncpy(AccountName, readpos, MAX_USERNAME_LEN); if (AccountName[MAX_USERNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max account name length exceeded"); return -1; } readpos += std::strlen(AccountName) + 1; std::strncpy(CharName, readpos, MAX_CHARNAME_LEN); if (CharName[MAX_CHARNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded"); return -1; } readpos += std::strlen(CharName) + 1; std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN); if (RealmName[MAX_REALMNAME_LEN - 1] != 0) { eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded"); return -1; } readpos += std::strlen(RealmName) + 1; if (readpos != conn->ReadBuf + bn_short_get(charlock->h.size)) { eventlog(eventlog_level_error, __FUNCTION__, "request packet size error"); return -1; } if (bn_int_get(charlock->lockstatus)) { if (cl_lock_char((unsigned char*)CharName, (unsigned char*)RealmName, conn->serverid) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to lock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid); } else { eventlog(eventlog_level_info, __FUNCTION__, "lock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid); } } else { if (cl_unlock_char((unsigned char*)CharName, (unsigned char*)RealmName, conn->serverid) != 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to unlock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid); } else { eventlog(eventlog_level_info, __FUNCTION__, "unlock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid); } } return 1; } /* return value: 1 : process one or more packet 0 : not get a whole packet,do nothing -1 : error */ extern int dbs_packet_handle(t_d2dbs_connection* conn) { unsigned short readlen, writelen; t_d2dbs_d2gs_header * readhead; int retval; if (conn->stats == 0) { if (conn->nCharsInReadBuffer < (signed)sizeof(t_d2gs_d2dbs_connect)) { return 0; } conn->stats = 1; conn->type = conn->ReadBuf[0]; if (conn->type == CONNECT_CLASS_D2GS_TO_D2DBS) { if (dbs_verify_ipaddr(d2dbs_prefs_get_d2gs_list(), conn) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "d2gs connection from unknown ip address"); return -1; } readlen = 1; writelen = 0; eventlog(eventlog_level_info, __FUNCTION__, "set connection type for gs {}({}) on socket {}", conn->serverip, conn->serverid, conn->sd); eventlog_step(prefs_get_logfile_gs(), eventlog_level_info, __FUNCTION__, "set connection type for gs %s(%d) on socket %d", conn->serverip, conn->serverid, conn->sd); } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown connection type"); return -1; } conn->nCharsInReadBuffer -= readlen; std::memmove(conn->ReadBuf, conn->ReadBuf + readlen, conn->nCharsInReadBuffer); } else if (conn->stats == 1) { if (conn->type == CONNECT_CLASS_D2GS_TO_D2DBS) { while (conn->nCharsInReadBuffer >= (signed)sizeof(*readhead)) { readhead = (t_d2dbs_d2gs_header *)conn->ReadBuf; readlen = bn_short_get(readhead->size); if (conn->nCharsInReadBuffer < readlen) break; switch (bn_short_get(readhead->type)) { case D2GS_D2DBS_SAVE_DATA_REQUEST: retval = dbs_packet_savedata(conn); break; case D2GS_D2DBS_GET_DATA_REQUEST: retval = dbs_packet_getdata(conn); break; case D2GS_D2DBS_UPDATE_LADDER: retval = dbs_packet_updateladder(conn); break; case D2GS_D2DBS_CHAR_LOCK: retval = dbs_packet_charlock(conn); break; case D2GS_D2DBS_ECHOREPLY: retval = dbs_packet_echoreply(conn); break; default: eventlog(eventlog_level_error, __FUNCTION__, "unknown request type {}", \ bn_short_get(readhead->type)); retval = -1; } if (retval != 1) return retval; conn->nCharsInReadBuffer -= readlen; std::memmove(conn->ReadBuf, conn->ReadBuf + readlen, conn->nCharsInReadBuffer); } } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown connection type {}", conn->type); return -1; } } else { eventlog(eventlog_level_error, __FUNCTION__, "unknown connection stats"); return -1; } return 1; } /* FIXME: we should save client ipaddr into c->ipaddr after accept */ static int dbs_verify_ipaddr(char const * addrlist, t_d2dbs_connection * c) { char * adlist; char * s, *temp; t_elem * elem; t_d2dbs_connection * tempc; unsigned int valid; unsigned int resolveipaddr; adlist = xstrdup(addrlist); temp = adlist; valid = 0; while ((s = strsep(&temp, ","))) { host_lookup(s, &resolveipaddr); if (resolveipaddr == 0) continue; if (c->ipaddr == resolveipaddr) { valid = 1; break; } } xfree(adlist); if (valid) { eventlog(eventlog_level_info, __FUNCTION__, "ip address {} is valid", addr_num_to_ip_str(c->ipaddr)); LIST_TRAVERSE(dbs_server_connection_list, elem) { if (!(tempc = (t_d2dbs_connection*)elem_get_data(elem))) continue; if (tempc != c && tempc->ipaddr == c->ipaddr) { eventlog(eventlog_level_info, __FUNCTION__, "destroying previous connection {}", tempc->serverid); dbs_server_shutdown_connection(tempc); list_remove_elem(dbs_server_connection_list, &elem); } } c->verified = 1; return 0; } else { eventlog(eventlog_level_info, __FUNCTION__, "ip address {} is invalid", addr_num_to_ip_str(c->ipaddr)); } return -1; } int dbs_check_timeout(void) { t_elem *elem; t_d2dbs_connection *tempc; std::time_t now; int timeout; now = std::time(NULL); timeout = d2dbs_prefs_get_idletime(); LIST_TRAVERSE(dbs_server_connection_list, elem) { if (!(tempc = (t_d2dbs_connection*)elem_get_data(elem))) continue; if (now - tempc->last_active > timeout) { eventlog(eventlog_level_debug, __FUNCTION__, "connection {} timed out", tempc->serverid); dbs_server_shutdown_connection(tempc); list_remove_elem(dbs_server_connection_list, &elem); continue; } } return 0; } int dbs_keepalive(void) { t_elem *elem; t_d2dbs_connection *tempc; t_d2dbs_d2gs_echorequest *echoreq; unsigned short writelen; unsigned char *writepos; std::time_t now; writelen = sizeof(t_d2dbs_d2gs_echorequest); now = std::time(NULL); LIST_TRAVERSE(dbs_server_connection_list, elem) { if (!(tempc = (t_d2dbs_connection*)elem_get_data(elem))) continue; if (writelen > kBufferSize - tempc->nCharsInWriteBuffer) continue; writepos = (unsigned char*)(tempc->WriteBuf + tempc->nCharsInWriteBuffer); echoreq = (t_d2dbs_d2gs_echorequest*)writepos; bn_short_set(&echoreq->h.type, D2DBS_D2GS_ECHOREQUEST); bn_short_set(&echoreq->h.size, writelen); /* FIXME: sequence number not set */ bn_int_set(&echoreq->h.seqno, 0); tempc->nCharsInWriteBuffer += writelen; } return 0; } /*************************************************************************************/ #define CHARINFO_SIZE 0xC0 #define CHARINFO_PORTRAIT_LEVEL_OFFSET 0x89 #define CHARINFO_PORTRAIT_STATUS_OFFSET 0x8A #define CHARINFO_SUMMARY_LEVEL_OFFSET 0xB8 #define CHARINFO_SUMMARY_STATUS_OFFSET 0xB4 #define CHARINFO_PORTRAIT_GFX_OFFSET 0x72 #define CHARINFO_PORTRAIT_COLOR_OFFSET 0x7E #define CHARSAVE_LEVEL_OFFSET 0x2B #define CHARSAVE_STATUS_OFFSET 0x24 #define CHARSAVE_GFX_OFFSET 0x88 #define CHARSAVE_COLOR_OFFSET 0x98 #define charstatus_to_portstatus(status) ((((status & 0xFF00) << 1) | (status & 0x00FF)) | 0x8080) #define portstatus_to_charstatus(status) (((status & 0x7F00) >> 1) | (status & 0x007F)) static void dbs_packet_set_charinfo_level(char * CharName, char * charinfo) { if (prefs_get_difficulty_hack()) { /* difficulty hack enabled */ unsigned int level = bn_int_get((bn_basic*)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET]); unsigned int plevel = bn_byte_get((bn_basic*)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET]); /* levels 257 thru 355 */ if (level != plevel) { eventlog(eventlog_level_info, __FUNCTION__, "level mis-match for {} ( {} != {} ) setting to 255", CharName, level, plevel); bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET], 255); bn_int_set((bn_int *)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET], 255); } } } static int dbs_packet_fix_charinfo(t_d2dbs_connection * conn, char * AccountName, char * CharName, char * charsave) { if (prefs_get_difficulty_hack()) { unsigned char charinfo[CHARINFO_SIZE]; unsigned int level = bn_byte_get((bn_basic*)&charsave[CHARSAVE_LEVEL_OFFSET]); unsigned short status = bn_short_get((bn_basic*)&charsave[CHARSAVE_STATUS_OFFSET]); unsigned short pstatus = charstatus_to_portstatus(status); int i; /* * charinfo is only updated from level 1 to 99 (d2gs issue) * from 100 to 256 d2gs does not send it * when value rolls over (level 256 = 0) * and charactar reaches level 257 (rolled over to level 1) * d2gs starts sending it agian until level 356 (rolled over to 100) * is reached agian. etc. etc. etc. */ if (level == 0) /* level 256, 512, 768, etc */ level = 255; if (level < 100) return 1; /* d2gs will send charinfo - level will be set to 255 at that std::time if needed */ eventlog(eventlog_level_info, __FUNCTION__, "level {} > 99 for {}", level, CharName); if (!(dbs_packet_getdata_charinfo(conn, AccountName, CharName, (char*)charinfo, CHARINFO_SIZE))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to get charinfo for {}", CharName); return 0; } /* if level in charinfo file is already set to 255, * then is must have been set when d2gs sent the charinfo * and got a level mis-match (levels 257 - 355) * or level is actually 255. In eather case we set to 255 * this should work for any level mod */ if (bn_byte_get(&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET]) == 255) level = 255; eventlog(eventlog_level_info, __FUNCTION__, "updating charinfo for {} -> level = {} , status = 0x{:04X} , pstatus = 0x{:04X}", CharName, level, status, pstatus); bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET], level); bn_int_set((bn_int *)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET], level); bn_short_set((bn_short *)&charinfo[CHARINFO_PORTRAIT_STATUS_OFFSET], pstatus); bn_int_set((bn_int *)&charinfo[CHARINFO_SUMMARY_STATUS_OFFSET], status); for (i = 0; i < 11; i++) { bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_GFX_OFFSET + i], bn_byte_get((bn_basic*)&charsave[CHARSAVE_GFX_OFFSET + i])); bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_COLOR_OFFSET + i], bn_byte_get((bn_basic*)&charsave[CHARSAVE_GFX_OFFSET + i])); } if (!(dbs_packet_savedata_charinfo(conn, AccountName, CharName, (char*)charinfo, CHARINFO_SIZE))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to save charinfo for {}", CharName); return 0; } return 1; /* charinfo updated */ } return 1; /* difficulty hack not enabled */ } } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/dbspacket.h000066400000000000000000000061461462640461400201050ustar00rootroot00000000000000/* * Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn) * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_DBSPACKET_H #define INCLUDED_DBSPACKET_H #include "common/bn_type.h" #include "dbserver.h" namespace pvpgn { namespace d2dbs { typedef struct { bn_short size; bn_short type; bn_int seqno; } t_d2dbs_d2gs_header; typedef struct { bn_byte cclass; } t_d2gs_d2dbs_connect; #define CONNECT_CLASS_D2GS_TO_D2DBS 0x65 #define D2GS_D2DBS_SAVE_DATA_REQUEST 0x30 typedef struct { t_d2dbs_d2gs_header h; bn_short datatype; bn_short datalen; /* AccountName */ /* CharName */ /* data */ } t_d2gs_d2dbs_save_data_request; #define D2GS_DATA_CHARSAVE 0x01 #define D2GS_DATA_PORTRAIT 0x02 #define D2DBS_D2GS_SAVE_DATA_REPLY 0x30 typedef struct { t_d2dbs_d2gs_header h; bn_int result; bn_short datatype; /* CharName */ } t_d2dbs_d2gs_save_data_reply; #define D2DBS_SAVE_DATA_SUCCESS 0 #define D2DBS_SAVE_DATA_FAILED 1 #define D2GS_D2DBS_GET_DATA_REQUEST 0x31 typedef struct { t_d2dbs_d2gs_header h; bn_short datatype; /* AccountName */ /* CharName */ } t_d2gs_d2dbs_get_data_request; #define D2DBS_D2GS_GET_DATA_REPLY 0x31 typedef struct { t_d2dbs_d2gs_header h; bn_int result; bn_int charcreatetime; bn_int allowladder; bn_short datatype; bn_short datalen; /* CharName */ /* data */ } t_d2dbs_d2gs_get_data_reply; #define D2DBS_GET_DATA_SUCCESS 0 #define D2DBS_GET_DATA_FAILED 1 #define D2DBS_GET_DATA_CHARLOCKED 2 #define D2GS_D2DBS_UPDATE_LADDER 0x32 typedef struct { t_d2dbs_d2gs_header h; bn_int charlevel; bn_int charexplow; bn_int charexphigh; bn_short charclass; bn_short charstatus; /* CharName */ /* RealmName */ } t_d2gs_d2dbs_update_ladder; #define D2GS_D2DBS_CHAR_LOCK 0x33 typedef struct { t_d2dbs_d2gs_header h; bn_int lockstatus; /* CharName */ /* RealmName */ } t_d2gs_d2dbs_char_lock; #define D2DBS_D2GS_ECHOREQUEST 0x34 typedef struct { t_d2dbs_d2gs_header h; } t_d2dbs_d2gs_echorequest; #define D2GS_D2DBS_ECHOREPLY 0x34 typedef struct { t_d2dbs_d2gs_header h; } t_d2gs_d2dbs_echoreply; extern int dbs_packet_handle(t_d2dbs_connection * conn); extern int dbs_keepalive(void); extern int dbs_check_timeout(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2dbs/handle_signal.cpp000066400000000000000000000117451462640461400212710ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "handle_signal.h" #include #include #include #include "common/eventlog.h" #include "common/xalloc.h" #include "prefs.h" #include "d2ladder.h" #include "cmdline.h" #include "common/setup_after.h" namespace pvpgn { namespace d2dbs { static void on_signal(int s); static volatile struct { unsigned char do_quit; unsigned char cancel_quit; unsigned char reload_config; unsigned char save_ladder; time_t exit_time; } signal_data = { 0, 0, 0, 0, 0 }; extern int d2dbs_handle_signal(void) { std::time_t now; char const * levels; char * temp; char const * tok; if (signal_data.cancel_quit) { signal_data.cancel_quit = 0; if (!signal_data.exit_time) { eventlog(eventlog_level_info, __FUNCTION__, "there is no previous shutdown to be canceled"); } else { signal_data.exit_time = 0; eventlog(eventlog_level_info, __FUNCTION__, "shutdown was canceled due to std::signal"); } } if (signal_data.do_quit) { signal_data.do_quit = 0; now = std::time(NULL); if (!signal_data.exit_time) { signal_data.exit_time = now + d2dbs_prefs_get_shutdown_delay(); } else { signal_data.exit_time -= d2dbs_prefs_get_shutdown_decr(); } eventlog(eventlog_level_info, __FUNCTION__, "the server is going to shutdown in {} minutes", (signal_data.exit_time - now) / 60); } if (signal_data.exit_time) { now = std::time(NULL); if (now >= (signed)signal_data.exit_time) { signal_data.exit_time = 0; eventlog(eventlog_level_info, __FUNCTION__, "shutdown server due to std::signal"); return -1; } } if (signal_data.reload_config) { signal_data.reload_config = 0; eventlog(eventlog_level_info, __FUNCTION__, "reloading configuartion file due to std::signal"); if (d2dbs_prefs_reload(cmdline_get_preffile()) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error reload configuration file,exitting"); return -1; } eventlog_clear_level(); if ((levels = d2dbs_prefs_get_loglevels())) { temp = xstrdup(levels); tok = std::strtok(temp, ","); /* std::strtok modifies the string it is passed */ while (tok) { if (eventlog_add_level(tok) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not add std::log level \"{}\"", tok); tok = std::strtok(NULL, ","); } xfree(temp); } #ifdef DO_DAEMONIZE if (!cmdline_get_foreground()) #endif eventlog_open(d2dbs_prefs_get_logfile()); } if (signal_data.save_ladder) { signal_data.save_ladder = 0; eventlog(eventlog_level_info, __FUNCTION__, "save ladder data due to std::signal"); d2ladder_saveladder(); } return 0; } #ifdef WIN32 extern void d2dbs_signal_quit_wrapper(void) { signal_data.do_quit = 1; } extern void d2dbs_signal_reload_config_wrapper(void) { signal_data.reload_config = 1; } extern void d2dbs_signal_save_ladder_wrapper(void) { signal_data.save_ladder = 1; } extern void d2dbs_signal_exit_wrapper(void) { signal_data.exit_time = 1; } #else extern int d2dbs_handle_signal_init(void) { std::signal(SIGINT, on_signal); std::signal(SIGTERM, on_signal); std::signal(SIGABRT, on_signal); std::signal(SIGHUP, on_signal); std::signal(SIGUSR1, on_signal); std::signal(SIGPIPE, on_signal); return 0; } static void on_signal(int s) { switch (s) { case SIGINT: eventlog(eventlog_level_debug, __FUNCTION__, "sigint received"); signal_data.do_quit = 1; break; case SIGTERM: eventlog(eventlog_level_debug, __FUNCTION__, "sigint received"); signal_data.do_quit = 1; break; case SIGABRT: eventlog(eventlog_level_debug, __FUNCTION__, "sigabrt received"); signal_data.cancel_quit = 1; break; case SIGHUP: eventlog(eventlog_level_debug, __FUNCTION__, "sighup received"); signal_data.reload_config = 1; break; case SIGUSR1: eventlog(eventlog_level_debug, __FUNCTION__, "sigusr1 received"); signal_data.save_ladder = 1; break; case SIGPIPE: eventlog(eventlog_level_debug, __FUNCTION__, "sigpipe received"); break; } std::signal(s, on_signal); } #endif } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/handle_signal.h000066400000000000000000000022771462640461400207360ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_HANDLE_SIGNAL_H #define INCLUDED_HANDLE_SIGNAL_H namespace pvpgn { namespace d2dbs { #ifndef WIN32 extern int d2dbs_handle_signal_init(void); #else extern void d2dbs_signal_quit_wrapper(void); extern void d2dbs_signal_reload_config_wrapper(void); extern void d2dbs_signal_save_ladder_wrapper(void); extern void d2dbs_signal_exit_wrapper(void); #endif extern int d2dbs_handle_signal(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2dbs/main.cpp000066400000000000000000000141471462640461400174240ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2005 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef WIN32 # include "win32/service.h" #endif #ifdef WIN32_GUI # include "win32/winmain.h" #endif #ifdef WIN32 # include "win32/windump.h" #endif #include "compat/stdfileno.h" #include "compat/pgetpid.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "cmdline.h" #include "prefs.h" #include "version.h" #include "handle_signal.h" #include "dbserver.h" #include "common/setup_after.h" using namespace pvpgn::d2dbs; using namespace pvpgn; static std::FILE * eventlog_fp; char serviceLongName[] = "d2dbs service"; char serviceName[] = "d2dbs"; char serviceDescription[] = "Diablo 2 DataBase Server"; int g_ServiceStatus = -1; static int init(void); static int cleanup(void); static int config_init(int argc, char * * argv); static int config_cleanup(void); static int setup_daemon(void); static char * write_to_pidfile(void); #ifdef DO_DAEMONIZE static int setup_daemon(void) { int pid; if (chdir("/") < 0) { eventlog(eventlog_level_error, __FUNCTION__, "can not change working directory to root directory (chdir: {})", std::strerror(errno)); return -1; } close(STDIN_FILENO); close(STDOUT_FILENO); if (!cmdline_get_foreground()) { close(STDERR_FILENO); } switch ((pid = fork())) { case 0: break; case -1: eventlog(eventlog_level_error, __FUNCTION__, "error create child process (fork: {})", std::strerror(errno)); return -1; default: return pid; } umask(0); setsid(); return 0; } #endif static char * write_to_pidfile(void) { char *pidfile = xstrdup(d2dbs_prefs_get_pidfile()); if (pidfile) { if (pidfile[0] == '\0') { xfree((void *)pidfile); /* avoid warning */ return NULL; } #ifdef HAVE_GETPID std::FILE * fp; if (!(fp = std::fopen(pidfile, "w"))) { eventlog(eventlog_level_error, __FUNCTION__, "unable to open pid file \"{}\" for writing (std::fopen: {})", pidfile, std::strerror(errno)); xfree((void *)pidfile); /* avoid warning */ return NULL; } else { std::fprintf(fp, "%u", (unsigned int)getpid()); if (std::fclose(fp) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not close pid file \"{}\" after writing (std::fclose: {})", pidfile, std::strerror(errno)); } #else eventlog(eventlog_level_warn, __FUNCTION__, "no getpid() std::system call, disable pid file in d2dbs.conf"); xfree((void *)pidfile); /* avoid warning */ return NULL; #endif } return pidfile; } static int init(void) { return 0; } static int cleanup(void) { return 0; } static int config_init(int argc, char * * argv) { char const * levels; char * temp; char const * tok; if (cmdline_load(argc, argv) < 0) { return -1; } #ifdef DO_DAEMONIZE int pid; if (!cmdline_get_foreground()) { if (!((pid = setup_daemon()) == 0)) { return pid; } } #endif if (d2dbs_prefs_load(cmdline_get_preffile()) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error loading configuration file {}", cmdline_get_preffile()); return -1; } eventlog_clear_level(); if ((levels = d2dbs_prefs_get_loglevels())) { temp = xstrdup(levels); tok = std::strtok(temp, ","); /* std::strtok modifies the string it is passed */ while (tok) { if (eventlog_add_level(tok) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not add std::log level \"{}\"", tok); tok = std::strtok(NULL, ","); } xfree(temp); } #ifdef WIN32_GUI if (cmdline_get_gui()){ eventlog_add_level(eventlog_get_levelname_str(eventlog_level_gui)); } #endif #ifdef DO_DAEMONIZE if (cmdline_get_foreground()) { eventlog_set(stderr); } else #endif { if (cmdline_get_logfile()) { if (eventlog_open(cmdline_get_logfile()) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error open eventlog file {}", cmdline_get_logfile()); return -1; } } else { if (eventlog_open(d2dbs_prefs_get_logfile()) < 0) { eventlog(eventlog_level_error, __FUNCTION__, "error open eventlog file {}", d2dbs_prefs_get_logfile()); return -1; } } } return 0; } static int config_cleanup(void) { d2dbs_prefs_unload(); cmdline_unload(); eventlog_close(); if (eventlog_fp) std::fclose(eventlog_fp); return 0; } #ifdef WIN32_GUI extern int app_main(int argc, char ** argv) #else extern int main(int argc, char ** argv) #endif { int pid; char * pidfile; #ifdef WIN32 // create a dump file whenever the gateway crashes SetUnhandledExceptionFilter(unhandled_handler); #endif eventlog_set(stderr); pid = config_init(argc, argv); if (!(pid == 0)) { // if (pid==1) pid=0; return pid; } pidfile = write_to_pidfile(); eventlog(eventlog_level_info, __FUNCTION__, D2DBS_VERSION); if (init() < 0) { eventlog(eventlog_level_error, __FUNCTION__, "failed to init"); return -1; } else { eventlog(eventlog_level_info, __FUNCTION__, "server initialized"); } #ifndef WIN32 d2dbs_handle_signal_init(); #endif dbs_server_main(); cleanup(); if (pidfile) { if (std::remove(pidfile) < 0) eventlog(eventlog_level_error, __FUNCTION__, "could not remove pid file \"{}\" (std::remove: {})", pidfile, std::strerror(errno)); xfree((void *)pidfile); /* avoid warning */ } config_cleanup(); return 0; } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/prefs.cpp000066400000000000000000000346711462640461400176230ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2005 Olaf Freyer (aaron@cs.tu-berlin.de) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #include "prefs.h" #include #include "common/conf.h" #include "common/eventlog.h" #include "common/setup_after.h" namespace pvpgn { namespace d2dbs { static struct { char const * logfile; char const * logfile_gs; char const * servaddrs; char const * charsave_dir; char const * charinfo_dir; char const * charsave_bak_dir; char const * charinfo_bak_dir; char const * ladder_dir; char const * d2gs_list; unsigned int laddersave_interval; unsigned int ladderinit_time; char const * loglevels; char const * pidfile; unsigned int shutdown_delay; unsigned int shutdown_decr; unsigned int idletime; unsigned int keepalive_interval; unsigned int timeout_checkinterval; unsigned int XML_output_ladder; unsigned int ladderupdate_threshold; unsigned int ladder_chars_only; unsigned int difficulty_hack; } prefs_conf; static int conf_set_logfile(const char* valstr); static int conf_setdef_logfile(void); static int conf_set_logfile_gs(const char* valstr); static int conf_setdef_logfile_gs(void); static int conf_set_servaddrs(const char* valstr); static int conf_setdef_servaddrs(void); static int conf_set_charsave_dir(const char* valstr); static int conf_setdef_charsave_dir(void); static int conf_set_charinfo_dir(const char* valstr); static int conf_setdef_charinfo_dir(void); static int conf_set_charsave_bak_dir(const char* valstr); static int conf_setdef_charsave_bak_dir(void); static int conf_set_charinfo_bak_dir(const char* valstr); static int conf_setdef_charinfo_bak_dir(void); static int conf_set_ladder_dir(const char* valstr); static int conf_setdef_ladder_dir(void); static int conf_set_d2gs_list(const char* valstr); static int conf_setdef_d2gs_list(void); static int conf_set_laddersave_interval(const char* valstr); static int conf_setdef_laddersave_interval(void); static int conf_set_ladderinit_time(const char* valstr); static int conf_setdef_ladderinit_time(void); static int conf_set_loglevels(const char* valstr); static int conf_setdef_loglevels(void); static int conf_set_pidfile(const char* valstr); static int conf_setdef_pidfile(void); static int conf_set_shutdown_delay(const char* valstr); static int conf_setdef_shutdown_delay(void); static int conf_set_shutdown_decr(const char* valstr); static int conf_setdef_shutdown_decr(void); static int conf_set_idletime(const char* valstr); static int conf_setdef_idletime(void); static int conf_set_keepalive_interval(const char* valstr); static int conf_setdef_keepalive_interval(void); static int conf_set_timeout_checkinterval(const char* valstr); static int conf_setdef_timeout_checkinterval(void); static int conf_set_XML_output_ladder(const char* valstr); static int conf_setdef_XML_output_ladder(void); static int conf_set_ladderupdate_threshold(const char* valstr); static int conf_setdef_ladderupdate_threshold(void); static int conf_set_ladder_chars_only(const char* valstr); static int conf_setdef_ladder_chars_only(void); static int conf_set_difficulty_hack(const char* valstr); static int conf_setdef_difficulty_hack(void); static t_conf_entry prefs_conf_table[] = { { "logfile", conf_set_logfile, NULL, conf_setdef_logfile }, { "logfile-gs", conf_set_logfile_gs, NULL, conf_setdef_logfile_gs }, { "servaddrs", conf_set_servaddrs, NULL, conf_setdef_servaddrs }, { "charsavedir", conf_set_charsave_dir, NULL, conf_setdef_charsave_dir }, { "charinfodir", conf_set_charinfo_dir, NULL, conf_setdef_charinfo_dir }, { "bak_charsavedir", conf_set_charsave_bak_dir, NULL, conf_setdef_charsave_bak_dir }, { "bak_charinfodir", conf_set_charinfo_bak_dir, NULL, conf_setdef_charinfo_bak_dir }, { "ladderdir", conf_set_ladder_dir, NULL, conf_setdef_ladder_dir }, { "gameservlist", conf_set_d2gs_list, NULL, conf_setdef_d2gs_list }, { "laddersave_interval", conf_set_laddersave_interval, NULL, conf_setdef_laddersave_interval }, { "ladderinit_time", conf_set_ladderinit_time, NULL, conf_setdef_ladderinit_time }, { "loglevels", conf_set_loglevels, NULL, conf_setdef_loglevels }, { "pidfile", conf_set_pidfile, NULL, conf_setdef_pidfile }, { "shutdown_delay", conf_set_shutdown_delay, NULL, conf_setdef_shutdown_delay }, { "shutdown_decr", conf_set_shutdown_decr, NULL, conf_setdef_shutdown_decr }, { "idletime", conf_set_idletime, NULL, conf_setdef_idletime }, { "keepalive_interval", conf_set_keepalive_interval, NULL, conf_setdef_keepalive_interval }, { "timeout_checkinterval", conf_set_timeout_checkinterval, NULL, conf_setdef_timeout_checkinterval }, { "XML_ladder_output", conf_set_XML_output_ladder, NULL, conf_setdef_XML_output_ladder }, { "ladderupdate_threshold", conf_set_ladderupdate_threshold, NULL, conf_setdef_ladderupdate_threshold }, { "ladder_chars_only", conf_set_ladder_chars_only, NULL, conf_setdef_ladder_chars_only }, { "difficulty_hack", conf_set_difficulty_hack, NULL, conf_setdef_difficulty_hack }, { NULL, NULL, NULL, NULL } }; extern int d2dbs_prefs_load(char const * filename) { std::FILE *fd; if (!filename) { eventlog(eventlog_level_error, __FUNCTION__, "got NULL filename"); return -1; } fd = std::fopen(filename, "rt"); if (!fd) { eventlog(eventlog_level_error, __FUNCTION__, "could not open file '{}'", filename); return -1; } if (conf_load_file(fd, prefs_conf_table)) { eventlog(eventlog_level_error, __FUNCTION__, "error loading config file '{}'", filename); std::fclose(fd); return -1; } std::fclose(fd); return 0; } extern int d2dbs_prefs_reload(char const * filename) { d2dbs_prefs_unload(); if (d2dbs_prefs_load(filename) < 0) return -1; return 0; } extern int d2dbs_prefs_unload(void) { conf_unload(prefs_conf_table); return 0; } extern char const * d2dbs_prefs_get_servaddrs(void) { return prefs_conf.servaddrs; } static int conf_set_servaddrs(const char* valstr) { return conf_set_str(&prefs_conf.servaddrs, valstr, NULL); } static int conf_setdef_servaddrs(void) { return conf_set_str(&prefs_conf.servaddrs, NULL, D2DBS_SERVER_ADDRS); } extern char const * d2dbs_prefs_get_logfile(void) { return prefs_conf.logfile; } static int conf_set_logfile(const char * valstr) { return conf_set_str(&prefs_conf.logfile, valstr, NULL); } static int conf_setdef_logfile(void) { return conf_set_str(&prefs_conf.logfile, NULL, DEFAULT_LOG_FILE); } extern char const * prefs_get_logfile_gs(void) { return prefs_conf.logfile_gs; } static int conf_set_logfile_gs(const char * valstr) { return conf_set_str(&prefs_conf.logfile_gs, valstr, NULL); } static int conf_setdef_logfile_gs(void) { return conf_set_str(&prefs_conf.logfile_gs, NULL, DEFAULT_LOG_FILE); } extern char const * d2dbs_prefs_get_charsave_dir(void) { return prefs_conf.charsave_dir; } static int conf_set_charsave_dir(const char * valstr) { return conf_set_str(&prefs_conf.charsave_dir, valstr, NULL); } static int conf_setdef_charsave_dir(void) { return conf_set_str(&prefs_conf.charsave_dir, NULL, D2DBS_CHARSAVE_DIR); } extern char const * d2dbs_prefs_get_charinfo_dir(void) { return prefs_conf.charinfo_dir; } static int conf_set_charinfo_dir(const char * valstr) { return conf_set_str(&prefs_conf.charinfo_dir, valstr, NULL); } static int conf_setdef_charinfo_dir(void) { return conf_set_str(&prefs_conf.charinfo_dir, NULL, D2DBS_CHARINFO_DIR); } extern char const * prefs_get_charsave_bak_dir(void) { return prefs_conf.charsave_bak_dir; } static int conf_set_charsave_bak_dir(const char * valstr) { return conf_set_str(&prefs_conf.charsave_bak_dir, valstr, NULL); } static int conf_setdef_charsave_bak_dir(void) { return conf_set_str(&prefs_conf.charsave_bak_dir, NULL, D2DBS_CHARSAVEBAK_DIR); } extern char const * prefs_get_charinfo_bak_dir(void) { return prefs_conf.charinfo_bak_dir; } static int conf_set_charinfo_bak_dir(const char * valstr) { return conf_set_str(&prefs_conf.charinfo_bak_dir, valstr, NULL); } static int conf_setdef_charinfo_bak_dir(void) { return conf_set_str(&prefs_conf.charinfo_bak_dir, NULL, D2DBS_CHARINFOBAK_DIR); } extern char const * d2dbs_prefs_get_ladder_dir(void) { return prefs_conf.ladder_dir; } static int conf_set_ladder_dir(const char * valstr) { return conf_set_str(&prefs_conf.ladder_dir, valstr, NULL); } static int conf_setdef_ladder_dir(void) { return conf_set_str(&prefs_conf.ladder_dir, NULL, D2DBS_LADDER_DIR); } extern char const * d2dbs_prefs_get_d2gs_list(void) { return prefs_conf.d2gs_list; } static int conf_set_d2gs_list(const char * valstr) { return conf_set_str(&prefs_conf.d2gs_list, valstr, NULL); } static int conf_setdef_d2gs_list(void) { return conf_set_str(&prefs_conf.d2gs_list, NULL, D2GS_SERVER_LIST); } extern unsigned int prefs_get_laddersave_interval(void) { return prefs_conf.laddersave_interval; } static int conf_set_laddersave_interval(char const * valstr) { return conf_set_int(&prefs_conf.laddersave_interval, valstr, 0); } static int conf_setdef_laddersave_interval(void) { return conf_set_int(&prefs_conf.laddersave_interval, NULL, 3600); } extern unsigned int prefs_get_ladderinit_time(void) { return prefs_conf.ladderinit_time; } static int conf_set_ladderinit_time(char const * valstr) { return conf_set_int(&prefs_conf.ladderinit_time, valstr, 0); } static int conf_setdef_ladderinit_time(void) { return conf_set_int(&prefs_conf.ladderinit_time, NULL, 0); } extern char const * d2dbs_prefs_get_loglevels(void) { return prefs_conf.loglevels; } static int conf_set_loglevels(const char* valstr) { return conf_set_str(&prefs_conf.loglevels, valstr, NULL); } static int conf_setdef_loglevels(void) { return conf_set_str(&prefs_conf.loglevels, NULL, DEFAULT_LOG_LEVELS); } extern char const * d2dbs_prefs_get_pidfile(void) { return prefs_conf.pidfile; } static int conf_set_pidfile(const char* valstr) { return conf_set_str(&prefs_conf.pidfile, valstr, NULL); } static int conf_setdef_pidfile(void) { return conf_set_str(&prefs_conf.pidfile, NULL, ""); } extern unsigned int d2dbs_prefs_get_shutdown_delay(void) { return prefs_conf.shutdown_delay; } static int conf_set_shutdown_delay(const char * valstr) { return conf_set_int(&prefs_conf.shutdown_delay, valstr, 0); } static int conf_setdef_shutdown_delay(void) { return conf_set_int(&prefs_conf.shutdown_delay, NULL, DEFAULT_SHUTDOWN_DELAY); } extern unsigned int d2dbs_prefs_get_shutdown_decr(void) { return prefs_conf.shutdown_decr; } static int conf_set_shutdown_decr(const char * valstr) { return conf_set_int(&prefs_conf.shutdown_decr, valstr, 0); } static int conf_setdef_shutdown_decr(void) { return conf_set_int(&prefs_conf.shutdown_decr, NULL, DEFAULT_SHUTDOWN_DECR); } extern unsigned int d2dbs_prefs_get_idletime(void) { return prefs_conf.idletime; } static int conf_set_idletime(const char * valstr) { return conf_set_int(&prefs_conf.idletime, valstr, 0); } static int conf_setdef_idletime(void) { return conf_set_int(&prefs_conf.idletime, NULL, DEFAULT_IDLETIME); } extern unsigned int prefs_get_keepalive_interval(void) { return prefs_conf.keepalive_interval; } static int conf_set_keepalive_interval(const char * valstr) { return conf_set_int(&prefs_conf.keepalive_interval, valstr, 0); } static int conf_setdef_keepalive_interval(void) { return conf_set_int(&prefs_conf.keepalive_interval, NULL, DEFAULT_KEEPALIVE_INTERVAL); } extern unsigned int d2dbs_prefs_get_timeout_checkinterval(void) { return prefs_conf.timeout_checkinterval; } static int conf_set_timeout_checkinterval(const char * valstr) { return conf_set_int(&prefs_conf.timeout_checkinterval, valstr, 0); } static int conf_setdef_timeout_checkinterval(void) { return conf_set_int(&prefs_conf.timeout_checkinterval, NULL, DEFAULT_TIMEOUT_CHECKINTERVAL); } extern unsigned int d2dbs_prefs_get_XML_output_ladder(void) { return prefs_conf.XML_output_ladder; } static int conf_set_XML_output_ladder(const char * valstr) { return conf_set_bool(&prefs_conf.XML_output_ladder, valstr, 0); } static int conf_setdef_XML_output_ladder(void) { return conf_set_bool(&prefs_conf.XML_output_ladder, NULL, 0); } extern unsigned int prefs_get_ladderupdate_threshold(void) { return prefs_conf.ladderupdate_threshold; } static int conf_set_ladderupdate_threshold(const char * valstr) { return conf_set_int(&prefs_conf.ladderupdate_threshold, valstr, 0); } static int conf_setdef_ladderupdate_threshold(void) { return conf_set_int(&prefs_conf.ladderupdate_threshold, NULL, DEFAULT_LADDERUPDATE_THRESHOLD); } extern unsigned int prefs_get_ladder_chars_only(void) { return prefs_conf.ladder_chars_only; } static int conf_set_ladder_chars_only(const char * valstr) { return conf_set_bool(&prefs_conf.ladder_chars_only, valstr, 0); } static int conf_setdef_ladder_chars_only(void) { return conf_set_bool(&prefs_conf.ladder_chars_only, NULL, 0); } extern unsigned int prefs_get_difficulty_hack(void) { return prefs_conf.difficulty_hack; } static int conf_set_difficulty_hack(const char * valstr) { return conf_set_int(&prefs_conf.difficulty_hack, valstr, 0); } static int conf_setdef_difficulty_hack(void) { return conf_set_int(&prefs_conf.difficulty_hack, NULL, 0); } } } pvpgn-1.99.7.2.1+dfsg/src/d2dbs/prefs.h000066400000000000000000000044411462640461400172600ustar00rootroot00000000000000/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_PREFS_H #define INCLUDED_PREFS_H namespace pvpgn { namespace d2dbs { extern int d2dbs_prefs_load(char const * filename); extern int d2dbs_prefs_reload(char const * filename); extern int d2dbs_prefs_unload(void); extern char const * d2dbs_prefs_get_logfile(void); extern char const * prefs_get_logfile_gs(void); extern char const * d2dbs_prefs_get_servaddrs(void); extern char const * d2dbs_prefs_get_charsave_dir(void); extern char const * d2dbs_prefs_get_charinfo_dir(void); extern char const * prefs_get_charsave_bak_dir(void); extern char const * prefs_get_charinfo_bak_dir(void); extern char const * d2dbs_prefs_get_ladder_dir(void); extern char const * d2dbs_prefs_get_d2gs_list(void); extern unsigned int prefs_get_laddersave_interval(void); extern unsigned int prefs_get_ladderinit_time(void); extern char const * d2dbs_prefs_get_loglevels(void); extern unsigned int d2dbs_prefs_get_shutdown_delay(void); extern unsigned int d2dbs_prefs_get_shutdown_decr(void); extern unsigned int d2dbs_prefs_get_idletime(void); extern unsigned int prefs_get_keepalive_interval(void); extern unsigned int d2dbs_prefs_get_timeout_checkinterval(void); extern unsigned int d2dbs_prefs_get_XML_output_ladder(void); extern unsigned int prefs_get_ladderupdate_threshold(void); extern unsigned int prefs_get_ladder_chars_only(void); extern unsigned int prefs_get_difficulty_hack(void); extern char const * d2dbs_prefs_get_pidfile(void); } } #endif pvpgn-1.99.7.2.1+dfsg/src/d2dbs/setup.h000066400000000000000000000040071462640461400172770ustar00rootroot00000000000000/* * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INLCUDED_D2DBS_SETUP_H #define INLCUDED_D2DBS_SETUP_H #if !defined(MAX_PATH) && !defined(WIN32) # define MAX_PATH 1024 #endif #define tf(a) ((a)?1:0) #define SELECT_TIME_OUT 20000 constexpr long kBufferSize = 1024L * 20L; #define kMaxPacketLength (1024*5) #define DEFAULT_LOG_FILE "/usr/local/var/d2dbs.std::log" #define DEFAULT_LOG_FILE_GS "/usr/local/var/d2dbs-gs.std::log" #define DEFAULT_LOG_LEVELS "info,warn,error" #define D2DBS_CHARSAVE_DIR "/usr/local/var/charsave" #define D2DBS_CHARINFO_DIR "/usr/local/var/charinfo" #define D2DBS_CHARSAVEBAK_DIR "/usr/local/var/bak/charsave" #define D2DBS_CHARINFOBAK_DIR "/usr/local/var/bak/charinfo" #define D2DBS_LADDER_DIR "/usr/local/var/ladders" #ifndef D2DBS_DEFAULT_CONF_FILE # define D2DBS_DEFAULT_CONF_FILE "conf/d2dbs.conf" #endif #define DEFAULT_MEMLOG_FILE "/tmp/d2dbs-mem.std::log" #define DEFAULT_LISTEN_PORT 6114 #define D2DBS_SERVER_ADDRS "0.0.0.0" #define D2GS_SERVER_LIST "192.168.0.1" #define LOG_LEVEL LOG_MSG #define DEFAULT_GS_MAX 256 #define DEFAULT_SHUTDOWN_DELAY 300 #define DEFAULT_SHUTDOWN_DECR 60 #define DEFAULT_IDLETIME 300 #define DEFAULT_KEEPALIVE_INTERVAL 60 #define DEFAULT_TIMEOUT_CHECKINTERVAL 60 #define DEFAULT_LADDERUPDATE_THRESHOLD 0 #endif pvpgn-1.99.7.2.1+dfsg/src/d2dbs/version.h000066400000000000000000000017141462640461400176260ustar00rootroot00000000000000/* * Copyright (C) 2001 sousou (liupeng.cs@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDED_VERSION_H #define INCLUDED_VERSION_H #define D2DBS_VERSION_NUMBER "1.10.1.1" #define D2DBS_VERSION "D2DBS Version " D2DBS_VERSION_NUMBER " Built " __DATE__ " " __TIME__ #endif pvpgn-1.99.7.2.1+dfsg/src/json/000077500000000000000000000000001462640461400157405ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/json/json.hpp000066400000000000000000022135561462640461400174400ustar00rootroot00000000000000/* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ | | |__ | | | | | | version 3.1.2 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . Copyright (c) 2013-2018 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP #define NLOHMANN_JSON_VERSION_MAJOR 3 #define NLOHMANN_JSON_VERSION_MINOR 1 #define NLOHMANN_JSON_VERSION_PATCH 2 #include // all_of, find, for_each #include // assert #include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list #include // istream, ostream #include // iterator_traits, random_access_iterator_tag #include // accumulate #include // string, stoi, to_string #include // declval, forward, move, pair, swap // #include #ifndef NLOHMANN_JSON_FWD_HPP #define NLOHMANN_JSON_FWD_HPP #include // int64_t, uint64_t #include // map #include // allocator #include // string #include // vector /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @since version 1.0.0 */ namespace nlohmann { /*! @brief default JSONSerializer template argument This serializer ignores the template arguments and uses ADL ([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) for serialization. */ template struct adl_serializer; template class ObjectType = std::map, template class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, template class AllocatorType = std::allocator, template class JSONSerializer = adl_serializer> class basic_json; /*! @brief JSON Pointer A JSON pointer defines a string syntax for identifying a specific value within a JSON document. It can be used with functions `at` and `operator[]`. Furthermore, JSON pointers are the base for JSON patches. @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) @since version 2.0.0 */ template class json_pointer; /*! @brief default JSON class This type is the default specialization of the @ref basic_json class which uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; } #endif // #include // This file contains all internal macro definitions // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them // exclude unsupported compilers #if defined(__clang__) #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" #endif #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" #endif #endif // disable float-equal warnings on GCC/clang #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif // disable documentation warnings on clang #if defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdocumentation" #endif // allow for portable deprecation warnings #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #define JSON_DEPRECATED __attribute__((deprecated)) #elif defined(_MSC_VER) #define JSON_DEPRECATED __declspec(deprecated) #else #define JSON_DEPRECATED #endif // allow to disable exceptions #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try #define JSON_CATCH(exception) catch(exception) #else #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) #endif // override exception macros #if defined(JSON_THROW_USER) #undef JSON_THROW #define JSON_THROW JSON_THROW_USER #endif #if defined(JSON_TRY_USER) #undef JSON_TRY #define JSON_TRY JSON_TRY_USER #endif #if defined(JSON_CATCH_USER) #undef JSON_CATCH #define JSON_CATCH JSON_CATCH_USER #endif // manual branch prediction #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) #else #define JSON_LIKELY(x) x #define JSON_UNLIKELY(x) x #endif // C++ language standard detection #if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. #define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ template class ObjectType, \ template class ArrayType, \ class StringType, class BooleanType, class NumberIntegerType, \ class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ template class JSONSerializer> #define NLOHMANN_BASIC_JSON_TPL \ basic_json /*! @brief Helper to determine whether there's a key_type for T. This helper is used to tell associative containers apart from other containers such as sequence containers. For instance, `std::map` passes the test as it contains a `mapped_type`, whereas `std::vector` fails the test. @sa http://stackoverflow.com/a/7728728/266378 @since version 1.0.0, overworked in version 2.0.6 */ #define NLOHMANN_JSON_HAS_HELPER(type) \ template struct has_##type { \ private: \ template \ static int detect(U &&); \ static void detect(...); \ public: \ static constexpr bool value = \ std::is_integral()))>::value; \ } // #include #include // not #include // size_t #include // numeric_limits #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type #include // declval // #include // #include namespace nlohmann { /*! @brief detail namespace with internal helper functions This namespace collects functions that should not be exposed, implementations of some @ref basic_json methods, and meta-programming helpers. @since version 2.1.0 */ namespace detail { ///////////// // helpers // ///////////// template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; // alias templates to reduce boilerplate template using enable_if_t = typename std::enable_if::type; template using uncvref_t = typename std::remove_cv::type>::type; // implementation of C++14 index_sequence and affiliates // source: https://stackoverflow.com/a/32223343 template struct index_sequence { using type = index_sequence; using value_type = std::size_t; static constexpr std::size_t size() noexcept { return sizeof...(Ints); } }; template struct merge_and_renumber; template struct merge_and_renumber, index_sequence> : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; template struct make_index_sequence : merge_and_renumber < typename make_index_sequence < N / 2 >::type, typename make_index_sequence < N - N / 2 >::type > {}; template<> struct make_index_sequence<0> : index_sequence<> {}; template<> struct make_index_sequence<1> : index_sequence<0> {}; template using index_sequence_for = make_index_sequence; /* Implementation of two C++17 constructs: conjunction, negation. This is needed to avoid evaluating all the traits in a condition For example: not std::is_same::value and has_value_type::value will not compile when T = void (on MSVC at least). Whereas conjunction>, has_value_type>::value will stop evaluating if negation<...>::value == false Please note that those constructs must be used with caution, since symbols can become very long quickly (which can slow down compilation and cause MSVC internal compiler errors). Only use it when you have to (see example ahead). */ template struct conjunction : std::true_type {}; template struct conjunction : B1 {}; template struct conjunction : std::conditional, B1>::type {}; template struct negation : std::integral_constant {}; // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; //////////////////////// // has_/is_ functions // //////////////////////// // source: https://stackoverflow.com/a/37193089/4116453 template struct is_complete_type : std::false_type {}; template struct is_complete_type : std::true_type {}; NLOHMANN_JSON_HAS_HELPER(mapped_type); NLOHMANN_JSON_HAS_HELPER(key_type); NLOHMANN_JSON_HAS_HELPER(value_type); NLOHMANN_JSON_HAS_HELPER(iterator); template struct is_compatible_object_type_impl : std::false_type {}; template struct is_compatible_object_type_impl { static constexpr auto value = std::is_constructible::value and std::is_constructible::value; }; template struct is_compatible_object_type { static auto constexpr value = is_compatible_object_type_impl < conjunction>, has_mapped_type, has_key_type>::value, typename BasicJsonType::object_t, CompatibleObjectType >::value; }; template struct is_basic_json_nested_type { static auto constexpr value = std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value; }; template struct is_compatible_array_type { static auto constexpr value = conjunction>, negation>, negation>, negation>, has_value_type, has_iterator>::value; }; template struct is_compatible_integer_type_impl : std::false_type {}; template struct is_compatible_integer_type_impl { // is there an assert somewhere on overflows? using RealLimits = std::numeric_limits; using CompatibleLimits = std::numeric_limits; static constexpr auto value = std::is_constructible::value and CompatibleLimits::is_integer and RealLimits::is_signed == CompatibleLimits::is_signed; }; template struct is_compatible_integer_type { static constexpr auto value = is_compatible_integer_type_impl < std::is_integral::value and not std::is_same::value, RealIntegerType, CompatibleNumberIntegerType > ::value; }; // trait checking if JSONSerializer::from_json(json const&, udt&) exists template struct has_from_json { private: // also check the return type of from_json template::from_json( std::declval(), std::declval()))>::value>> static int detect(U&&); static void detect(...); public: static constexpr bool value = std::is_integral>()))>::value; }; // This trait checks if JSONSerializer::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types template struct has_non_default_from_json { private: template < typename U, typename = enable_if_t::from_json(std::declval()))>::value >> static int detect(U&&); static void detect(...); public: static constexpr bool value = std::is_integral>()))>::value; }; // This trait checks if BasicJsonType::json_serializer::to_json exists template struct has_to_json { private: template::to_json( std::declval(), std::declval()))> static int detect(U&&); static void detect(...); public: static constexpr bool value = std::is_integral>()))>::value; }; template struct is_compatible_complete_type { static constexpr bool value = not std::is_base_of::value and not is_basic_json::value and not is_basic_json_nested_type::value and has_to_json::value; }; template struct is_compatible_type : conjunction, is_compatible_complete_type> { }; // taken from ranges-v3 template struct static_const { static constexpr T value{}; }; template constexpr T static_const::value; } } // #include #include // exception #include // runtime_error #include // to_string namespace nlohmann { namespace detail { //////////////// // exceptions // //////////////// /*! @brief general exception of the @ref basic_json class This class is an extension of `std::exception` objects with a member @a id for exception ids. It is used as the base class for all exceptions thrown by the @ref basic_json class. This class can hence be used as "wildcard" to catch exceptions. Subclasses: - @ref parse_error for exceptions indicating a parse error - @ref invalid_iterator for exceptions indicating errors with iterators - @ref type_error for exceptions indicating executing a member function with a wrong type - @ref out_of_range for exceptions indicating access out of the defined range - @ref other_error for exceptions indicating other library errors @internal @note To have nothrow-copy-constructible exceptions, we internally use `std::runtime_error` which can cope with arbitrary-length error messages. Intermediate strings are built with static functions and then passed to the actual constructor. @endinternal @liveexample{The following code shows how arbitrary library exceptions can be caught.,exception} @since version 3.0.0 */ class exception : public std::exception { public: /// returns the explanatory string const char* what() const noexcept override { return m.what(); } /// the id of the exception const int id; protected: exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id_) { return "[json.exception." + ename + "." + std::to_string(id_) + "] "; } private: /// an exception object as storage for error messages std::runtime_error m; }; /*! @brief exception indicating a parse error This exception is thrown by the library when a parse error occurs. Parse errors can occur during the deserialization of JSON text, CBOR, MessagePack, as well as when using JSON Patch. Member @a byte holds the byte index of the last read character in the input file. Exceptions have ids 1xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also holds true when reading a byte vector (CBOR or MessagePack). @liveexample{The following code shows how a `parse_error` exception can be caught.,parse_error} @sa @ref exception for the base class of the library exceptions @sa @ref invalid_iterator for exceptions indicating errors with iterators @sa @ref type_error for exceptions indicating executing a member function with a wrong type @sa @ref out_of_range for exceptions indicating access out of the defined range @sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class parse_error : public exception { public: /*! @brief create a parse error exception @param[in] id_ the id of the exception @param[in] byte_ the byte index where the error occurred (or 0 if the position cannot be determined) @param[in] what_arg the explanatory string @return parse_error object */ static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) { std::string w = exception::name("parse_error", id_) + "parse error" + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + ": " + what_arg; return parse_error(id_, byte_, w.c_str()); } /*! @brief byte index of the parse error The byte index of the last read character in the input file. @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also holds true when reading a byte vector (CBOR or MessagePack). */ const std::size_t byte; private: parse_error(int id_, std::size_t byte_, const char* what_arg) : exception(id_, what_arg), byte(byte_) {} }; /*! @brief exception indicating errors with iterators This exception is thrown if iterators passed to a library function do not match the expected semantics. Exceptions have ids 2xx. name / id | example message | description ----------------------------------- | --------------- | ------------------------- json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). @liveexample{The following code shows how an `invalid_iterator` exception can be caught.,invalid_iterator} @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error @sa @ref type_error for exceptions indicating executing a member function with a wrong type @sa @ref out_of_range for exceptions indicating access out of the defined range @sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class invalid_iterator : public exception { public: static invalid_iterator create(int id_, const std::string& what_arg) { std::string w = exception::name("invalid_iterator", id_) + what_arg; return invalid_iterator(id_, w.c_str()); } private: invalid_iterator(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating executing a member function with a wrong type This exception is thrown in case of a type error; that is, a library function is executed on a JSON value whose type does not match the expected semantics. Exceptions have ids 3xx. name / id | example message | description ----------------------------- | --------------- | ------------------------- json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | @liveexample{The following code shows how a `type_error` exception can be caught.,type_error} @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error @sa @ref invalid_iterator for exceptions indicating errors with iterators @sa @ref out_of_range for exceptions indicating access out of the defined range @sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class type_error : public exception { public: static type_error create(int id_, const std::string& what_arg) { std::string w = exception::name("type_error", id_) + what_arg; return type_error(id_, w.c_str()); } private: type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating access out of the defined range This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance in case of array indices or nonexisting object keys. Exceptions have ids 4xx. name / id | example message | description ------------------------------- | --------------- | ------------------------- json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. | json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | @liveexample{The following code shows how an `out_of_range` exception can be caught.,out_of_range} @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error @sa @ref invalid_iterator for exceptions indicating errors with iterators @sa @ref type_error for exceptions indicating executing a member function with a wrong type @sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class out_of_range : public exception { public: static out_of_range create(int id_, const std::string& what_arg) { std::string w = exception::name("out_of_range", id_) + what_arg; return out_of_range(id_, w.c_str()); } private: out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} }; /*! @brief exception indicating other library errors This exception is thrown in case of errors that cannot be classified with the other exception types. Exceptions have ids 5xx. name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. @sa @ref exception for the base class of the library exceptions @sa @ref parse_error for exceptions indicating a parse error @sa @ref invalid_iterator for exceptions indicating errors with iterators @sa @ref type_error for exceptions indicating executing a member function with a wrong type @sa @ref out_of_range for exceptions indicating access out of the defined range @liveexample{The following code shows how an `other_error` exception can be caught.,other_error} @since version 3.0.0 */ class other_error : public exception { public: static other_error create(int id_, const std::string& what_arg) { std::string w = exception::name("other_error", id_) + what_arg; return other_error(id_, w.c_str()); } private: other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; } } // #include #include // array #include // and #include // size_t #include // uint8_t namespace nlohmann { namespace detail { /////////////////////////// // JSON type enumeration // /////////////////////////// /*! @brief the JSON type enumeration This enumeration collects the different JSON types. It is internally used to distinguish the stored values, and the functions @ref basic_json::is_null(), @ref basic_json::is_object(), @ref basic_json::is_array(), @ref basic_json::is_string(), @ref basic_json::is_boolean(), @ref basic_json::is_number() (with @ref basic_json::is_number_integer(), @ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), @ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and @ref basic_json::is_structured() rely on it. @note There are three enumeration entries (number_integer, number_unsigned, and number_float), because the library distinguishes these three types for numbers: @ref basic_json::number_unsigned_t is used for unsigned integers, @ref basic_json::number_integer_t is used for signed integers, and @ref basic_json::number_float_t is used for floating-point numbers or to approximate integers which do not fit in the limits of their respective type. @sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON value with the default value for a given type @since version 1.0.0 */ enum class value_t : std::uint8_t { null, ///< null value object, ///< object (unordered set of name/value pairs) array, ///< array (ordered collection of values) string, ///< string value boolean, ///< boolean value number_integer, ///< number value (signed integer) number_unsigned, ///< number value (unsigned integer) number_float, ///< number value (floating-point) discarded ///< discarded by the the parser callback function }; /*! @brief comparison operator for JSON types Returns an ordering that is similar to Python: - order: null < boolean < number < object < array < string - furthermore, each type is not smaller than itself - discarded values are not comparable @since version 1.0.0 */ inline bool operator<(const value_t lhs, const value_t rhs) noexcept { static constexpr std::array order = {{ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */ } }; const auto l_index = static_cast(lhs); const auto r_index = static_cast(rhs); return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index]; } } } // #include #include // transform #include // array #include // and, not #include // forward_list #include // inserter, front_inserter, end #include // string #include // tuple, make_tuple #include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible #include // pair, declval #include // valarray // #include // #include // #include // #include namespace nlohmann { namespace detail { // overloads for basic_json template parameters template::value and not std::is_same::value, int> = 0> void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) { case value_t::number_unsigned: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_integer: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_float: { val = static_cast(*j.template get_ptr()); break; } default: JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } } template void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (JSON_UNLIKELY(not j.is_boolean())) { JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); } b = *j.template get_ptr(); } template void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (JSON_UNLIKELY(not j.is_string())) { JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr(); } template void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) { get_arithmetic_value(j, val); } template void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) { get_arithmetic_value(j, val); } template void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) { get_arithmetic_value(j, val); } template::value, int> = 0> void from_json(const BasicJsonType& j, EnumType& e) { typename std::underlying_type::type val; get_arithmetic_value(j, val); e = static_cast(val); } template void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) { if (JSON_UNLIKELY(not j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } arr = *j.template get_ptr(); } // forward_list doesn't have an insert method template::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list& l) { if (JSON_UNLIKELY(not j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } std::transform(j.rbegin(), j.rend(), std::front_inserter(l), [](const BasicJsonType & i) { return i.template get(); }); } // valarray doesn't have an insert method template::value, int> = 0> void from_json(const BasicJsonType& j, std::valarray& l) { if (JSON_UNLIKELY(not j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } l.resize(j.size()); std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l)); } template void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/) { using std::end; std::transform(j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i) { // get() returns *this, this won't call a from_json // method when value_type is BasicJsonType return i.template get(); }); } template auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( arr.reserve(std::declval()), void()) { using std::end; arr.reserve(j.size()); std::transform(j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i) { // get() returns *this, this won't call a from_json // method when value_type is BasicJsonType return i.template get(); }); } template void from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) { for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); } } template < typename BasicJsonType, typename CompatibleArrayType, enable_if_t < is_compatible_array_type::value and not std::is_same::value and std::is_constructible < BasicJsonType, typename CompatibleArrayType::value_type >::value, int > = 0 > void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { if (JSON_UNLIKELY(not j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } from_json_array_impl(j, arr, priority_tag<2> {}); } template::value, int> = 0> void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { if (JSON_UNLIKELY(not j.is_object())) { JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); } auto inner_object = j.template get_ptr(); using value_type = typename CompatibleObjectType::value_type; std::transform( inner_object->begin(), inner_object->end(), std::inserter(obj, obj.begin()), [](typename BasicJsonType::object_t::value_type const & p) { return value_type(p.first, p.second.template get()); }); } // overload for arithmetic types, not chosen for basic_json template arguments // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? template::value and not std::is_same::value and not std::is_same::value and not std::is_same::value and not std::is_same::value, int> = 0> void from_json(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) { case value_t::number_unsigned: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_integer: { val = static_cast(*j.template get_ptr()); break; } case value_t::number_float: { val = static_cast(*j.template get_ptr()); break; } case value_t::boolean: { val = static_cast(*j.template get_ptr()); break; } default: JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } } template void from_json(const BasicJsonType& j, std::pair& p) { p = {j.at(0).template get(), j.at(1).template get()}; } template void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence) { t = std::make_tuple(j.at(Idx).template get::type>()...); } template void from_json(const BasicJsonType& j, std::tuple& t) { from_json_tuple_impl(j, t, index_sequence_for {}); } struct from_json_fn { private: template auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const noexcept(noexcept(from_json(j, val))) -> decltype(from_json(j, val), void()) { return from_json(j, val); } template void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept { static_assert(sizeof(BasicJsonType) == 0, "could not find from_json() method in T's namespace"); #ifdef _MSC_VER // MSVC does not show a stacktrace for the above assert using decayed = uncvref_t; static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, "forcing MSVC stacktrace to show which T we're talking about."); #endif } public: template void operator()(const BasicJsonType& j, T& val) const noexcept(noexcept(std::declval().call(j, val, priority_tag<1> {}))) { return call(j, val, priority_tag<1> {}); } }; } /// namespace to hold default `from_json` function /// to see why this is required: /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html namespace { constexpr const auto& from_json = detail::static_const::value; } } // #include #include // or, and, not #include // begin, end #include // tuple, get #include // is_same, is_constructible, is_floating_point, is_enum, underlying_type #include // move, forward, declval, pair #include // valarray #include // vector // #include // #include namespace nlohmann { namespace detail { ////////////////// // constructors // ////////////////// template struct external_constructor; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept { j.m_type = value_t::boolean; j.m_value = b; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) { j.m_type = value_t::string; j.m_value = s; j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) { j.m_type = value_t::string; j.m_value = std::move(s); j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { j.m_type = value_t::number_float; j.m_value = val; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept { j.m_type = value_t::number_unsigned; j.m_value = val; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept { j.m_type = value_t::number_integer; j.m_value = val; j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) { j.m_type = value_t::array; j.m_value = arr; j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { j.m_type = value_t::array; j.m_value = std::move(arr); j.assert_invariant(); } template::value, int> = 0> static void construct(BasicJsonType& j, const CompatibleArrayType& arr) { using std::begin; using std::end; j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); j.assert_invariant(); } template static void construct(BasicJsonType& j, const std::vector& arr) { j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->reserve(arr.size()); for (const bool x : arr) { j.m_value.array->push_back(x); } j.assert_invariant(); } template::value, int> = 0> static void construct(BasicJsonType& j, const std::valarray& arr) { j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->resize(arr.size()); std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); j.assert_invariant(); } }; template<> struct external_constructor { template static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) { j.m_type = value_t::object; j.m_value = obj; j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { j.m_type = value_t::object; j.m_value = std::move(obj); j.assert_invariant(); } template::value, int> = 0> static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; using std::end; j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); j.assert_invariant(); } }; ///////////// // to_json // ///////////// template::value, int> = 0> void to_json(BasicJsonType& j, T b) noexcept { external_constructor::construct(j, b); } template::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleString& s) { external_constructor::construct(j, s); } template void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) { external_constructor::construct(j, std::move(s)); } template::value, int> = 0> void to_json(BasicJsonType& j, FloatType val) noexcept { external_constructor::construct(j, static_cast(val)); } template::value, int> = 0> void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept { external_constructor::construct(j, static_cast(val)); } template::value, int> = 0> void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept { external_constructor::construct(j, static_cast(val)); } template::value, int> = 0> void to_json(BasicJsonType& j, EnumType e) noexcept { using underlying_type = typename std::underlying_type::type; external_constructor::construct(j, static_cast(e)); } template void to_json(BasicJsonType& j, const std::vector& e) { external_constructor::construct(j, e); } template::value or std::is_same::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { external_constructor::construct(j, arr); } template::value, int> = 0> void to_json(BasicJsonType& j, std::valarray arr) { external_constructor::construct(j, std::move(arr)); } template void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { external_constructor::construct(j, std::move(arr)); } template::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor::construct(j, obj); } template void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { external_constructor::construct(j, std::move(obj)); } template::value, int> = 0> void to_json(BasicJsonType& j, T (&arr)[N]) { external_constructor::construct(j, arr); } template void to_json(BasicJsonType& j, const std::pair& p) { j = {p.first, p.second}; } template void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence) { j = {std::get(t)...}; } template void to_json(BasicJsonType& j, const std::tuple& t) { to_json_tuple_impl(j, t, index_sequence_for {}); } struct to_json_fn { private: template auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward(val)))) -> decltype(to_json(j, std::forward(val)), void()) { return to_json(j, std::forward(val)); } template void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept { static_assert(sizeof(BasicJsonType) == 0, "could not find to_json() method in T's namespace"); #ifdef _MSC_VER // MSVC does not show a stacktrace for the above assert using decayed = uncvref_t; static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, "forcing MSVC stacktrace to show which T we're talking about."); #endif } public: template void operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1> {}))) { return call(j, std::forward(val), priority_tag<1> {}); } }; } /// namespace to hold default `to_json` function namespace { constexpr const auto& to_json = detail::static_const::value; } } // #include #include // min #include // array #include // assert #include // size_t #include // strlen #include // streamsize, streamoff, streampos #include // istream #include // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include // shared_ptr, make_shared, addressof #include // accumulate #include // string, char_traits #include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include // pair, declval // #include namespace nlohmann { namespace detail { //////////////////// // input adapters // //////////////////// /*! @brief abstract input adapter interface Produces a stream of std::char_traits::int_type characters from a std::istream, a buffer, or some other input type. Accepts the return of exactly one non-EOF character for future input. The int_type characters returned consist of all valid char values as positive values (typically unsigned char), plus an EOF value outside that range, specified by the value of the function std::char_traits::eof(). This value is typically -1, but could be any arbitrary value which is not a valid char value. */ struct input_adapter_protocol { /// get a character [0,255] or std::char_traits::eof(). virtual std::char_traits::int_type get_character() = 0; /// restore the last non-eof() character to input virtual void unget_character() = 0; virtual ~input_adapter_protocol() = default; }; /// a type to simplify interfaces using input_adapter_t = std::shared_ptr; /*! Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at beginning of input. Does not support changing the underlying std::streambuf in mid-input. Maintains underlying std::istream and std::streambuf to support subsequent use of standard std::istream operations to process any input characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ class input_stream_adapter : public input_adapter_protocol { public: ~input_stream_adapter() override { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags is.clear(); } explicit input_stream_adapter(std::istream& i) : is(i), sb(*i.rdbuf()) { // skip byte order mark std::char_traits::int_type c; if ((c = get_character()) == 0xEF) { if ((c = get_character()) == 0xBB) { if ((c = get_character()) == 0xBF) { return; // Ignore BOM } else if (c != std::char_traits::eof()) { is.unget(); } is.putback('\xBB'); } else if (c != std::char_traits::eof()) { is.unget(); } is.putback('\xEF'); } else if (c != std::char_traits::eof()) { is.unget(); // no byte order mark; process as usual } } // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not // end up as the same value, eg. 0xFFFFFFFF. std::char_traits::int_type get_character() override { return sb.sbumpc(); } void unget_character() override { sb.sungetc(); // is.unget() avoided for performance } private: /// the associated input stream std::istream& is; std::streambuf& sb; }; /// input adapter for buffer input class input_buffer_adapter : public input_adapter_protocol { public: input_buffer_adapter(const char* b, const std::size_t l) : cursor(b), limit(b + l), start(b) { // skip byte order mark if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF') { cursor += 3; } } // delete because of pointer members input_buffer_adapter(const input_buffer_adapter&) = delete; input_buffer_adapter& operator=(input_buffer_adapter&) = delete; std::char_traits::int_type get_character() noexcept override { if (JSON_LIKELY(cursor < limit)) { return std::char_traits::to_int_type(*(cursor++)); } return std::char_traits::eof(); } void unget_character() noexcept override { if (JSON_LIKELY(cursor > start)) { --cursor; } } private: /// pointer to the current character const char* cursor; /// pointer past the last character const char* limit; /// pointer to the first character const char* start; }; class input_adapter { public: // native support /// input adapter for input stream input_adapter(std::istream& i) : ia(std::make_shared(i)) {} /// input adapter for input stream input_adapter(std::istream&& i) : ia(std::make_shared(i)) {} /// input adapter for buffer template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> input_adapter(CharT b, std::size_t l) : ia(std::make_shared(reinterpret_cast(b), l)) {} // derived support /// input adapter for string literal template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> input_adapter(CharT b) : input_adapter(reinterpret_cast(b), std::strlen(reinterpret_cast(b))) {} /// input adapter for iterator range with contiguous storage template::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> input_adapter(IteratorType first, IteratorType last) { // assertion to check that the iterator range is indeed contiguous, // see http://stackoverflow.com/a/35008842/266378 for more discussion assert(std::accumulate( first, last, std::pair(true, 0), [&first](std::pair res, decltype(*first) val) { res.first &= (val == *(std::next(std::addressof(*first), res.second++))); return res; }).first); // assertion to check that each element is 1 byte long static_assert( sizeof(typename std::iterator_traits::value_type) == 1, "each element in the iterator range must have the size of 1 byte"); const auto len = static_cast(std::distance(first, last)); if (JSON_LIKELY(len > 0)) { // there is at least one element: use the address of first ia = std::make_shared(reinterpret_cast(&(*first)), len); } else { // the address of first cannot be used: use nullptr ia = std::make_shared(nullptr, len); } } /// input adapter for array template input_adapter(T (&array)[N]) : input_adapter(std::begin(array), std::end(array)) {} /// input adapter for contiguous container template::value and std::is_base_of()))>::iterator_category>::value, int>::type = 0> input_adapter(const ContiguousContainer& c) : input_adapter(std::begin(c), std::end(c)) {} operator input_adapter_t() { return ia; } private: /// the actual adapter input_adapter_t ia = nullptr; }; } } // #include #include // localeconv #include // size_t #include // strtof, strtod, strtold, strtoll, strtoull #include // initializer_list #include // hex, uppercase #include // setw, setfill #include // stringstream #include // char_traits, string #include // vector // #include // #include namespace nlohmann { namespace detail { /////////// // lexer // /////////// /*! @brief lexical analysis This class organizes the lexical analysis during JSON deserialization. */ template class lexer { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; public: /// token types for the parser enum class token_type { uninitialized, ///< indicating the scanner is uninitialized literal_true, ///< the `true` literal literal_false, ///< the `false` literal literal_null, ///< the `null` literal value_string, ///< a string -- use get_string() for actual value value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value value_integer, ///< a signed integer -- use get_number_integer() for actual value value_float, ///< an floating point number -- use get_number_float() for actual value begin_array, ///< the character for array begin `[` begin_object, ///< the character for object begin `{` end_array, ///< the character for array end `]` end_object, ///< the character for object end `}` name_separator, ///< the name separator `:` value_separator, ///< the value separator `,` parse_error, ///< indicating a parse error end_of_input, ///< indicating the end of the input buffer literal_or_value ///< a literal or the begin of a value (only for diagnostics) }; /// return name of values of type token_type (only used for errors) static const char* token_type_name(const token_type t) noexcept { switch (t) { case token_type::uninitialized: return ""; case token_type::literal_true: return "true literal"; case token_type::literal_false: return "false literal"; case token_type::literal_null: return "null literal"; case token_type::value_string: return "string literal"; case lexer::token_type::value_unsigned: case lexer::token_type::value_integer: case lexer::token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; case token_type::begin_object: return "'{'"; case token_type::end_array: return "']'"; case token_type::end_object: return "'}'"; case token_type::name_separator: return "':'"; case token_type::value_separator: return "','"; case token_type::parse_error: return ""; case token_type::end_of_input: return "end of input"; case token_type::literal_or_value: return "'[', '{', or a literal"; default: // catch non-enum values return "unknown token"; // LCOV_EXCL_LINE } } explicit lexer(detail::input_adapter_t adapter) : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} // delete because of pointer members lexer(const lexer&) = delete; lexer& operator=(lexer&) = delete; private: ///////////////////// // locales ///////////////////// /// return the locale-dependent decimal point static char get_decimal_point() noexcept { const auto loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } ///////////////////// // scan functions ///////////////////// /*! @brief get codepoint from 4 hex characters following `\u` For input "\u c1 c2 c3 c4" the codepoint is: (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The conversion is done by subtracting the offset (0x30, 0x37, and 0x57) between the ASCII value of the character and the desired integer value. @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or non-hex character) */ int get_codepoint() { // this function only makes sense after reading `\u` assert(current == 'u'); int codepoint = 0; const auto factors = { 12, 8, 4, 0 }; for (const auto factor : factors) { get(); if (current >= '0' and current <= '9') { codepoint += ((current - 0x30) << factor); } else if (current >= 'A' and current <= 'F') { codepoint += ((current - 0x37) << factor); } else if (current >= 'a' and current <= 'f') { codepoint += ((current - 0x57) << factor); } else { return -1; } } assert(0x0000 <= codepoint and codepoint <= 0xFFFF); return codepoint; } /*! @brief check if the next byte(s) are inside a given range Adds the current byte and, for each passed range, reads a new byte and checks if it is inside the range. If a violation was detected, set up an error message and return false. Otherwise, return true. @param[in] ranges list of integers; interpreted as list of pairs of inclusive lower and upper bound, respectively @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, 1, 2, or 3 pairs. This precondition is enforced by an assertion. @return true if and only if no range violation was detected */ bool next_byte_in_range(std::initializer_list ranges) { assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); add(current); for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); if (JSON_LIKELY(*range <= current and current <= *(++range))) { add(current); } else { error_message = "invalid string: ill-formed UTF-8 byte"; return false; } } return true; } /*! @brief scan a string literal This function scans a string according to Sect. 7 of RFC 7159. While scanning, bytes are escaped and copied into buffer token_buffer. Then the function returns successfully, token_buffer is *not* null-terminated (as it may contain \0 bytes), and token_buffer.size() is the number of bytes in the string. @return token_type::value_string if string could be successfully scanned, token_type::parse_error otherwise @note In case of errors, variable error_message contains a textual description. */ token_type scan_string() { // reset token_buffer (ignore opening quote) reset(); // we entered the function by reading an open quote assert(current == '\"'); while (true) { // get next character switch (get()) { // end of file while parsing string case std::char_traits::eof(): { error_message = "invalid string: missing closing quote"; return token_type::parse_error; } // closing quote case '\"': { return token_type::value_string; } // escapes case '\\': { switch (get()) { // quotation mark case '\"': add('\"'); break; // reverse solidus case '\\': add('\\'); break; // solidus case '/': add('/'); break; // backspace case 'b': add('\b'); break; // form feed case 'f': add('\f'); break; // line feed case 'n': add('\n'); break; // carriage return case 'r': add('\r'); break; // tab case 't': add('\t'); break; // unicode escapes case 'u': { const int codepoint1 = get_codepoint(); int codepoint = codepoint1; // start with codepoint1 if (JSON_UNLIKELY(codepoint1 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if code point is a high surrogate if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) { // expect next \uxxxx entry if (JSON_LIKELY(get() == '\\' and get() == 'u')) { const int codepoint2 = get_codepoint(); if (JSON_UNLIKELY(codepoint2 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if codepoint2 is a low surrogate if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) { // overwrite codepoint codepoint = // high surrogate occupies the most significant 22 bits (codepoint1 << 10) // low surrogate occupies the least significant 15 bits + codepoint2 // there is still the 0xD800, 0xDC00 and 0x10000 noise // in the result so we have to subtract with: // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - 0x35FDC00; } else { error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) { error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; return token_type::parse_error; } } // result of the above calculation yields a proper codepoint assert(0x00 <= codepoint and codepoint <= 0x10FFFF); // translate codepoint into bytes if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) add(codepoint); } else if (codepoint <= 0x7FF) { // 2-byte characters: 110xxxxx 10xxxxxx add(0xC0 | (codepoint >> 6)); add(0x80 | (codepoint & 0x3F)); } else if (codepoint <= 0xFFFF) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx add(0xE0 | (codepoint >> 12)); add(0x80 | ((codepoint >> 6) & 0x3F)); add(0x80 | (codepoint & 0x3F)); } else { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx add(0xF0 | (codepoint >> 18)); add(0x80 | ((codepoint >> 12) & 0x3F)); add(0x80 | ((codepoint >> 6) & 0x3F)); add(0x80 | (codepoint & 0x3F)); } break; } // other characters after escape default: error_message = "invalid string: forbidden character after backslash"; return token_type::parse_error; } break; } // invalid control characters case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: { error_message = "invalid string: control character must be escaped"; return token_type::parse_error; } // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) case 0x20: case 0x21: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5D: case 0x5E: case 0x5F: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: { add(current); break; } // U+0080..U+07FF: bytes C2..DF 80..BF case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7: case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7: case 0xD8: case 0xD9: case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE: case 0xDF: { if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) { return token_type::parse_error; } break; } // U+0800..U+0FFF: bytes E0 A0..BF 80..BF case 0xE0: { if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8: case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xEE: case 0xEF: { if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+D000..U+D7FF: bytes ED 80..9F 80..BF case 0xED: { if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF case 0xF0: { if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF case 0xF1: case 0xF2: case 0xF3: { if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF case 0xF4: { if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } break; } // remaining bytes (80..C1 and F5..FF) are ill-formed default: { error_message = "invalid string: ill-formed UTF-8 byte"; return token_type::parse_error; } } } } static void strtof(float& f, const char* str, char** endptr) noexcept { f = std::strtof(str, endptr); } static void strtof(double& f, const char* str, char** endptr) noexcept { f = std::strtod(str, endptr); } static void strtof(long double& f, const char* str, char** endptr) noexcept { f = std::strtold(str, endptr); } /*! @brief scan a number literal This function scans a string according to Sect. 6 of RFC 7159. The function is realized with a deterministic finite state machine derived from the grammar described in RFC 7159. Starting in state "init", the input is read and used to determined the next state. Only state "done" accepts the number. State "error" is a trap state to model errors. In the table below, "anything" means any character but the ones listed before. state | 0 | 1-9 | e E | + | - | . | anything ---------|----------|----------|----------|---------|---------|----------|----------- init | zero | any1 | [error] | [error] | minus | [error] | [error] minus | zero | any1 | [error] | [error] | [error] | [error] | [error] zero | done | done | exponent | done | done | decimal1 | done any1 | any1 | any1 | exponent | done | done | decimal1 | done decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] decimal2 | decimal2 | decimal2 | exponent | done | done | done | done exponent | any2 | any2 | [error] | sign | sign | [error] | [error] sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] any2 | any2 | any2 | done | done | done | done | done The state machine is realized with one label per state (prefixed with "scan_number_") and `goto` statements between them. The state machine contains cycles, but any cycle can be left when EOF is read. Therefore, the function is guaranteed to terminate. During scanning, the read bytes are stored in token_buffer. This string is then converted to a signed integer, an unsigned integer, or a floating-point number. @return token_type::value_unsigned, token_type::value_integer, or token_type::value_float if number could be successfully scanned, token_type::parse_error otherwise @note The scanner is independent of the current locale. Internally, the locale's decimal point is used instead of `.` to work with the locale-dependent converters. */ token_type scan_number() { // reset token_buffer to store the number's bytes reset(); // the type of the parsed number; initially set to unsigned; will be // changed if minus sign, decimal point or exponent is read token_type number_type = token_type::value_unsigned; // state (init): we just found out we need to scan a number switch (current) { case '-': { add(current); goto scan_number_minus; } case '0': { add(current); goto scan_number_zero; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any1; } default: { // all other characters are rejected outside scan_number() assert(false); // LCOV_EXCL_LINE } } scan_number_minus: // state: we just parsed a leading minus sign number_type = token_type::value_integer; switch (get()) { case '0': { add(current); goto scan_number_zero; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any1; } default: { error_message = "invalid number; expected digit after '-'"; return token_type::parse_error; } } scan_number_zero: // state: we just parse a zero (maybe with a leading minus sign) switch (get()) { case '.': { add(decimal_point_char); goto scan_number_decimal1; } case 'e': case 'E': { add(current); goto scan_number_exponent; } default: goto scan_number_done; } scan_number_any1: // state: we just parsed a number 0-9 (maybe with a leading minus sign) switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any1; } case '.': { add(decimal_point_char); goto scan_number_decimal1; } case 'e': case 'E': { add(current); goto scan_number_exponent; } default: goto scan_number_done; } scan_number_decimal1: // state: we just parsed a decimal point number_type = token_type::value_float; switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_decimal2; } default: { error_message = "invalid number; expected digit after '.'"; return token_type::parse_error; } } scan_number_decimal2: // we just parsed at least one number after a decimal point switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_decimal2; } case 'e': case 'E': { add(current); goto scan_number_exponent; } default: goto scan_number_done; } scan_number_exponent: // we just parsed an exponent number_type = token_type::value_float; switch (get()) { case '+': case '-': { add(current); goto scan_number_sign; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any2; } default: { error_message = "invalid number; expected '+', '-', or digit after exponent"; return token_type::parse_error; } } scan_number_sign: // we just parsed an exponent sign switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any2; } default: { error_message = "invalid number; expected digit after exponent sign"; return token_type::parse_error; } } scan_number_any2: // we just parsed a number after the exponent or exponent sign switch (get()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { add(current); goto scan_number_any2; } default: goto scan_number_done; } scan_number_done: // unget the character after the number (we only read it to know that // we are done scanning a number) unget(); char* endptr = nullptr; errno = 0; // try to parse integers first and fall back to floats if (number_type == token_type::value_unsigned) { const auto x = std::strtoull(token_buffer.data(), &endptr, 10); // we checked the number format before assert(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { value_unsigned = static_cast(x); if (value_unsigned == x) { return token_type::value_unsigned; } } } else if (number_type == token_type::value_integer) { const auto x = std::strtoll(token_buffer.data(), &endptr, 10); // we checked the number format before assert(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { value_integer = static_cast(x); if (value_integer == x) { return token_type::value_integer; } } } // this code is reached if we parse a floating-point number or if an // integer conversion above failed strtof(value_float, token_buffer.data(), &endptr); // we checked the number format before assert(endptr == token_buffer.data() + token_buffer.size()); return token_type::value_float; } /*! @param[in] literal_text the literal text to expect @param[in] length the length of the passed literal text @param[in] return_type the token type to return on success */ token_type scan_literal(const char* literal_text, const std::size_t length, token_type return_type) { assert(current == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { if (JSON_UNLIKELY(get() != literal_text[i])) { error_message = "invalid literal"; return token_type::parse_error; } } return return_type; } ///////////////////// // input management ///////////////////// /// reset token_buffer; current character is beginning of token void reset() noexcept { token_buffer.clear(); token_string.clear(); token_string.push_back(std::char_traits::to_char_type(current)); } /* @brief get next character from the input This function provides the interface to the used input adapter. It does not throw in case the input reached EOF, but returns a `std::char_traits::eof()` in that case. Stores the scanned characters for use in error messages. @return character read from the input */ std::char_traits::int_type get() { ++chars_read; current = ia->get_character(); if (JSON_LIKELY(current != std::char_traits::eof())) { token_string.push_back(std::char_traits::to_char_type(current)); } return current; } /// unget current character (return it again on next get) void unget() { --chars_read; if (JSON_LIKELY(current != std::char_traits::eof())) { ia->unget_character(); assert(token_string.size() != 0); token_string.pop_back(); } } /// add a character to token_buffer void add(int c) { token_buffer.push_back(std::char_traits::to_char_type(c)); } public: ///////////////////// // value getters ///////////////////// /// return integer value constexpr number_integer_t get_number_integer() const noexcept { return value_integer; } /// return unsigned integer value constexpr number_unsigned_t get_number_unsigned() const noexcept { return value_unsigned; } /// return floating-point value constexpr number_float_t get_number_float() const noexcept { return value_float; } /// return current string value (implicitly resets the token; useful only once) string_t&& move_string() { return std::move(token_buffer); } ///////////////////// // diagnostics ///////////////////// /// return position of last read token constexpr std::size_t get_position() const noexcept { return chars_read; } /// return the last read token (for errors only). Will never contain EOF /// (an arbitrary value that is not a valid char value, often -1), because /// 255 may legitimately occur. May contain NUL, which should be escaped. std::string get_token_string() const { // escape control characters std::string result; for (const auto c : token_string) { if ('\x00' <= c and c <= '\x1F') { // escape control characters std::stringstream ss; ss << "(c) << ">"; result += ss.str(); } else { // add character as is result.push_back(c); } } return result; } /// return syntax error message constexpr const char* get_error_message() const noexcept { return error_message; } ///////////////////// // actual scanner ///////////////////// token_type scan() { // read next character and ignore whitespace do { get(); } while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); switch (current) { // structural characters case '[': return token_type::begin_array; case ']': return token_type::end_array; case '{': return token_type::begin_object; case '}': return token_type::end_object; case ':': return token_type::name_separator; case ',': return token_type::value_separator; // literals case 't': return scan_literal("true", 4, token_type::literal_true); case 'f': return scan_literal("false", 5, token_type::literal_false); case 'n': return scan_literal("null", 4, token_type::literal_null); // string case '\"': return scan_string(); // number case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return scan_number(); // end of input (the null byte is needed when parsing from // string literals) case '\0': case std::char_traits::eof(): return token_type::end_of_input; // error default: error_message = "invalid literal"; return token_type::parse_error; } } private: /// input adapter detail::input_adapter_t ia = nullptr; /// the current character std::char_traits::int_type current = std::char_traits::eof(); /// the number of characters read std::size_t chars_read = 0; /// raw input token string (for error messages) std::vector token_string {}; /// buffer for variable-length tokens (numbers, strings) string_t token_buffer {}; /// a description of occurred lexer errors const char* error_message = ""; // number values number_integer_t value_integer = 0; number_unsigned_t value_unsigned = 0; number_float_t value_float = 0; /// the decimal point const char decimal_point_char = '.'; }; } } // #include #include // assert #include // isfinite #include // uint8_t #include // function #include // string #include // move // #include // #include // #include // #include // #include namespace nlohmann { namespace detail { //////////// // parser // //////////// /*! @brief syntax analysis This class implements a recursive decent parser. */ template class parser { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; using lexer_t = lexer; using token_type = typename lexer_t::token_type; public: enum class parse_event_t : uint8_t { /// the parser read `{` and started to process a JSON object object_start, /// the parser read `}` and finished processing a JSON object object_end, /// the parser read `[` and started to process a JSON array array_start, /// the parser read `]` and finished processing a JSON array array_end, /// the parser read a key of a value in an object key, /// the parser finished reading a JSON value value }; using parser_callback_t = std::function; /// a parser reading from an input adapter explicit parser(detail::input_adapter_t adapter, const parser_callback_t cb = nullptr, const bool allow_exceptions_ = true) : callback(cb), m_lexer(adapter), allow_exceptions(allow_exceptions_) {} /*! @brief public parser interface @param[in] strict whether to expect the last token to be EOF @param[in,out] result parsed JSON value @throw parse_error.101 in case of an unexpected token @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails */ void parse(const bool strict, BasicJsonType& result) { // read first token get_token(); parse_internal(true, result); result.assert_invariant(); // in strict mode, input must be completely read if (strict) { get_token(); expect(token_type::end_of_input); } // in case of an error, return discarded value if (errored) { result = value_t::discarded; return; } // set top-level value to null if it was discarded by the callback // function if (result.is_discarded()) { result = nullptr; } } /*! @brief public accept interface @param[in] strict whether to expect the last token to be EOF @return whether the input is a proper JSON text */ bool accept(const bool strict = true) { // read first token get_token(); if (not accept_internal()) { return false; } // strict => last token must be EOF return not strict or (get_token() == token_type::end_of_input); } private: /*! @brief the actual parser @throw parse_error.101 in case of an unexpected token @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails */ void parse_internal(bool keep, BasicJsonType& result) { // never parse after a parse error was detected assert(not errored); // start with a discarded value if (not result.is_discarded()) { result.m_value.destroy(result.m_type); result.m_type = value_t::discarded; } switch (last_token) { case token_type::begin_object: { if (keep) { if (callback) { keep = callback(depth++, parse_event_t::object_start, result); } if (not callback or keep) { // explicitly set result to object to cope with {} result.m_type = value_t::object; result.m_value = value_t::object; } } // read next token get_token(); // closing } -> we are done if (last_token == token_type::end_object) { if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) { result.m_value.destroy(result.m_type); result.m_type = value_t::discarded; } break; } // parse values string_t key; BasicJsonType value; while (true) { // store key if (not expect(token_type::value_string)) { return; } key = m_lexer.move_string(); bool keep_tag = false; if (keep) { if (callback) { BasicJsonType k(key); keep_tag = callback(depth, parse_event_t::key, k); } else { keep_tag = true; } } // parse separator (:) get_token(); if (not expect(token_type::name_separator)) { return; } // parse and add value get_token(); value.m_value.destroy(value.m_type); value.m_type = value_t::discarded; parse_internal(keep, value); if (JSON_UNLIKELY(errored)) { return; } if (keep and keep_tag and not value.is_discarded()) { result.m_value.object->emplace(std::move(key), std::move(value)); } // comma -> next value get_token(); if (last_token == token_type::value_separator) { get_token(); continue; } // closing } if (not expect(token_type::end_object)) { return; } break; } if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) { result.m_value.destroy(result.m_type); result.m_type = value_t::discarded; } break; } case token_type::begin_array: { if (keep) { if (callback) { keep = callback(depth++, parse_event_t::array_start, result); } if (not callback or keep) { // explicitly set result to array to cope with [] result.m_type = value_t::array; result.m_value = value_t::array; } } // read next token get_token(); // closing ] -> we are done if (last_token == token_type::end_array) { if (callback and not callback(--depth, parse_event_t::array_end, result)) { result.m_value.destroy(result.m_type); result.m_type = value_t::discarded; } break; } // parse values BasicJsonType value; while (true) { // parse value value.m_value.destroy(value.m_type); value.m_type = value_t::discarded; parse_internal(keep, value); if (JSON_UNLIKELY(errored)) { return; } if (keep and not value.is_discarded()) { result.m_value.array->push_back(std::move(value)); } // comma -> next value get_token(); if (last_token == token_type::value_separator) { get_token(); continue; } // closing ] if (not expect(token_type::end_array)) { return; } break; } if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) { result.m_value.destroy(result.m_type); result.m_type = value_t::discarded; } break; } case token_type::literal_null: { result.m_type = value_t::null; break; } case token_type::value_string: { result.m_type = value_t::string; result.m_value = m_lexer.move_string(); break; } case token_type::literal_true: { result.m_type = value_t::boolean; result.m_value = true; break; } case token_type::literal_false: { result.m_type = value_t::boolean; result.m_value = false; break; } case token_type::value_unsigned: { result.m_type = value_t::number_unsigned; result.m_value = m_lexer.get_number_unsigned(); break; } case token_type::value_integer: { result.m_type = value_t::number_integer; result.m_value = m_lexer.get_number_integer(); break; } case token_type::value_float: { result.m_type = value_t::number_float; result.m_value = m_lexer.get_number_float(); // throw in case of infinity or NAN if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float))) { if (allow_exceptions) { JSON_THROW(out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); } expect(token_type::uninitialized); } break; } case token_type::parse_error: { // using "uninitialized" to avoid "expected" message if (not expect(token_type::uninitialized)) { return; } break; // LCOV_EXCL_LINE } default: { // the last token was unexpected; we expected a value if (not expect(token_type::literal_or_value)) { return; } break; // LCOV_EXCL_LINE } } if (keep and callback and not callback(depth, parse_event_t::value, result)) { result.m_value.destroy(result.m_type); result.m_type = value_t::discarded; } } /*! @brief the actual acceptor @invariant 1. The last token is not yet processed. Therefore, the caller of this function must make sure a token has been read. 2. When this function returns, the last token is processed. That is, the last read character was already considered. This invariant makes sure that no token needs to be "unput". */ bool accept_internal() { switch (last_token) { case token_type::begin_object: { // read next token get_token(); // closing } -> we are done if (last_token == token_type::end_object) { return true; } // parse values while (true) { // parse key if (last_token != token_type::value_string) { return false; } // parse separator (:) get_token(); if (last_token != token_type::name_separator) { return false; } // parse value get_token(); if (not accept_internal()) { return false; } // comma -> next value get_token(); if (last_token == token_type::value_separator) { get_token(); continue; } // closing } return (last_token == token_type::end_object); } } case token_type::begin_array: { // read next token get_token(); // closing ] -> we are done if (last_token == token_type::end_array) { return true; } // parse values while (true) { // parse value if (not accept_internal()) { return false; } // comma -> next value get_token(); if (last_token == token_type::value_separator) { get_token(); continue; } // closing ] return (last_token == token_type::end_array); } } case token_type::value_float: { // reject infinity or NAN return std::isfinite(m_lexer.get_number_float()); } case token_type::literal_false: case token_type::literal_null: case token_type::literal_true: case token_type::value_integer: case token_type::value_string: case token_type::value_unsigned: return true; default: // the last token was unexpected return false; } } /// get next token from lexer token_type get_token() { return (last_token = m_lexer.scan()); } /*! @throw parse_error.101 if expected token did not occur */ bool expect(token_type t) { if (JSON_UNLIKELY(t != last_token)) { errored = true; expected = t; if (allow_exceptions) { throw_exception(); } else { return false; } } return true; } [[noreturn]] void throw_exception() const { std::string error_msg = "syntax error - "; if (last_token == token_type::parse_error) { error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + m_lexer.get_token_string() + "'"; } else { error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); } if (expected != token_type::uninitialized) { error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); } JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); } private: /// current level of recursion int depth = 0; /// callback function const parser_callback_t callback = nullptr; /// the type of the last read token token_type last_token = token_type::uninitialized; /// the lexer lexer_t m_lexer; /// whether a syntax error occurred bool errored = false; /// possible reason for the syntax error token_type expected = token_type::uninitialized; /// whether to throw exceptions in case of errors const bool allow_exceptions = true; }; } } // #include #include // ptrdiff_t #include // numeric_limits namespace nlohmann { namespace detail { /* @brief an iterator for primitive JSON types This class models an iterator for primitive JSON types (boolean, number, string). It's only purpose is to allow the iterator/const_iterator classes to "iterate" over primitive values. Internally, the iterator is modeled by a `difference_type` variable. Value begin_value (`0`) models the begin, end_value (`1`) models past the end. */ class primitive_iterator_t { private: using difference_type = std::ptrdiff_t; static constexpr difference_type begin_value = 0; static constexpr difference_type end_value = begin_value + 1; /// iterator as signed integer type difference_type m_it = (std::numeric_limits::min)(); public: constexpr difference_type get_value() const noexcept { return m_it; } /// set iterator to a defined beginning void set_begin() noexcept { m_it = begin_value; } /// set iterator to a defined past the end void set_end() noexcept { m_it = end_value; } /// return whether the iterator can be dereferenced constexpr bool is_begin() const noexcept { return m_it == begin_value; } /// return whether the iterator is at end constexpr bool is_end() const noexcept { return m_it == end_value; } friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it == rhs.m_it; } friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it < rhs.m_it; } primitive_iterator_t operator+(difference_type n) noexcept { auto result = *this; result += n; return result; } friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it - rhs.m_it; } primitive_iterator_t& operator++() noexcept { ++m_it; return *this; } primitive_iterator_t const operator++(int) noexcept { auto result = *this; m_it++; return result; } primitive_iterator_t& operator--() noexcept { --m_it; return *this; } primitive_iterator_t const operator--(int) noexcept { auto result = *this; m_it--; return result; } primitive_iterator_t& operator+=(difference_type n) noexcept { m_it += n; return *this; } primitive_iterator_t& operator-=(difference_type n) noexcept { m_it -= n; return *this; } }; } } // #include // #include namespace nlohmann { namespace detail { /*! @brief an iterator value @note This structure could easily be a union, but MSVC currently does not allow unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. */ template struct internal_iterator { /// iterator for JSON objects typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; } } // #include #include // not #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const // #include // #include // #include // #include // #include // #include namespace nlohmann { namespace detail { // forward declare, to be able to friend it later on template class iteration_proxy; /*! @brief a template for a bidirectional iterator for the @ref basic_json class This class implements a both iterators (iterator and const_iterator) for the @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most methods are undefined. **The library uses assertions to detect calls on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator): The iterator that can be moved can be moved in both directions (i.e. incremented and decremented). @since version 1.0.0, simplified in version 2.0.9, change to bidirectional iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) */ template class iter_impl { /// allow basic_json to access private members friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; friend BasicJsonType; friend iteration_proxy; using object_t = typename BasicJsonType::object_t; using array_t = typename BasicJsonType::array_t; // make sure BasicJsonType is basic_json or const basic_json static_assert(is_basic_json::type>::value, "iter_impl only accepts (const) basic_json"); public: /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. /// The C++ Standard has never required user-defined iterators to derive from std::iterator. /// A user-defined iterator should provide publicly accessible typedefs named /// iterator_category, value_type, difference_type, pointer, and reference. /// Note that value_type is required to be non-const, even for constant iterators. using iterator_category = std::bidirectional_iterator_tag; /// the type of the values when the iterator is dereferenced using value_type = typename BasicJsonType::value_type; /// a type to represent differences between iterators using difference_type = typename BasicJsonType::difference_type; /// defines a pointer to the type iterated over (value_type) using pointer = typename std::conditional::value, typename BasicJsonType::const_pointer, typename BasicJsonType::pointer>::type; /// defines a reference to the type iterated over (value_type) using reference = typename std::conditional::value, typename BasicJsonType::const_reference, typename BasicJsonType::reference>::type; /// default constructor iter_impl() = default; /*! @brief constructor for a given JSON instance @param[in] object pointer to a JSON object for this iterator @pre object != nullptr @post The iterator is initialized; i.e. `m_object != nullptr`. */ explicit iter_impl(pointer object) noexcept : m_object(object) { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { m_it.object_iterator = typename object_t::iterator(); break; } case value_t::array: { m_it.array_iterator = typename array_t::iterator(); break; } default: { m_it.primitive_iterator = primitive_iterator_t(); break; } } } /*! @note The conventional copy constructor and copy assignment are implicitly defined. Combined with the following converting constructor and assignment, they support: (1) copy from iterator to iterator, (2) copy from const iterator to const iterator, and (3) conversion from iterator to const iterator. However conversion from const iterator to iterator is not defined. */ /*! @brief converting constructor @param[in] other non-const iterator to copy from @note It is not checked whether @a other is initialized. */ iter_impl(const iter_impl::type>& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} /*! @brief converting assignment @param[in,out] other non-const iterator to copy from @return const/non-const iterator @note It is not checked whether @a other is initialized. */ iter_impl& operator=(const iter_impl::type>& other) noexcept { m_object = other.m_object; m_it = other.m_it; return *this; } private: /*! @brief set the iterator to the first value @pre The iterator is initialized; i.e. `m_object != nullptr`. */ void set_begin() noexcept { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { m_it.object_iterator = m_object->m_value.object->begin(); break; } case value_t::array: { m_it.array_iterator = m_object->m_value.array->begin(); break; } case value_t::null: { // set to end so begin()==end() is true: null is empty m_it.primitive_iterator.set_end(); break; } default: { m_it.primitive_iterator.set_begin(); break; } } } /*! @brief set the iterator past the last value @pre The iterator is initialized; i.e. `m_object != nullptr`. */ void set_end() noexcept { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { m_it.object_iterator = m_object->m_value.object->end(); break; } case value_t::array: { m_it.array_iterator = m_object->m_value.array->end(); break; } default: { m_it.primitive_iterator.set_end(); break; } } } public: /*! @brief return a reference to the value pointed to by the iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference operator*() const { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { assert(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case value_t::array: { assert(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } case value_t::null: JSON_THROW(invalid_iterator::create(214, "cannot get value")); default: { if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) { return *m_object; } JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } /*! @brief dereference the iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ pointer operator->() const { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { assert(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case value_t::array: { assert(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } default: { if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) { return m_object; } JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } /*! @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl const operator++(int) { auto result = *this; ++(*this); return result; } /*! @brief pre-increment (++it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator++() { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { std::advance(m_it.object_iterator, 1); break; } case value_t::array: { std::advance(m_it.array_iterator, 1); break; } default: { ++m_it.primitive_iterator; break; } } return *this; } /*! @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl const operator--(int) { auto result = *this; --(*this); return result; } /*! @brief pre-decrement (--it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator--() { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: { std::advance(m_it.object_iterator, -1); break; } case value_t::array: { std::advance(m_it.array_iterator, -1); break; } default: { --m_it.primitive_iterator; break; } } return *this; } /*! @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (JSON_UNLIKELY(m_object != other.m_object)) { JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: return (m_it.object_iterator == other.m_it.object_iterator); case value_t::array: return (m_it.array_iterator == other.m_it.array_iterator); default: return (m_it.primitive_iterator == other.m_it.primitive_iterator); } } /*! @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator!=(const iter_impl& other) const { return not operator==(other); } /*! @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (JSON_UNLIKELY(m_object != other.m_object)) { JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); } assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); default: return (m_it.primitive_iterator < other.m_it.primitive_iterator); } } /*! @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<=(const iter_impl& other) const { return not other.operator < (*this); } /*! @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>(const iter_impl& other) const { return not operator<=(other); } /*! @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>=(const iter_impl& other) const { return not operator<(other); } /*! @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator+=(difference_type i) { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); case value_t::array: { std::advance(m_it.array_iterator, i); break; } default: { m_it.primitive_iterator += i; break; } } return *this; } /*! @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator-=(difference_type i) { return operator+=(-i); } /*! @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator+(difference_type i) const { auto result = *this; result += i; return result; } /*! @brief addition of distance and iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ friend iter_impl operator+(difference_type i, const iter_impl& it) { auto result = it; result += i; return result; } /*! @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator-(difference_type i) const { auto result = *this; result -= i; return result; } /*! @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ difference_type operator-(const iter_impl& other) const { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; default: return m_it.primitive_iterator - other.m_it.primitive_iterator; } } /*! @brief access to successor @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference operator[](difference_type n) const { assert(m_object != nullptr); switch (m_object->m_type) { case value_t::object: JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: JSON_THROW(invalid_iterator::create(214, "cannot get value")); default: { if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n)) { return *m_object; } JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } /*! @brief return the key of an object iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ typename object_t::key_type key() const { assert(m_object != nullptr); if (JSON_LIKELY(m_object->is_object())) { return m_it.object_iterator->first; } JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); } /*! @brief return the value of an iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference value() const { return operator*(); } private: /// associated JSON instance pointer m_object = nullptr; /// the actual iterator of the associated instance internal_iterator::type> m_it; }; } } // #include #include // size_t #include // string, to_string // #include namespace nlohmann { namespace detail { /// proxy class for the items() function template class iteration_proxy { private: /// helper class for iteration class iteration_proxy_internal { private: /// the iterator IteratorType anchor; /// an index for arrays (used to create key names) std::size_t array_index = 0; public: explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {} /// dereference operator (needed for range-based for) iteration_proxy_internal& operator*() { return *this; } /// increment operator (needed for range-based for) iteration_proxy_internal& operator++() { ++anchor; ++array_index; return *this; } /// inequality operator (needed for range-based for) bool operator!=(const iteration_proxy_internal& o) const noexcept { return anchor != o.anchor; } /// return key of the iterator std::string key() const { assert(anchor.m_object != nullptr); switch (anchor.m_object->type()) { // use integer array index as key case value_t::array: return std::to_string(array_index); // use key from the object case value_t::object: return anchor.key(); // use an empty key for all primitive types default: return ""; } } /// return value of the iterator typename IteratorType::reference value() const { return anchor.value(); } }; /// the container to iterate typename IteratorType::reference container; public: /// construct iteration proxy from a container explicit iteration_proxy(typename IteratorType::reference cont) noexcept : container(cont) {} /// return iterator begin (needed for range-based for) iteration_proxy_internal begin() noexcept { return iteration_proxy_internal(container.begin()); } /// return iterator end (needed for range-based for) iteration_proxy_internal end() noexcept { return iteration_proxy_internal(container.end()); } }; } } // #include #include // ptrdiff_t #include // reverse_iterator #include // declval namespace nlohmann { namespace detail { ////////////////////// // reverse_iterator // ////////////////////// /*! @brief a template for a reverse iterator class @tparam Base the base iterator type to reverse. Valid types are @ref iterator (to create @ref reverse_iterator) and @ref const_iterator (to create @ref const_reverse_iterator). @requirement The class satisfies the following concept requirements: - [BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator): The iterator that can be moved can be moved in both directions (i.e. incremented and decremented). - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): It is possible to write to the pointed-to element (only if @a Base is @ref iterator). @since version 1.0.0 */ template class json_reverse_iterator : public std::reverse_iterator { public: using difference_type = std::ptrdiff_t; /// shortcut to the reverse iterator adapter using base_iterator = std::reverse_iterator; /// the reference type for the pointed-to element using reference = typename Base::reference; /// create reverse iterator from iterator json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept : base_iterator(it) {} /// create reverse iterator from base class json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} /// post-increment (it++) json_reverse_iterator const operator++(int) { return static_cast(base_iterator::operator++(1)); } /// pre-increment (++it) json_reverse_iterator& operator++() { return static_cast(base_iterator::operator++()); } /// post-decrement (it--) json_reverse_iterator const operator--(int) { return static_cast(base_iterator::operator--(1)); } /// pre-decrement (--it) json_reverse_iterator& operator--() { return static_cast(base_iterator::operator--()); } /// add to iterator json_reverse_iterator& operator+=(difference_type i) { return static_cast(base_iterator::operator+=(i)); } /// add to iterator json_reverse_iterator operator+(difference_type i) const { return static_cast(base_iterator::operator+(i)); } /// subtract from iterator json_reverse_iterator operator-(difference_type i) const { return static_cast(base_iterator::operator-(i)); } /// return difference difference_type operator-(const json_reverse_iterator& other) const { return base_iterator(*this) - base_iterator(other); } /// access to successor reference operator[](difference_type n) const { return *(this->operator+(n)); } /// return the key of an object iterator auto key() const -> decltype(std::declval().key()) { auto it = --this->base(); return it.key(); } /// return the value of an iterator reference value() const { auto it = --this->base(); return it.operator * (); } }; } } // #include #include // copy #include // size_t #include // streamsize #include // back_inserter #include // shared_ptr, make_shared #include // basic_ostream #include // basic_string #include // vector namespace nlohmann { namespace detail { /// abstract output adapter interface template struct output_adapter_protocol { virtual void write_character(CharType c) = 0; virtual void write_characters(const CharType* s, std::size_t length) = 0; virtual ~output_adapter_protocol() = default; }; /// a type to simplify interfaces template using output_adapter_t = std::shared_ptr>; /// output adapter for byte vectors template class output_vector_adapter : public output_adapter_protocol { public: explicit output_vector_adapter(std::vector& vec) : v(vec) {} void write_character(CharType c) override { v.push_back(c); } void write_characters(const CharType* s, std::size_t length) override { std::copy(s, s + length, std::back_inserter(v)); } private: std::vector& v; }; /// output adapter for output streams template class output_stream_adapter : public output_adapter_protocol { public: explicit output_stream_adapter(std::basic_ostream& s) : stream(s) {} void write_character(CharType c) override { stream.put(c); } void write_characters(const CharType* s, std::size_t length) override { stream.write(s, static_cast(length)); } private: std::basic_ostream& stream; }; /// output adapter for basic_string template> class output_string_adapter : public output_adapter_protocol { public: explicit output_string_adapter(StringType& s) : str(s) {} void write_character(CharType c) override { str.push_back(c); } void write_characters(const CharType* s, std::size_t length) override { str.append(s, length); } private: StringType& str; }; template> class output_adapter { public: output_adapter(std::vector& vec) : oa(std::make_shared>(vec)) {} output_adapter(std::basic_ostream& s) : oa(std::make_shared>(s)) {} output_adapter(StringType& s) : oa(std::make_shared>(s)) {} operator output_adapter_t() { return oa; } private: output_adapter_t oa = nullptr; }; } } // #include #include // generate_n #include // array #include // assert #include // ldexp #include // size_t #include // uint8_t, uint16_t, uint32_t, uint64_t #include // memcpy #include // setw, setfill #include // hex #include // back_inserter #include // numeric_limits #include // stringstream #include // char_traits, string #include // make_pair, move // #include // #include // #include // #include namespace nlohmann { namespace detail { /////////////////// // binary reader // /////////////////// /*! @brief deserialization of CBOR and MessagePack values */ template class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using string_t = typename BasicJsonType::string_t; public: /*! @brief create a binary reader @param[in] adapter input adapter to read from */ explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) { assert(ia); } /*! @brief create a JSON value from CBOR input @param[in] strict whether to expect the input to be consumed completed @return JSON value created from CBOR input @throw parse_error.110 if input ended unexpectedly or the end of file was not reached when @a strict was set to true @throw parse_error.112 if unsupported byte was read */ BasicJsonType parse_cbor(const bool strict) { const auto res = parse_cbor_internal(); if (strict) { get(); expect_eof(); } return res; } /*! @brief create a JSON value from MessagePack input @param[in] strict whether to expect the input to be consumed completed @return JSON value created from MessagePack input @throw parse_error.110 if input ended unexpectedly or the end of file was not reached when @a strict was set to true @throw parse_error.112 if unsupported byte was read */ BasicJsonType parse_msgpack(const bool strict) { const auto res = parse_msgpack_internal(); if (strict) { get(); expect_eof(); } return res; } /*! @brief create a JSON value from UBJSON input @param[in] strict whether to expect the input to be consumed completed @return JSON value created from UBJSON input @throw parse_error.110 if input ended unexpectedly or the end of file was not reached when @a strict was set to true @throw parse_error.112 if unsupported byte was read */ BasicJsonType parse_ubjson(const bool strict) { const auto res = parse_ubjson_internal(); if (strict) { get_ignore_noop(); expect_eof(); } return res; } /*! @brief determine system byte order @return true if and only if system's byte order is little endian @note from http://stackoverflow.com/a/1001328/266378 */ static constexpr bool little_endianess(int num = 1) noexcept { return (*reinterpret_cast(&num) == 1); } private: /*! @param[in] get_char whether a new character should be retrieved from the input (true, default) or whether the last read character should be considered instead */ BasicJsonType parse_cbor_internal(const bool get_char = true) { switch (get_char ? get() : current) { // EOF case std::char_traits::eof(): JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); // Integer 0x00..0x17 (0..23) case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: return static_cast(current); case 0x18: // Unsigned integer (one-byte uint8_t follows) return get_number(); case 0x19: // Unsigned integer (two-byte uint16_t follows) return get_number(); case 0x1A: // Unsigned integer (four-byte uint32_t follows) return get_number(); case 0x1B: // Unsigned integer (eight-byte uint64_t follows) return get_number(); // Negative integer -1-0x00..-1-0x17 (-1..-24) case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: return static_cast(0x20 - 1 - current); case 0x38: // Negative integer (one-byte uint8_t follows) { return static_cast(-1) - get_number(); } case 0x39: // Negative integer -1-n (two-byte uint16_t follows) { return static_cast(-1) - get_number(); } case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) { return static_cast(-1) - get_number(); } case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) { return static_cast(-1) - static_cast(get_number()); } // UTF-8 string (0x00..0x17 bytes follow) case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: // UTF-8 string (one-byte uint8_t for n follows) case 0x79: // UTF-8 string (two-byte uint16_t for n follow) case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) case 0x7F: // UTF-8 string (indefinite length) { return get_cbor_string(); } // array (0x00..0x17 data items follow) case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: { return get_cbor_array(current & 0x1F); } case 0x98: // array (one-byte uint8_t for n follows) { return get_cbor_array(get_number()); } case 0x99: // array (two-byte uint16_t for n follow) { return get_cbor_array(get_number()); } case 0x9A: // array (four-byte uint32_t for n follow) { return get_cbor_array(get_number()); } case 0x9B: // array (eight-byte uint64_t for n follow) { return get_cbor_array(get_number()); } case 0x9F: // array (indefinite length) { BasicJsonType result = value_t::array; while (get() != 0xFF) { result.push_back(parse_cbor_internal(false)); } return result; } // map (0x00..0x17 pairs of data items follow) case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: { return get_cbor_object(current & 0x1F); } case 0xB8: // map (one-byte uint8_t for n follows) { return get_cbor_object(get_number()); } case 0xB9: // map (two-byte uint16_t for n follow) { return get_cbor_object(get_number()); } case 0xBA: // map (four-byte uint32_t for n follow) { return get_cbor_object(get_number()); } case 0xBB: // map (eight-byte uint64_t for n follow) { return get_cbor_object(get_number()); } case 0xBF: // map (indefinite length) { BasicJsonType result = value_t::object; while (get() != 0xFF) { auto key = get_cbor_string(); result[key] = parse_cbor_internal(); } return result; } case 0xF4: // false { return false; } case 0xF5: // true { return true; } case 0xF6: // null { return value_t::null; } case 0xF9: // Half-Precision Float (two-byte IEEE 754) { const int byte1 = get(); unexpect_eof(); const int byte2 = get(); unexpect_eof(); // code from RFC 7049, Appendix D, Figure 3: // As half-precision floating-point numbers were only added // to IEEE 754 in 2008, today's programming platforms often // still only have limited support for them. It is very // easy to include at least decoding support for them even // without such support. An example of a small decoder for // half-precision floating-point numbers in the C language // is shown in Fig. 3. const int half = (byte1 << 8) + byte2; const int exp = (half >> 10) & 0x1F; const int mant = half & 0x3FF; double val; if (exp == 0) { val = std::ldexp(mant, -24); } else if (exp != 31) { val = std::ldexp(mant + 1024, exp - 25); } else { val = (mant == 0) ? std::numeric_limits::infinity() : std::numeric_limits::quiet_NaN(); } return (half & 0x8000) != 0 ? -val : val; } case 0xFA: // Single-Precision Float (four-byte IEEE 754) { return get_number(); } case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { return get_number(); } default: // anything else (0xFF is handled inside the other types) { std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); } } } BasicJsonType parse_msgpack_internal() { switch (get()) { // EOF case std::char_traits::eof(): JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); // positive fixint case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: return static_cast(current); // fixmap case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: { return get_msgpack_object(current & 0x0F); } // fixarray case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F: { return get_msgpack_array(current & 0x0F); } // fixstr case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: return get_msgpack_string(); case 0xC0: // nil return value_t::null; case 0xC2: // false return false; case 0xC3: // true return true; case 0xCA: // float 32 return get_number(); case 0xCB: // float 64 return get_number(); case 0xCC: // uint 8 return get_number(); case 0xCD: // uint 16 return get_number(); case 0xCE: // uint 32 return get_number(); case 0xCF: // uint 64 return get_number(); case 0xD0: // int 8 return get_number(); case 0xD1: // int 16 return get_number(); case 0xD2: // int 32 return get_number(); case 0xD3: // int 64 return get_number(); case 0xD9: // str 8 case 0xDA: // str 16 case 0xDB: // str 32 return get_msgpack_string(); case 0xDC: // array 16 { return get_msgpack_array(get_number()); } case 0xDD: // array 32 { return get_msgpack_array(get_number()); } case 0xDE: // map 16 { return get_msgpack_object(get_number()); } case 0xDF: // map 32 { return get_msgpack_object(get_number()); } // positive fixint case 0xE0: case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8: case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xED: case 0xEE: case 0xEF: case 0xF0: case 0xF1: case 0xF2: case 0xF3: case 0xF4: case 0xF5: case 0xF6: case 0xF7: case 0xF8: case 0xF9: case 0xFA: case 0xFB: case 0xFC: case 0xFD: case 0xFE: case 0xFF: return static_cast(current); default: // anything else { std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(112, chars_read, "error reading MessagePack; last byte: 0x" + ss.str())); } } } /*! @param[in] get_char whether a new character should be retrieved from the input (true, default) or whether the last read character should be considered instead */ BasicJsonType parse_ubjson_internal(const bool get_char = true) { return get_ubjson_value(get_char ? get_ignore_noop() : current); } /*! @brief get next character from the input This function provides the interface to the used input adapter. It does not throw in case the input reached EOF, but returns a -'ve valued `std::char_traits::eof()` in that case. @return character read from the input */ int get() { ++chars_read; return (current = ia->get_character()); } /*! @return character read from the input after ignoring all 'N' entries */ int get_ignore_noop() { do { get(); } while (current == 'N'); return current; } /* @brief read a number from the input @tparam NumberType the type of the number @return number of type @a NumberType @note This function needs to respect the system's endianess, because bytes in CBOR and MessagePack are stored in network order (big endian) and therefore need reordering on little endian systems. @throw parse_error.110 if input has less than `sizeof(NumberType)` bytes */ template NumberType get_number() { // step 1: read input into array with system's byte order std::array vec; for (std::size_t i = 0; i < sizeof(NumberType); ++i) { get(); unexpect_eof(); // reverse byte order prior to conversion if necessary if (is_little_endian) { vec[sizeof(NumberType) - i - 1] = static_cast(current); } else { vec[i] = static_cast(current); // LCOV_EXCL_LINE } } // step 2: convert array into number of type T and return NumberType result; std::memcpy(&result, vec.data(), sizeof(NumberType)); return result; } /*! @brief create a string by reading characters from the input @param[in] len number of bytes to read @note We can not reserve @a len bytes for the result, because @a len may be too large. Usually, @ref unexpect_eof() detects the end of the input before we run out of string memory. @return string created by reading @a len bytes @throw parse_error.110 if input has less than @a len bytes */ template string_t get_string(const NumberType len) { string_t result; std::generate_n(std::back_inserter(result), len, [this]() { get(); unexpect_eof(); return static_cast(current); }); return result; } /*! @brief reads a CBOR string This function first reads starting bytes to determine the expected string length and then copies this number of bytes into a string. Additionally, CBOR's strings with indefinite lengths are supported. @return string @throw parse_error.110 if input ended @throw parse_error.113 if an unexpected byte is read */ string_t get_cbor_string() { unexpect_eof(); switch (current) { // UTF-8 string (0x00..0x17 bytes follow) case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: { return get_string(current & 0x1F); } case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { return get_string(get_number()); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { return get_string(get_number()); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { return get_string(get_number()); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { return get_string(get_number()); } case 0x7F: // UTF-8 string (indefinite length) { string_t result; while (get() != 0xFF) { result.append(get_cbor_string()); } return result; } default: { std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); } } } template BasicJsonType get_cbor_array(const NumberType len) { BasicJsonType result = value_t::array; std::generate_n(std::back_inserter(*result.m_value.array), len, [this]() { return parse_cbor_internal(); }); return result; } template BasicJsonType get_cbor_object(const NumberType len) { BasicJsonType result = value_t::object; std::generate_n(std::inserter(*result.m_value.object, result.m_value.object->end()), len, [this]() { get(); auto key = get_cbor_string(); auto val = parse_cbor_internal(); return std::make_pair(std::move(key), std::move(val)); }); return result; } /*! @brief reads a MessagePack string This function first reads starting bytes to determine the expected string length and then copies this number of bytes into a string. @return string @throw parse_error.110 if input ended @throw parse_error.113 if an unexpected byte is read */ string_t get_msgpack_string() { unexpect_eof(); switch (current) { // fixstr case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: { return get_string(current & 0x1F); } case 0xD9: // str 8 { return get_string(get_number()); } case 0xDA: // str 16 { return get_string(get_number()); } case 0xDB: // str 32 { return get_string(get_number()); } default: { std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(113, chars_read, "expected a MessagePack string; last byte: 0x" + ss.str())); } } } template BasicJsonType get_msgpack_array(const NumberType len) { BasicJsonType result = value_t::array; std::generate_n(std::back_inserter(*result.m_value.array), len, [this]() { return parse_msgpack_internal(); }); return result; } template BasicJsonType get_msgpack_object(const NumberType len) { BasicJsonType result = value_t::object; std::generate_n(std::inserter(*result.m_value.object, result.m_value.object->end()), len, [this]() { get(); auto key = get_msgpack_string(); auto val = parse_msgpack_internal(); return std::make_pair(std::move(key), std::move(val)); }); return result; } /*! @brief reads a UBJSON string This function is either called after reading the 'S' byte explicitly indicating a string, or in case of an object key where the 'S' byte can be left out. @param[in] get_char whether a new character should be retrieved from the input (true, default) or whether the last read character should be considered instead @return string @throw parse_error.110 if input ended @throw parse_error.113 if an unexpected byte is read */ string_t get_ubjson_string(const bool get_char = true) { if (get_char) { get(); // TODO: may we ignore N here? } unexpect_eof(); switch (current) { case 'U': return get_string(get_number()); case 'i': return get_string(get_number()); case 'I': return get_string(get_number()); case 'l': return get_string(get_number()); case 'L': return get_string(get_number()); default: std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(113, chars_read, "expected a UBJSON string; last byte: 0x" + ss.str())); } } /*! @brief determine the type and size for a container In the optimized UBJSON format, a type and a size can be provided to allow for a more compact representation. @return pair of the size and the type */ std::pair get_ubjson_size_type() { std::size_t sz = string_t::npos; int tc = 0; get_ignore_noop(); if (current == '$') { tc = get(); // must not ignore 'N', because 'N' maybe the type unexpect_eof(); get_ignore_noop(); if (current != '#') { std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(112, chars_read, "expected '#' after UBJSON type information; last byte: 0x" + ss.str())); } sz = parse_ubjson_internal(); } else if (current == '#') { sz = parse_ubjson_internal(); } return std::make_pair(sz, tc); } BasicJsonType get_ubjson_value(const int prefix) { switch (prefix) { case std::char_traits::eof(): // EOF JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); case 'T': // true return true; case 'F': // false return false; case 'Z': // null return nullptr; case 'U': return get_number(); case 'i': return get_number(); case 'I': return get_number(); case 'l': return get_number(); case 'L': return get_number(); case 'd': return get_number(); case 'D': return get_number(); case 'C': // char { get(); unexpect_eof(); if (JSON_UNLIKELY(current > 127)) { std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(113, chars_read, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + ss.str())); } return string_t(1, static_cast(current)); } case 'S': // string return get_ubjson_string(); case '[': // array return get_ubjson_array(); case '{': // object return get_ubjson_object(); default: // anything else std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; JSON_THROW(parse_error::create(112, chars_read, "error reading UBJSON; last byte: 0x" + ss.str())); } } BasicJsonType get_ubjson_array() { BasicJsonType result = value_t::array; const auto size_and_type = get_ubjson_size_type(); if (size_and_type.first != string_t::npos) { if (JSON_UNLIKELY(size_and_type.first > result.max_size())) { JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(size_and_type.first))); } if (size_and_type.second != 0) { if (size_and_type.second != 'N') { std::generate_n(std::back_inserter(*result.m_value.array), size_and_type.first, [this, size_and_type]() { return get_ubjson_value(size_and_type.second); }); } } else { std::generate_n(std::back_inserter(*result.m_value.array), size_and_type.first, [this]() { return parse_ubjson_internal(); }); } } else { while (current != ']') { result.push_back(parse_ubjson_internal(false)); get_ignore_noop(); } } return result; } BasicJsonType get_ubjson_object() { BasicJsonType result = value_t::object; const auto size_and_type = get_ubjson_size_type(); if (size_and_type.first != string_t::npos) { if (JSON_UNLIKELY(size_and_type.first > result.max_size())) { JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(size_and_type.first))); } if (size_and_type.second != 0) { std::generate_n(std::inserter(*result.m_value.object, result.m_value.object->end()), size_and_type.first, [this, size_and_type]() { auto key = get_ubjson_string(); auto val = get_ubjson_value(size_and_type.second); return std::make_pair(std::move(key), std::move(val)); }); } else { std::generate_n(std::inserter(*result.m_value.object, result.m_value.object->end()), size_and_type.first, [this]() { auto key = get_ubjson_string(); auto val = parse_ubjson_internal(); return std::make_pair(std::move(key), std::move(val)); }); } } else { while (current != '}') { auto key = get_ubjson_string(false); result[std::move(key)] = parse_ubjson_internal(); get_ignore_noop(); } } return result; } /*! @brief throw if end of input is not reached @throw parse_error.110 if input not ended */ void expect_eof() const { if (JSON_UNLIKELY(current != std::char_traits::eof())) { JSON_THROW(parse_error::create(110, chars_read, "expected end of input")); } } /*! @briefthrow if end of input is reached @throw parse_error.110 if input ended */ void unexpect_eof() const { if (JSON_UNLIKELY(current == std::char_traits::eof())) { JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); } } private: /// input adapter input_adapter_t ia = nullptr; /// the current character int current = std::char_traits::eof(); /// the number of characters read std::size_t chars_read = 0; /// whether we can assume little endianess const bool is_little_endian = little_endianess(); }; } } // #include #include // reverse #include // array #include // uint8_t, uint16_t, uint32_t, uint64_t #include // memcpy #include // numeric_limits // #include // #include namespace nlohmann { namespace detail { /////////////////// // binary writer // /////////////////// /*! @brief serialization to CBOR and MessagePack values */ template class binary_writer { public: /*! @brief create a binary writer @param[in] adapter output adapter to write to */ explicit binary_writer(output_adapter_t adapter) : oa(adapter) { assert(oa); } /*! @brief[in] j JSON value to serialize */ void write_cbor(const BasicJsonType& j) { switch (j.type()) { case value_t::null: { oa->write_character(static_cast(0xF6)); break; } case value_t::boolean: { oa->write_character(j.m_value.boolean ? static_cast(0xF5) : static_cast(0xF4)); break; } case value_t::number_integer: { if (j.m_value.number_integer >= 0) { // CBOR does not differentiate between positive signed // integers and unsigned integers. Therefore, we used the // code from the value_t::number_unsigned case here. if (j.m_value.number_integer <= 0x17) { write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x18)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x19)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x1A)); write_number(static_cast(j.m_value.number_integer)); } else { oa->write_character(static_cast(0x1B)); write_number(static_cast(j.m_value.number_integer)); } } else { // The conversions below encode the sign in the first // byte, and the value is converted to a positive number. const auto positive_number = -1 - j.m_value.number_integer; if (j.m_value.number_integer >= -24) { write_number(static_cast(0x20 + positive_number)); } else if (positive_number <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x38)); write_number(static_cast(positive_number)); } else if (positive_number <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x39)); write_number(static_cast(positive_number)); } else if (positive_number <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x3A)); write_number(static_cast(positive_number)); } else { oa->write_character(static_cast(0x3B)); write_number(static_cast(positive_number)); } } break; } case value_t::number_unsigned: { if (j.m_value.number_unsigned <= 0x17) { write_number(static_cast(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x18)); write_number(static_cast(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x19)); write_number(static_cast(j.m_value.number_unsigned)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x1A)); write_number(static_cast(j.m_value.number_unsigned)); } else { oa->write_character(static_cast(0x1B)); write_number(static_cast(j.m_value.number_unsigned)); } break; } case value_t::number_float: // Double-Precision Float { oa->write_character(static_cast(0xFB)); write_number(j.m_value.number_float); break; } case value_t::string: { // step 1: write control byte and the string length const auto N = j.m_value.string->size(); if (N <= 0x17) { write_number(static_cast(0x60 + N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x78)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x79)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x7A)); write_number(static_cast(N)); } // LCOV_EXCL_START else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x7B)); write_number(static_cast(N)); } // LCOV_EXCL_STOP // step 2: write the string oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); break; } case value_t::array: { // step 1: write control byte and the array size const auto N = j.m_value.array->size(); if (N <= 0x17) { write_number(static_cast(0x80 + N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x98)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x99)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x9A)); write_number(static_cast(N)); } // LCOV_EXCL_START else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x9B)); write_number(static_cast(N)); } // LCOV_EXCL_STOP // step 2: write each element for (const auto& el : *j.m_value.array) { write_cbor(el); } break; } case value_t::object: { // step 1: write control byte and the object size const auto N = j.m_value.object->size(); if (N <= 0x17) { write_number(static_cast(0xA0 + N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xB8)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xB9)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xBA)); write_number(static_cast(N)); } // LCOV_EXCL_START else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xBB)); write_number(static_cast(N)); } // LCOV_EXCL_STOP // step 2: write each element for (const auto& el : *j.m_value.object) { write_cbor(el.first); write_cbor(el.second); } break; } default: break; } } /*! @brief[in] j JSON value to serialize */ void write_msgpack(const BasicJsonType& j) { switch (j.type()) { case value_t::null: // nil { oa->write_character(static_cast(0xC0)); break; } case value_t::boolean: // true and false { oa->write_character(j.m_value.boolean ? static_cast(0xC3) : static_cast(0xC2)); break; } case value_t::number_integer: { if (j.m_value.number_integer >= 0) { // MessagePack does not differentiate between positive // signed integers and unsigned integers. Therefore, we used // the code from the value_t::number_unsigned case here. if (j.m_value.number_unsigned < 128) { // positive fixnum write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 oa->write_character(static_cast(0xCC)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 oa->write_character(static_cast(0xCD)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 oa->write_character(static_cast(0xCE)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 oa->write_character(static_cast(0xCF)); write_number(static_cast(j.m_value.number_integer)); } } else { if (j.m_value.number_integer >= -32) { // negative fixnum write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 8 oa->write_character(static_cast(0xD0)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 16 oa->write_character(static_cast(0xD1)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 32 oa->write_character(static_cast(0xD2)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) { // int 64 oa->write_character(static_cast(0xD3)); write_number(static_cast(j.m_value.number_integer)); } } break; } case value_t::number_unsigned: { if (j.m_value.number_unsigned < 128) { // positive fixnum write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 8 oa->write_character(static_cast(0xCC)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 16 oa->write_character(static_cast(0xCD)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 32 oa->write_character(static_cast(0xCE)); write_number(static_cast(j.m_value.number_integer)); } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { // uint 64 oa->write_character(static_cast(0xCF)); write_number(static_cast(j.m_value.number_integer)); } break; } case value_t::number_float: // float 64 { oa->write_character(static_cast(0xCB)); write_number(j.m_value.number_float); break; } case value_t::string: { // step 1: write control byte and the string length const auto N = j.m_value.string->size(); if (N <= 31) { // fixstr write_number(static_cast(0xA0 | N)); } else if (N <= (std::numeric_limits::max)()) { // str 8 oa->write_character(static_cast(0xD9)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // str 16 oa->write_character(static_cast(0xDA)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // str 32 oa->write_character(static_cast(0xDB)); write_number(static_cast(N)); } // step 2: write the string oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); break; } case value_t::array: { // step 1: write control byte and the array size const auto N = j.m_value.array->size(); if (N <= 15) { // fixarray write_number(static_cast(0x90 | N)); } else if (N <= (std::numeric_limits::max)()) { // array 16 oa->write_character(static_cast(0xDC)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // array 32 oa->write_character(static_cast(0xDD)); write_number(static_cast(N)); } // step 2: write each element for (const auto& el : *j.m_value.array) { write_msgpack(el); } break; } case value_t::object: { // step 1: write control byte and the object size const auto N = j.m_value.object->size(); if (N <= 15) { // fixmap write_number(static_cast(0x80 | (N & 0xF))); } else if (N <= (std::numeric_limits::max)()) { // map 16 oa->write_character(static_cast(0xDE)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { // map 32 oa->write_character(static_cast(0xDF)); write_number(static_cast(N)); } // step 2: write each element for (const auto& el : *j.m_value.object) { write_msgpack(el.first); write_msgpack(el.second); } break; } default: break; } } /*! @param[in] j JSON value to serialize @param[in] use_count whether to use '#' prefixes (optimized format) @param[in] use_type whether to use '$' prefixes (optimized format) @param[in] add_prefix whether prefixes need to be used for this value */ void write_ubjson(const BasicJsonType& j, const bool use_count, const bool use_type, const bool add_prefix = true) { switch (j.type()) { case value_t::null: { if (add_prefix) { oa->write_character(static_cast('Z')); } break; } case value_t::boolean: { if (add_prefix) oa->write_character(j.m_value.boolean ? static_cast('T') : static_cast('F')); break; } case value_t::number_integer: { write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); break; } case value_t::number_unsigned: { write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); break; } case value_t::number_float: { write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); break; } case value_t::string: { if (add_prefix) { oa->write_character(static_cast('S')); } write_number_with_ubjson_prefix(j.m_value.string->size(), true); oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); break; } case value_t::array: { if (add_prefix) { oa->write_character(static_cast('[')); } bool prefix_required = true; if (use_type and not j.m_value.array->empty()) { assert(use_count); const char first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin() + 1, j.end(), [this, first_prefix](const BasicJsonType & v) { return ubjson_prefix(v) == first_prefix; }); if (same_prefix) { prefix_required = false; oa->write_character(static_cast('$')); oa->write_character(static_cast(first_prefix)); } } if (use_count) { oa->write_character(static_cast('#')); write_number_with_ubjson_prefix(j.m_value.array->size(), true); } for (const auto& el : *j.m_value.array) { write_ubjson(el, use_count, use_type, prefix_required); } if (not use_count) { oa->write_character(static_cast(']')); } break; } case value_t::object: { if (add_prefix) { oa->write_character(static_cast('{')); } bool prefix_required = true; if (use_type and not j.m_value.object->empty()) { assert(use_count); const char first_prefix = ubjson_prefix(j.front()); const bool same_prefix = std::all_of(j.begin(), j.end(), [this, first_prefix](const BasicJsonType & v) { return ubjson_prefix(v) == first_prefix; }); if (same_prefix) { prefix_required = false; oa->write_character(static_cast('$')); oa->write_character(static_cast(first_prefix)); } } if (use_count) { oa->write_character(static_cast('#')); write_number_with_ubjson_prefix(j.m_value.object->size(), true); } for (const auto& el : *j.m_value.object) { write_number_with_ubjson_prefix(el.first.size(), true); oa->write_characters( reinterpret_cast(el.first.c_str()), el.first.size()); write_ubjson(el.second, use_count, use_type, prefix_required); } if (not use_count) { oa->write_character(static_cast('}')); } break; } default: break; } } private: /* @brief write a number to output input @param[in] n number of type @a NumberType @tparam NumberType the type of the number @note This function needs to respect the system's endianess, because bytes in CBOR, MessagePack, and UBJSON are stored in network order (big endian) and therefore need reordering on little endian systems. */ template void write_number(const NumberType n) { // step 1: write number to array of length NumberType std::array vec; std::memcpy(vec.data(), &n, sizeof(NumberType)); // step 2: write array to output (with possible reordering) if (is_little_endian) { // reverse byte order prior to conversion if necessary std::reverse(vec.begin(), vec.end()); } oa->write_characters(vec.data(), sizeof(NumberType)); } // UBJSON: write number (floating point) template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { if (add_prefix) { oa->write_character(static_cast('D')); // float64 } write_number(n); } // UBJSON: write number (unsigned integer) template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(static_cast('i')); // int8 } write_number(static_cast(n)); } else if (n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(static_cast('U')); // uint8 } write_number(static_cast(n)); } else if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(static_cast('I')); // int16 } write_number(static_cast(n)); } else if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(static_cast('l')); // int32 } write_number(static_cast(n)); } else if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(static_cast('L')); // int64 } write_number(static_cast(n)); } else { JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); } } // UBJSON: write number (signed integer) template::value and not std::is_floating_point::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(static_cast('i')); // int8 } write_number(static_cast(n)); } else if (static_cast((std::numeric_limits::min)()) <= n and n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { oa->write_character(static_cast('U')); // uint8 } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(static_cast('I')); // int16 } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(static_cast('l')); // int32 } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { if (add_prefix) { oa->write_character(static_cast('L')); // int64 } write_number(static_cast(n)); } // LCOV_EXCL_START else { JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); } // LCOV_EXCL_STOP } /*! @brief determine the type prefix of container values @note This function does not need to be 100% accurate when it comes to integer limits. In case a number exceeds the limits of int64_t, this will be detected by a later call to function write_number_with_ubjson_prefix. Therefore, we return 'L' for any value that does not fit the previous limits. */ char ubjson_prefix(const BasicJsonType& j) const noexcept { switch (j.type()) { case value_t::null: return 'Z'; case value_t::boolean: return j.m_value.boolean ? 'T' : 'F'; case value_t::number_integer: { if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'i'; } else if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'U'; } else if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'I'; } else if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) { return 'l'; } else // no check and assume int64_t (see note above) { return 'L'; } } case value_t::number_unsigned: { if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { return 'i'; } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { return 'U'; } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { return 'I'; } else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) { return 'l'; } else // no check and assume int64_t (see note above) { return 'L'; } } case value_t::number_float: return 'D'; case value_t::string: return 'S'; case value_t::array: return '['; case value_t::object: return '{'; default: // discarded values return 'N'; } } private: /// whether we can assume little endianess const bool is_little_endian = binary_reader::little_endianess(); /// the output output_adapter_t oa = nullptr; }; } } // #include #include // reverse, remove, fill, find, none_of #include // array #include // assert #include // and, or #include // localeconv, lconv #include // labs, isfinite, isnan, signbit #include // size_t, ptrdiff_t #include // uint8_t #include // snprintf #include // setfill #include // next #include // numeric_limits #include // string #include // stringstream #include // is_same // #include // #include #include // assert #include // or, and, not #include // signbit, isfinite #include // intN_t, uintN_t #include // memcpy, memmove namespace nlohmann { namespace detail { /*! @brief implements the Grisu2 algorithm for binary to decimal floating-point conversion. This implementation is a slightly modified version of the reference implementation which may be obtained from http://florian.loitsch.com/publications (bench.tar.gz). The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. For a detailed description of the algorithm see: [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, PLDI 2010 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, PLDI 1996 */ namespace dtoa_impl { template Target reinterpret_bits(const Source source) { static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); Target target; std::memcpy(&target, &source, sizeof(Source)); return target; } struct diyfp // f * 2^e { static constexpr int kPrecision = 64; // = q uint64_t f; int e; constexpr diyfp() noexcept : f(0), e(0) {} constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {} /*! @brief returns x - y @pre x.e == y.e and x.f >= y.f */ static diyfp sub(const diyfp& x, const diyfp& y) noexcept { assert(x.e == y.e); assert(x.f >= y.f); return diyfp(x.f - y.f, x.e); } /*! @brief returns x * y @note The result is rounded. (Only the upper q bits are returned.) */ static diyfp mul(const diyfp& x, const diyfp& y) noexcept { static_assert(kPrecision == 64, "internal error"); // Computes: // f = round((x.f * y.f) / 2^q) // e = x.e + y.e + q // Emulate the 64-bit * 64-bit multiplication: // // p = u * v // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) // // (Since Q might be larger than 2^32 - 1) // // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) // // (Q_hi + H does not overflow a 64-bit int) // // = p_lo + 2^64 p_hi const uint64_t u_lo = x.f & 0xFFFFFFFF; const uint64_t u_hi = x.f >> 32; const uint64_t v_lo = y.f & 0xFFFFFFFF; const uint64_t v_hi = y.f >> 32; const uint64_t p0 = u_lo * v_lo; const uint64_t p1 = u_lo * v_hi; const uint64_t p2 = u_hi * v_lo; const uint64_t p3 = u_hi * v_hi; const uint64_t p0_hi = p0 >> 32; const uint64_t p1_lo = p1 & 0xFFFFFFFF; const uint64_t p1_hi = p1 >> 32; const uint64_t p2_lo = p2 & 0xFFFFFFFF; const uint64_t p2_hi = p2 >> 32; uint64_t Q = p0_hi + p1_lo + p2_lo; // The full product might now be computed as // // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) // p_lo = p0_lo + (Q << 32) // // But in this particular case here, the full p_lo is not required. // Effectively we only need to add the highest bit in p_lo to p_hi (and // Q_hi + 1 does not overflow). Q += uint64_t{1} << (64 - 32 - 1); // round, ties up const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32); return diyfp(h, x.e + y.e + 64); } /*! @brief normalize x such that the significand is >= 2^(q-1) @pre x.f != 0 */ static diyfp normalize(diyfp x) noexcept { assert(x.f != 0); while ((x.f >> 63) == 0) { x.f <<= 1; x.e--; } return x; } /*! @brief normalize x such that the result has the exponent E @pre e >= x.e and the upper e - x.e bits of x.f must be zero. */ static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept { const int delta = x.e - target_exponent; assert(delta >= 0); assert(((x.f << delta) >> delta) == x.f); return diyfp(x.f << delta, target_exponent); } }; struct boundaries { diyfp w; diyfp minus; diyfp plus; }; /*! Compute the (normalized) diyfp representing the input number 'value' and its boundaries. @pre value must be finite and positive */ template boundaries compute_boundaries(FloatType value) { assert(std::isfinite(value)); assert(value > 0); // Convert the IEEE representation into a diyfp. // // If v is denormal: // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) // If v is normalized: // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) static_assert(std::numeric_limits::is_iec559, "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); constexpr int kMinExp = 1 - kBias; constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1) using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type; const uint64_t bits = reinterpret_bits(value); const uint64_t E = bits >> (kPrecision - 1); const uint64_t F = bits & (kHiddenBit - 1); const bool is_denormal = (E == 0); const diyfp v = is_denormal ? diyfp(F, kMinExp) : diyfp(F + kHiddenBit, static_cast(E) - kBias); // Compute the boundaries m- and m+ of the floating-point value // v = f * 2^e. // // Determine v- and v+, the floating-point predecessor and successor if v, // respectively. // // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) // // v+ = v + 2^e // // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ // between m- and m+ round to v, regardless of how the input rounding // algorithm breaks ties. // // ---+-------------+-------------+-------------+-------------+--- (A) // v- m- v m+ v+ // // -----------------+------+------+-------------+-------------+--- (B) // v- m- v m+ v+ const bool lower_boundary_is_closer = (F == 0 and E > 1); const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); const diyfp m_minus = lower_boundary_is_closer ? diyfp(4 * v.f - 1, v.e - 2) // (B) : diyfp(2 * v.f - 1, v.e - 1); // (A) // Determine the normalized w+ = m+. const diyfp w_plus = diyfp::normalize(m_plus); // Determine w- = m- such that e_(w-) = e_(w+). const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); return {diyfp::normalize(v), w_minus, w_plus}; } // Given normalized diyfp w, Grisu needs to find a (normalized) cached // power-of-ten c, such that the exponent of the product c * w = f * 2^e lies // within a certain range [alpha, gamma] (Definition 3.2 from [1]) // // alpha <= e = e_c + e_w + q <= gamma // // or // // f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q // <= f_c * f_w * 2^gamma // // Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies // // 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma // // or // // 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) // // The choice of (alpha,gamma) determines the size of the table and the form of // the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well // in practice: // // The idea is to cut the number c * w = f * 2^e into two parts, which can be // processed independently: An integral part p1, and a fractional part p2: // // f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e // = (f div 2^-e) + (f mod 2^-e) * 2^e // = p1 + p2 * 2^e // // The conversion of p1 into decimal form requires a series of divisions and // modulos by (a power of) 10. These operations are faster for 32-bit than for // 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be // achieved by choosing // // -e >= 32 or e <= -32 := gamma // // In order to convert the fractional part // // p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... // // into decimal form, the fraction is repeatedly multiplied by 10 and the digits // d[-i] are extracted in order: // // (10 * p2) div 2^-e = d[-1] // (10 * p2) mod 2^-e = d[-2] / 10^1 + ... // // The multiplication by 10 must not overflow. It is sufficient to choose // // 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. // // Since p2 = f mod 2^-e < 2^-e, // // -e <= 60 or e >= -60 := alpha constexpr int kAlpha = -60; constexpr int kGamma = -32; struct cached_power // c = f * 2^e ~= 10^k { uint64_t f; int e; int k; }; /*! For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c satisfies (Definition 3.2 from [1]) alpha <= e_c + e + q <= gamma. */ inline cached_power get_cached_power_for_binary_exponent(int e) { // Now // // alpha <= e_c + e + q <= gamma (1) // ==> f_c * 2^alpha <= c * 2^e * 2^q // // and since the c's are normalized, 2^(q-1) <= f_c, // // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) // ==> 2^(alpha - e - 1) <= c // // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as // // k = ceil( log_10( 2^(alpha - e - 1) ) ) // = ceil( (alpha - e - 1) * log_10(2) ) // // From the paper: // "In theory the result of the procedure could be wrong since c is rounded, // and the computation itself is approximated [...]. In practice, however, // this simple function is sufficient." // // For IEEE double precision floating-point numbers converted into // normalized diyfp's w = f * 2^e, with q = 64, // // e >= -1022 (min IEEE exponent) // -52 (p - 1) // -52 (p - 1, possibly normalize denormal IEEE numbers) // -11 (normalize the diyfp) // = -1137 // // and // // e <= +1023 (max IEEE exponent) // -52 (p - 1) // -11 (normalize the diyfp) // = 960 // // This binary exponent range [-1137,960] results in a decimal exponent // range [-307,324]. One does not need to store a cached power for each // k in this range. For each such k it suffices to find a cached power // such that the exponent of the product lies in [alpha,gamma]. // This implies that the difference of the decimal exponents of adjacent // table entries must be less than or equal to // // floor( (gamma - alpha) * log_10(2) ) = 8. // // (A smaller distance gamma-alpha would require a larger table.) // NB: // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. constexpr int kCachedPowersSize = 79; constexpr int kCachedPowersMinDecExp = -300; constexpr int kCachedPowersDecStep = 8; static constexpr cached_power kCachedPowers[] = { { 0xAB70FE17C79AC6CA, -1060, -300 }, { 0xFF77B1FCBEBCDC4F, -1034, -292 }, { 0xBE5691EF416BD60C, -1007, -284 }, { 0x8DD01FAD907FFC3C, -980, -276 }, { 0xD3515C2831559A83, -954, -268 }, { 0x9D71AC8FADA6C9B5, -927, -260 }, { 0xEA9C227723EE8BCB, -901, -252 }, { 0xAECC49914078536D, -874, -244 }, { 0x823C12795DB6CE57, -847, -236 }, { 0xC21094364DFB5637, -821, -228 }, { 0x9096EA6F3848984F, -794, -220 }, { 0xD77485CB25823AC7, -768, -212 }, { 0xA086CFCD97BF97F4, -741, -204 }, { 0xEF340A98172AACE5, -715, -196 }, { 0xB23867FB2A35B28E, -688, -188 }, { 0x84C8D4DFD2C63F3B, -661, -180 }, { 0xC5DD44271AD3CDBA, -635, -172 }, { 0x936B9FCEBB25C996, -608, -164 }, { 0xDBAC6C247D62A584, -582, -156 }, { 0xA3AB66580D5FDAF6, -555, -148 }, { 0xF3E2F893DEC3F126, -529, -140 }, { 0xB5B5ADA8AAFF80B8, -502, -132 }, { 0x87625F056C7C4A8B, -475, -124 }, { 0xC9BCFF6034C13053, -449, -116 }, { 0x964E858C91BA2655, -422, -108 }, { 0xDFF9772470297EBD, -396, -100 }, { 0xA6DFBD9FB8E5B88F, -369, -92 }, { 0xF8A95FCF88747D94, -343, -84 }, { 0xB94470938FA89BCF, -316, -76 }, { 0x8A08F0F8BF0F156B, -289, -68 }, { 0xCDB02555653131B6, -263, -60 }, { 0x993FE2C6D07B7FAC, -236, -52 }, { 0xE45C10C42A2B3B06, -210, -44 }, { 0xAA242499697392D3, -183, -36 }, { 0xFD87B5F28300CA0E, -157, -28 }, { 0xBCE5086492111AEB, -130, -20 }, { 0x8CBCCC096F5088CC, -103, -12 }, { 0xD1B71758E219652C, -77, -4 }, { 0x9C40000000000000, -50, 4 }, { 0xE8D4A51000000000, -24, 12 }, { 0xAD78EBC5AC620000, 3, 20 }, { 0x813F3978F8940984, 30, 28 }, { 0xC097CE7BC90715B3, 56, 36 }, { 0x8F7E32CE7BEA5C70, 83, 44 }, { 0xD5D238A4ABE98068, 109, 52 }, { 0x9F4F2726179A2245, 136, 60 }, { 0xED63A231D4C4FB27, 162, 68 }, { 0xB0DE65388CC8ADA8, 189, 76 }, { 0x83C7088E1AAB65DB, 216, 84 }, { 0xC45D1DF942711D9A, 242, 92 }, { 0x924D692CA61BE758, 269, 100 }, { 0xDA01EE641A708DEA, 295, 108 }, { 0xA26DA3999AEF774A, 322, 116 }, { 0xF209787BB47D6B85, 348, 124 }, { 0xB454E4A179DD1877, 375, 132 }, { 0x865B86925B9BC5C2, 402, 140 }, { 0xC83553C5C8965D3D, 428, 148 }, { 0x952AB45CFA97A0B3, 455, 156 }, { 0xDE469FBD99A05FE3, 481, 164 }, { 0xA59BC234DB398C25, 508, 172 }, { 0xF6C69A72A3989F5C, 534, 180 }, { 0xB7DCBF5354E9BECE, 561, 188 }, { 0x88FCF317F22241E2, 588, 196 }, { 0xCC20CE9BD35C78A5, 614, 204 }, { 0x98165AF37B2153DF, 641, 212 }, { 0xE2A0B5DC971F303A, 667, 220 }, { 0xA8D9D1535CE3B396, 694, 228 }, { 0xFB9B7CD9A4A7443C, 720, 236 }, { 0xBB764C4CA7A44410, 747, 244 }, { 0x8BAB8EEFB6409C1A, 774, 252 }, { 0xD01FEF10A657842C, 800, 260 }, { 0x9B10A4E5E9913129, 827, 268 }, { 0xE7109BFBA19C0C9D, 853, 276 }, { 0xAC2820D9623BF429, 880, 284 }, { 0x80444B5E7AA7CF85, 907, 292 }, { 0xBF21E44003ACDD2D, 933, 300 }, { 0x8E679C2F5E44FF8F, 960, 308 }, { 0xD433179D9C8CB841, 986, 316 }, { 0x9E19DB92B4E31BA9, 1013, 324 }, }; // This computation gives exactly the same results for k as // k = ceil((kAlpha - e - 1) * 0.30102999566398114) // for |e| <= 1500, but doesn't require floating-point operations. // NB: log_10(2) ~= 78913 / 2^18 assert(e >= -1500); assert(e <= 1500); const int f = kAlpha - e - 1; const int k = (f * 78913) / (1 << 18) + (f > 0); const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; assert(index >= 0); assert(index < kCachedPowersSize); static_cast(kCachedPowersSize); // Fix warning. const cached_power cached = kCachedPowers[index]; assert(kAlpha <= cached.e + e + 64); assert(kGamma >= cached.e + e + 64); return cached; } /*! For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. For n == 0, returns 1 and sets pow10 := 1. */ inline int find_largest_pow10(const uint32_t n, uint32_t& pow10) { // LCOV_EXCL_START if (n >= 1000000000) { pow10 = 1000000000; return 10; } // LCOV_EXCL_STOP else if (n >= 100000000) { pow10 = 100000000; return 9; } else if (n >= 10000000) { pow10 = 10000000; return 8; } else if (n >= 1000000) { pow10 = 1000000; return 7; } else if (n >= 100000) { pow10 = 100000; return 6; } else if (n >= 10000) { pow10 = 10000; return 5; } else if (n >= 1000) { pow10 = 1000; return 4; } else if (n >= 100) { pow10 = 100; return 3; } else if (n >= 10) { pow10 = 10; return 2; } else { pow10 = 1; return 1; } } inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, uint64_t rest, uint64_t ten_k) { assert(len >= 1); assert(dist <= delta); assert(rest <= delta); assert(ten_k > 0); // <--------------------------- delta ----> // <---- dist ---------> // --------------[------------------+-------------------]-------------- // M- w M+ // // ten_k // <------> // <---- rest ----> // --------------[------------------+----+--------------]-------------- // w V // = buf * 10^k // // ten_k represents a unit-in-the-last-place in the decimal representation // stored in buf. // Decrement buf by ten_k while this takes buf closer to w. // The tests are written in this order to avoid overflow in unsigned // integer arithmetic. while (rest < dist and delta - rest >= ten_k and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) { assert(buf[len - 1] != '0'); buf[len - 1]--; rest += ten_k; } } /*! Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. M- and M+ must be normalized and share the same exponent -60 <= e <= -32. */ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, diyfp M_minus, diyfp w, diyfp M_plus) { static_assert(kAlpha >= -60, "internal error"); static_assert(kGamma <= -32, "internal error"); // Generates the digits (and the exponent) of a decimal floating-point // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. // // <--------------------------- delta ----> // <---- dist ---------> // --------------[------------------+-------------------]-------------- // M- w M+ // // Grisu2 generates the digits of M+ from left to right and stops as soon as // V is in [M-,M+]. assert(M_plus.e >= kAlpha); assert(M_plus.e <= kGamma); uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): // // M+ = f * 2^e // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e // = ((p1 ) * 2^-e + (p2 )) * 2^e // = p1 + p2 * 2^e const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e); uint32_t p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e // 1) // // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] assert(p1 > 0); uint32_t pow10; const int k = find_largest_pow10(p1, pow10); // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) // // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) // // M+ = p1 + p2 * 2^e // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e // = d[k-1] * 10^(k-1) + ( rest) * 2^e // // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) // // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] // // but stop as soon as // // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e int n = k; while (n > 0) { // Invariants: // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) // pow10 = 10^(n-1) <= p1 < 10^n // const uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) const uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) // // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) // assert(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) // p1 = r; n--; // // M+ = buffer * 10^n + (p1 + p2 * 2^e) // pow10 = 10^n // // Now check if enough digits have been generated. // Compute // // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e // // Note: // Since rest and delta share the same exponent e, it suffices to // compare the significands. const uint64_t rest = (uint64_t{p1} << -one.e) + p2; if (rest <= delta) { // V = buffer * 10^n, with M- <= V <= M+. decimal_exponent += n; // We may now just stop. But instead look if the buffer could be // decremented to bring V closer to w. // // pow10 = 10^n is now 1 ulp in the decimal representation V. // The rounding procedure works with diyfp's with an implicit // exponent of e. // // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e // const uint64_t ten_n = uint64_t{pow10} << -one.e; grisu2_round(buffer, length, dist, delta, rest, ten_n); return; } pow10 /= 10; // // pow10 = 10^(n-1) <= p1 < 10^n // Invariants restored. } // 2) // // The digits of the integral part have been generated: // // M+ = d[k-1]...d[1]d[0] + p2 * 2^e // = buffer + p2 * 2^e // // Now generate the digits of the fractional part p2 * 2^e. // // Note: // No decimal point is generated: the exponent is adjusted instead. // // p2 actually represents the fraction // // p2 * 2^e // = p2 / 2^-e // = d[-1] / 10^1 + d[-2] / 10^2 + ... // // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) // // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) // // using // // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) // = ( d) * 2^-e + ( r) // // or // 10^m * p2 * 2^e = d + r * 2^e // // i.e. // // M+ = buffer + p2 * 2^e // = buffer + 10^-m * (d + r * 2^e) // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e // // and stop as soon as 10^-m * r * 2^e <= delta * 2^e assert(p2 > delta); int m = 0; for (;;) { // Invariant: // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e // = buffer * 10^-m + 10^-m * (p2 ) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e // assert(p2 <= UINT64_MAX / 10); p2 *= 10; const uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e // // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e // assert(d <= 9); buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d // // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e // p2 = r; m++; // // M+ = buffer * 10^-m + 10^-m * p2 * 2^e // Invariant restored. // Check if enough digits have been generated. // // 10^-m * p2 * 2^e <= delta * 2^e // p2 * 2^e <= 10^m * delta * 2^e // p2 <= 10^m * delta delta *= 10; dist *= 10; if (p2 <= delta) { break; } } // V = buffer * 10^-m, with M- <= V <= M+. decimal_exponent -= m; // 1 ulp in the decimal representation is now 10^-m. // Since delta and dist are now scaled by 10^m, we need to do the // same with ulp in order to keep the units in sync. // // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e // const uint64_t ten_m = one.f; grisu2_round(buffer, length, dist, delta, p2, ten_m); // By construction this algorithm generates the shortest possible decimal // number (Loitsch, Theorem 6.2) which rounds back to w. // For an input number of precision p, at least // // N = 1 + ceil(p * log_10(2)) // // decimal digits are sufficient to identify all binary floating-point // numbers (Matula, "In-and-Out conversions"). // This implies that the algorithm does not produce more than N decimal // digits. // // N = 17 for p = 53 (IEEE double precision) // N = 9 for p = 24 (IEEE single precision) } /*! v = buf * 10^decimal_exponent len is the length of the buffer (number of decimal digits) The buffer must be large enough, i.e. >= max_digits10. */ inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) { assert(m_plus.e == m_minus.e); assert(m_plus.e == v.e); // --------(-----------------------+-----------------------)-------- (A) // m- v m+ // // --------------------(-----------+-----------------------)-------- (B) // m- v m+ // // First scale v (and m- and m+) such that the exponent is in the range // [alpha, gamma]. const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] const diyfp w = diyfp::mul(v, c_minus_k); const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); // ----(---+---)---------------(---+---)---------------(---+---)---- // w- w w+ // = c*m- = c*v = c*m+ // // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and // w+ are now off by a small amount. // In fact: // // w - v * 10^k < 1 ulp // // To account for this inaccuracy, add resp. subtract 1 ulp. // // --------+---[---------------(---+---)---------------]---+-------- // w- M- w M+ w+ // // Now any number in [M-, M+] (bounds included) will round to w when input, // regardless of how the input rounding algorithm breaks ties. // // And digit_gen generates the shortest possible such number in [M-, M+]. // Note that this does not mean that Grisu2 always generates the shortest // possible number in the interval (m-, m+). const diyfp M_minus(w_minus.f + 1, w_minus.e); const diyfp M_plus (w_plus.f - 1, w_plus.e ); decimal_exponent = -cached.k; // = -(-k) = k grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); } /*! v = buf * 10^decimal_exponent len is the length of the buffer (number of decimal digits) The buffer must be large enough, i.e. >= max_digits10. */ template void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) { static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, "internal error: not enough precision"); assert(std::isfinite(value)); assert(value > 0); // If the neighbors (and boundaries) of 'value' are always computed for double-precision // numbers, all float's can be recovered using strtod (and strtof). However, the resulting // decimal representations are not exactly "short". // // The documentation for 'std::to_chars' (http://en.cppreference.com/w/cpp/utility/to_chars) // says "value is converted to a string as if by std::sprintf in the default ("C") locale" // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' // does. // On the other hand, the documentation for 'std::to_chars' requires that "parsing the // representation using the corresponding std::from_chars function recovers value exactly". That // indicates that single precision floating-point numbers should be recovered using // 'std::strtof'. // // NB: If the neighbors are computed for single-precision numbers, there is a single float // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision // value is off by 1 ulp. #if 0 const boundaries w = compute_boundaries(static_cast(value)); #else const boundaries w = compute_boundaries(value); #endif grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); } /*! @brief appends a decimal representation of e to buf @return a pointer to the element following the exponent. @pre -1000 < e < 1000 */ inline char* append_exponent(char* buf, int e) { assert(e > -1000); assert(e < 1000); if (e < 0) { e = -e; *buf++ = '-'; } else { *buf++ = '+'; } uint32_t k = static_cast(e); if (k < 10) { // Always print at least two digits in the exponent. // This is for compatibility with printf("%g"). *buf++ = '0'; *buf++ = static_cast('0' + k); } else if (k < 100) { *buf++ = static_cast('0' + k / 10); k %= 10; *buf++ = static_cast('0' + k); } else { *buf++ = static_cast('0' + k / 100); k %= 100; *buf++ = static_cast('0' + k / 10); k %= 10; *buf++ = static_cast('0' + k); } return buf; } /*! @brief prettify v = buf * 10^decimal_exponent If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point notation. Otherwise it will be printed in exponential notation. @pre min_exp < 0 @pre max_exp > 0 */ inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp) { assert(min_exp < 0); assert(max_exp > 0); const int k = len; const int n = len + decimal_exponent; // v = buf * 10^(n-k) // k is the length of the buffer (number of decimal digits) // n is the position of the decimal point relative to the start of the buffer. if (k <= n and n <= max_exp) { // digits[000] // len <= max_exp + 2 std::memset(buf + k, '0', static_cast(n - k)); // Make it look like a floating-point number (#362, #378) buf[n + 0] = '.'; buf[n + 1] = '0'; return buf + (n + 2); } if (0 < n and n <= max_exp) { // dig.its // len <= max_digits10 + 1 assert(k > n); std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); buf[n] = '.'; return buf + (k + 1); } if (min_exp < n and n <= 0) { // 0.[000]digits // len <= 2 + (-min_exp - 1) + max_digits10 std::memmove(buf + (2 + -n), buf, static_cast(k)); buf[0] = '0'; buf[1] = '.'; std::memset(buf + 2, '0', static_cast(-n)); return buf + (2 + (-n) + k); } if (k == 1) { // dE+123 // len <= 1 + 5 buf += 1; } else { // d.igitsE+123 // len <= max_digits10 + 1 + 5 std::memmove(buf + 2, buf + 1, static_cast(k - 1)); buf[1] = '.'; buf += 1 + k; } *buf++ = 'e'; return append_exponent(buf, n - 1); } } // namespace dtoa_impl /*! @brief generates a decimal representation of the floating-point number value in [first, last). The format of the resulting decimal representation is similar to printf's %g format. Returns an iterator pointing past-the-end of the decimal representation. @note The input number must be finite, i.e. NaN's and Inf's are not supported. @note The buffer must be large enough. @note The result is NOT null-terminated. */ template char* to_chars(char* first, char* last, FloatType value) { static_cast(last); // maybe unused - fix warning assert(std::isfinite(value)); // Use signbit(value) instead of (value < 0) since signbit works for -0. if (std::signbit(value)) { value = -value; *first++ = '-'; } if (value == 0) // +-0 { *first++ = '0'; // Make it look like a floating-point number (#362, #378) *first++ = '.'; *first++ = '0'; return first; } assert(last - first >= std::numeric_limits::max_digits10); // Compute v = buffer * 10^decimal_exponent. // The decimal digits are stored in the buffer, which needs to be interpreted // as an unsigned decimal integer. // len is the length of the buffer, i.e. the number of decimal digits. int len = 0; int decimal_exponent = 0; dtoa_impl::grisu2(first, len, decimal_exponent, value); assert(len <= std::numeric_limits::max_digits10); // Format the buffer like printf("%.*g", prec, value) constexpr int kMinExp = -4; // Use digits10 here to increase compatibility with version 2. constexpr int kMaxExp = std::numeric_limits::digits10; assert(last - first >= kMaxExp + 2); assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); assert(last - first >= std::numeric_limits::max_digits10 + 6); return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); } } // namespace detail } // namespace nlohmann // #include // #include // #include // #include namespace nlohmann { namespace detail { /////////////////// // serialization // /////////////////// template class serializer { using string_t = typename BasicJsonType::string_t; using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; static constexpr uint8_t UTF8_ACCEPT = 0; static constexpr uint8_t UTF8_REJECT = 1; public: /*! @param[in] s output stream to serialize to @param[in] ichar indentation character to use */ serializer(output_adapter_t s, const char ichar) : o(std::move(s)), loc(std::localeconv()), thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)), decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)), indent_char(ichar), indent_string(512, indent_char) {} // delete because of pointer members serializer(const serializer&) = delete; serializer& operator=(const serializer&) = delete; /*! @brief internal implementation of the serialization function This function is called by the public member function dump and organizes the serialization internally. The indentation level is propagated as additional parameter. In case of arrays and objects, the function is called recursively. - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format @param[in] val value to serialize @param[in] pretty_print whether the output shall be pretty-printed @param[in] indent_step the indent level @param[in] current_indent the current indent level (only used internally) */ void dump(const BasicJsonType& val, const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, const unsigned int current_indent = 0) { switch (val.m_type) { case value_t::object: { if (val.m_value.object->empty()) { o->write_characters("{}", 2); return; } if (pretty_print) { o->write_characters("{\n", 2); // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; if (JSON_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } // first n-1 elements auto i = val.m_value.object->cbegin(); for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element assert(i != val.m_value.object->cend()); assert(std::next(i) == val.m_value.object->cend()); o->write_characters(indent_string.c_str(), new_indent); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); o->write_character('}'); } else { o->write_character('{'); // first n-1 elements auto i = val.m_value.object->cbegin(); for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) { o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element assert(i != val.m_value.object->cend()); assert(std::next(i) == val.m_value.object->cend()); o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character('}'); } return; } case value_t::array: { if (val.m_value.array->empty()) { o->write_characters("[]", 2); return; } if (pretty_print) { o->write_characters("[\n", 2); // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; if (JSON_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { o->write_characters(indent_string.c_str(), new_indent); dump(*i, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element assert(not val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); o->write_character(']'); } else { o->write_character('['); // first n-1 elements for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { dump(*i, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element assert(not val.m_value.array->empty()); dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); } return; } case value_t::string: { o->write_character('\"'); dump_escaped(*val.m_value.string, ensure_ascii); o->write_character('\"'); return; } case value_t::boolean: { if (val.m_value.boolean) { o->write_characters("true", 4); } else { o->write_characters("false", 5); } return; } case value_t::number_integer: { dump_integer(val.m_value.number_integer); return; } case value_t::number_unsigned: { dump_integer(val.m_value.number_unsigned); return; } case value_t::number_float: { dump_float(val.m_value.number_float); return; } case value_t::discarded: { o->write_characters("", 11); return; } case value_t::null: { o->write_characters("null", 4); return; } } } private: /*! @brief dump escaped string Escape a string by replacing certain special characters by a sequence of an escape character (backslash) and another character and other control characters by a sequence of "\u" followed by a four-digit hex representation. The escaped string is written to output stream @a o. @param[in] s the string to escape @param[in] ensure_ascii whether to escape non-ASCII characters with \uXXXX sequences @complexity Linear in the length of string @a s. */ void dump_escaped(const string_t& s, const bool ensure_ascii) { uint32_t codepoint; uint8_t state = UTF8_ACCEPT; std::size_t bytes = 0; // number of bytes written to string_buffer for (std::size_t i = 0; i < s.size(); ++i) { const auto byte = static_cast(s[i]); switch (decode(state, codepoint, byte)) { case UTF8_ACCEPT: // decode found a new code point { switch (codepoint) { case 0x08: // backspace { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'b'; break; } case 0x09: // horizontal tab { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 't'; break; } case 0x0A: // newline { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'n'; break; } case 0x0C: // formfeed { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'f'; break; } case 0x0D: // carriage return { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = 'r'; break; } case 0x22: // quotation mark { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = '\"'; break; } case 0x5C: // reverse solidus { string_buffer[bytes++] = '\\'; string_buffer[bytes++] = '\\'; break; } default: { // escape control characters (0x00..0x1F) or, if // ensure_ascii parameter is used, non-ASCII characters if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F))) { if (codepoint <= 0xFFFF) { std::snprintf(string_buffer.data() + bytes, 7, "\\u%04x", static_cast(codepoint)); bytes += 6; } else { std::snprintf(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", static_cast(0xD7C0 + (codepoint >> 10)), static_cast(0xDC00 + (codepoint & 0x3FF))); bytes += 12; } } else { // copy byte to buffer (all previous bytes // been copied have in default case above) string_buffer[bytes++] = s[i]; } break; } } // write buffer and reset index; there must be 13 bytes // left, as this is the maximal number of bytes to be // written ("\uxxxx\uxxxx\0") for one code point if (string_buffer.size() - bytes < 13) { o->write_characters(string_buffer.data(), bytes); bytes = 0; } break; } case UTF8_REJECT: // decode found invalid UTF-8 byte { std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast(byte); JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str())); } default: // decode found yet incomplete multi-byte code point { if (not ensure_ascii) { // code point will not be escaped - copy byte to buffer string_buffer[bytes++] = s[i]; } break; } } } if (JSON_LIKELY(state == UTF8_ACCEPT)) { // write buffer if (bytes > 0) { o->write_characters(string_buffer.data(), bytes); } } else { // we finish reading, but do not accept: string was incomplete std::stringstream ss; ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast(static_cast(s.back())); JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str())); } } /*! @brief dump an integer Dump a given integer to output stream @a o. Works internally with @a number_buffer. @param[in] x integer number (signed or unsigned) to dump @tparam NumberType either @a number_integer_t or @a number_unsigned_t */ template::value or std::is_same::value, int> = 0> void dump_integer(NumberType x) { // special case for "0" if (x == 0) { o->write_character('0'); return; } const bool is_negative = (x <= 0) and (x != 0); // see issue #755 std::size_t i = 0; while (x != 0) { // spare 1 byte for '\0' assert(i < number_buffer.size() - 1); const auto digit = std::labs(static_cast(x % 10)); number_buffer[i++] = static_cast('0' + digit); x /= 10; } if (is_negative) { // make sure there is capacity for the '-' assert(i < number_buffer.size() - 2); number_buffer[i++] = '-'; } std::reverse(number_buffer.begin(), number_buffer.begin() + i); o->write_characters(number_buffer.data(), i); } /*! @brief dump a floating-point number Dump a given floating-point number to output stream @a o. Works internally with @a number_buffer. @param[in] x floating-point number to dump */ void dump_float(number_float_t x) { // NaN / inf if (not std::isfinite(x)) { o->write_characters("null", 4); return; } // If number_float_t is an IEEE-754 single or double precision number, // use the Grisu2 algorithm to produce short numbers which are // guaranteed to round-trip, using strtof and strtod, resp. // // NB: The test below works if == . static constexpr bool is_ieee_single_or_double = (std::numeric_limits::is_iec559 and std::numeric_limits::digits == 24 and std::numeric_limits::max_exponent == 128) or (std::numeric_limits::is_iec559 and std::numeric_limits::digits == 53 and std::numeric_limits::max_exponent == 1024); dump_float(x, std::integral_constant()); } void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) { char* begin = number_buffer.data(); char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); o->write_characters(begin, static_cast(end - begin)); } void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) { // get number of digits for a float -> text -> float round-trip static constexpr auto d = std::numeric_limits::max_digits10; // the actual conversion std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x); // negative value indicates an error assert(len > 0); // check if buffer was large enough assert(static_cast(len) < number_buffer.size()); // erase thousands separator if (thousands_sep != '\0') { const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); std::fill(end, number_buffer.end(), '\0'); assert((end - number_buffer.begin()) <= len); len = (end - number_buffer.begin()); } // convert decimal point to '.' if (decimal_point != '\0' and decimal_point != '.') { const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); if (dec_pos != number_buffer.end()) { *dec_pos = '.'; } } o->write_characters(number_buffer.data(), static_cast(len)); // determine if need to append ".0" const bool value_is_int_like = std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, [](char c) { return (c == '.' or c == 'e'); }); if (value_is_int_like) { o->write_characters(".0", 2); } } /*! @brief check whether a string is UTF-8 encoded The function checks each byte of a string whether it is UTF-8 encoded. The result of the check is stored in the @a state parameter. The function must be called initially with state 0 (accept). State 1 means the string must be rejected, because the current byte is not allowed. If the string is completely processed, but the state is non-zero, the string ended prematurely; that is, the last byte indicated more bytes should have followed. @param[in,out] state the state of the decoding @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) @param[in] byte next byte to decode @return new state @note The function has been edited: a std::array is used. @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ */ static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept { static const std::array utf8d = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 } }; const uint8_t type = utf8d[byte]; codep = (state != UTF8_ACCEPT) ? (byte & 0x3fu) | (codep << 6) : static_cast(0xff >> type) & (byte); state = utf8d[256u + state * 16u + type]; return state; } private: /// the output of the serializer output_adapter_t o = nullptr; /// a (hopefully) large enough character buffer std::array number_buffer{{}}; /// the locale const std::lconv* loc = nullptr; /// the locale's thousand separator character const char thousands_sep = '\0'; /// the locale's decimal point character const char decimal_point = '\0'; /// string buffer std::array string_buffer{{}}; /// the indentation character const char indent_char; /// the indentation string string_t indent_string; }; } } // #include #include #include namespace nlohmann { namespace detail { template class json_ref { public: using value_type = BasicJsonType; json_ref(value_type&& value) : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) {} json_ref(const value_type& value) : value_ref(const_cast(&value)), is_rvalue(false) {} json_ref(std::initializer_list init) : owned_value(init), value_ref(&owned_value), is_rvalue(true) {} template json_ref(Args&& ... args) : owned_value(std::forward(args)...), value_ref(&owned_value), is_rvalue(true) {} // class should be movable only json_ref(json_ref&&) = default; json_ref(const json_ref&) = delete; json_ref& operator=(const json_ref&) = delete; value_type moved_or_copied() const { if (is_rvalue) { return std::move(*value_ref); } return *value_ref; } value_type const& operator*() const { return *static_cast(value_ref); } value_type const* operator->() const { return static_cast(value_ref); } private: mutable value_type owned_value = nullptr; value_type* value_ref = nullptr; const bool is_rvalue; }; } } // #include #include // assert #include // accumulate #include // string #include // vector // #include // #include // #include namespace nlohmann { template class json_pointer { // allow basic_json to access private members NLOHMANN_BASIC_JSON_TPL_DECLARATION friend class basic_json; public: /*! @brief create JSON pointer Create a JSON pointer according to the syntax described in [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). @param[in] s string representing the JSON pointer; if omitted, the empty string is assumed which references the whole JSON value @throw parse_error.107 if the given JSON pointer @a s is nonempty and does not begin with a slash (`/`); see example below @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is not followed by `0` (representing `~`) or `1` (representing `/`); see example below @liveexample{The example shows the construction several valid JSON pointers as well as the exceptional behavior.,json_pointer} @since version 2.0.0 */ explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} /*! @brief return a string representation of the JSON pointer @invariant For each JSON pointer `ptr`, it holds: @code {.cpp} ptr == json_pointer(ptr.to_string()); @endcode @return a string representation of the JSON pointer @liveexample{The example shows the result of `to_string`., json_pointer__to_string} @since version 2.0.0 */ std::string to_string() const noexcept { return std::accumulate(reference_tokens.begin(), reference_tokens.end(), std::string{}, [](const std::string & a, const std::string & b) { return a + "/" + escape(b); }); } /// @copydoc to_string() operator std::string() const { return to_string(); } /*! @param[in] s reference token to be converted into an array index @return integer representation of @a s @throw out_of_range.404 if string @a s could not be converted to an integer */ static int array_index(const std::string& s) { std::size_t processed_chars = 0; const int res = std::stoi(s, &processed_chars); // check if the string was completely read if (JSON_UNLIKELY(processed_chars != s.size())) { JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } return res; } private: /*! @brief remove and return last reference pointer @throw out_of_range.405 if JSON pointer has no parent */ std::string pop_back() { if (JSON_UNLIKELY(is_root())) { JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } auto last = reference_tokens.back(); reference_tokens.pop_back(); return last; } /// return whether pointer points to the root document bool is_root() const { return reference_tokens.empty(); } json_pointer top() const { if (JSON_UNLIKELY(is_root())) { JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } json_pointer result = *this; result.reference_tokens = {reference_tokens[0]}; return result; } /*! @brief create and return a reference to the pointed to value @complexity Linear in the number of reference tokens. @throw parse_error.109 if array index is not a number @throw type_error.313 if value cannot be unflattened */ BasicJsonType& get_and_create(BasicJsonType& j) const { using size_type = typename BasicJsonType::size_type; auto result = &j; // in case no reference tokens exist, return a reference to the JSON value // j which will be overwritten by a primitive value for (const auto& reference_token : reference_tokens) { switch (result->m_type) { case detail::value_t::null: { if (reference_token == "0") { // start a new array if reference token is 0 result = &result->operator[](0); } else { // start a new object otherwise result = &result->operator[](reference_token); } break; } case detail::value_t::object: { // create an entry in the object result = &result->operator[](reference_token); break; } case detail::value_t::array: { // create an entry in the array JSON_TRY { result = &result->operator[](static_cast(array_index(reference_token))); } JSON_CATCH(std::invalid_argument&) { JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } /* The following code is only reached if there exists a reference token _and_ the current value is primitive. In this case, we have an error situation, because primitive values may only occur as single value; that is, with an empty list of reference tokens. */ default: JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); } } return *result; } /*! @brief return a reference to the pointed to value @note This version does not throw if a value is not present, but tries to create nested values instead. For instance, calling this function with pointer `"/this/that"` on a null value is equivalent to calling `operator[]("this").operator[]("that")` on that value, effectively changing the null value to an object. @param[in] ptr a JSON value @return reference to the JSON value pointed to by the JSON pointer @complexity Linear in the length of the JSON pointer. @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.404 if the JSON pointer can not be resolved */ BasicJsonType& get_unchecked(BasicJsonType* ptr) const { using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { // convert null values to arrays or objects before continuing if (ptr->m_type == detail::value_t::null) { // check if reference token is a number const bool nums = std::all_of(reference_token.begin(), reference_token.end(), [](const char x) { return (x >= '0' and x <= '9'); }); // change value to array for numbers or "-" or to object otherwise *ptr = (nums or reference_token == "-") ? detail::value_t::array : detail::value_t::object; } switch (ptr->m_type) { case detail::value_t::object: { // use unchecked object access ptr = &ptr->operator[](reference_token); break; } case detail::value_t::array: { // error condition (cf. RFC 6901, Sect. 4) if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } if (reference_token == "-") { // explicitly treat "-" as index beyond the end ptr = &ptr->operator[](ptr->m_value.array->size()); } else { // convert array index to number; unchecked access JSON_TRY { ptr = &ptr->operator[]( static_cast(array_index(reference_token))); } JSON_CATCH(std::invalid_argument&) { JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } } break; } default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } return *ptr; } /*! @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ BasicJsonType& get_checked(BasicJsonType* ptr) const { using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->m_type) { case detail::value_t::object: { // note: at performs range check ptr = &ptr->at(reference_token); break; } case detail::value_t::array: { if (JSON_UNLIKELY(reference_token == "-")) { // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check JSON_TRY { ptr = &ptr->at(static_cast(array_index(reference_token))); } JSON_CATCH(std::invalid_argument&) { JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } return *ptr; } /*! @brief return a const reference to the pointed to value @param[in] ptr a JSON value @return const reference to the JSON value pointed to by the JSON pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const { using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->m_type) { case detail::value_t::object: { // use unchecked object access ptr = &ptr->operator[](reference_token); break; } case detail::value_t::array: { if (JSON_UNLIKELY(reference_token == "-")) { // "-" cannot be used for const access JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // use unchecked array access JSON_TRY { ptr = &ptr->operator[]( static_cast(array_index(reference_token))); } JSON_CATCH(std::invalid_argument&) { JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } return *ptr; } /*! @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ const BasicJsonType& get_checked(const BasicJsonType* ptr) const { using size_type = typename BasicJsonType::size_type; for (const auto& reference_token : reference_tokens) { switch (ptr->m_type) { case detail::value_t::object: { // note: at performs range check ptr = &ptr->at(reference_token); break; } case detail::value_t::array: { if (JSON_UNLIKELY(reference_token == "-")) { // "-" always fails the range check JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range")); } // error condition (cf. RFC 6901, Sect. 4) if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) { JSON_THROW(detail::parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'")); } // note: at performs range check JSON_TRY { ptr = &ptr->at(static_cast(array_index(reference_token))); } JSON_CATCH(std::invalid_argument&) { JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); } break; } default: JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); } } return *ptr; } /*! @brief split the string input to reference tokens @note This function is only called by the json_pointer constructor. All exceptions below are documented there. @throw parse_error.107 if the pointer is not empty or begins with '/' @throw parse_error.108 if character '~' is not followed by '0' or '1' */ static std::vector split(const std::string& reference_string) { std::vector result; // special case: empty reference string -> no reference tokens if (reference_string.empty()) { return result; } // check if nonempty reference string begins with slash if (JSON_UNLIKELY(reference_string[0] != '/')) { JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); } // extract the reference tokens: // - slash: position of the last read slash (or end of string) // - start: position after the previous slash for ( // search for the first slash after the first character std::size_t slash = reference_string.find_first_of('/', 1), // set the beginning of the first reference token start = 1; // we can stop if start == string::npos+1 = 0 start != 0; // set the beginning of the next reference token // (will eventually be 0 if slash == std::string::npos) start = slash + 1, // find next slash slash = reference_string.find_first_of('/', start)) { // use the text between the beginning of the reference token // (start) and the last slash (slash). auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped for (std::size_t pos = reference_token.find_first_of('~'); pos != std::string::npos; pos = reference_token.find_first_of('~', pos + 1)) { assert(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 if (JSON_UNLIKELY(pos == reference_token.size() - 1 or (reference_token[pos + 1] != '0' and reference_token[pos + 1] != '1'))) { JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); } } // finally, store the reference token unescape(reference_token); result.push_back(reference_token); } return result; } /*! @brief replace all occurrences of a substring by another string @param[in,out] s the string to manipulate; changed so that all occurrences of @a f are replaced with @a t @param[in] f the substring to replace with @a t @param[in] t the string to replace @a f @pre The search string @a f must not be empty. **This precondition is enforced with an assertion.** @since version 2.0.0 */ static void replace_substring(std::string& s, const std::string& f, const std::string& t) { assert(not f.empty()); for (auto pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and pos = s.find(f, pos + t.size())) // find next occurrence of f {} } /// escape "~"" to "~0" and "/" to "~1" static std::string escape(std::string s) { replace_substring(s, "~", "~0"); replace_substring(s, "/", "~1"); return s; } /// unescape "~1" to tilde and "~0" to slash (order is important!) static void unescape(std::string& s) { replace_substring(s, "~1", "/"); replace_substring(s, "~0", "~"); } /*! @param[in] reference_string the reference string to the current value @param[in] value the value to consider @param[in,out] result the result object to insert values to @note Empty objects or arrays are flattened to `null`. */ static void flatten(const std::string& reference_string, const BasicJsonType& value, BasicJsonType& result) { switch (value.m_type) { case detail::value_t::array: { if (value.m_value.array->empty()) { // flatten empty array as null result[reference_string] = nullptr; } else { // iterate array and use index as reference string for (std::size_t i = 0; i < value.m_value.array->size(); ++i) { flatten(reference_string + "/" + std::to_string(i), value.m_value.array->operator[](i), result); } } break; } case detail::value_t::object: { if (value.m_value.object->empty()) { // flatten empty object as null result[reference_string] = nullptr; } else { // iterate object and use keys as reference string for (const auto& element : *value.m_value.object) { flatten(reference_string + "/" + escape(element.first), element.second, result); } } break; } default: { // add primitive value with its reference string result[reference_string] = value; break; } } } /*! @param[in] value flattened JSON @return unflattened JSON @throw parse_error.109 if array index is not a number @throw type_error.314 if value is not an object @throw type_error.315 if object values are not primitive @throw type_error.313 if value cannot be unflattened */ static BasicJsonType unflatten(const BasicJsonType& value) { if (JSON_UNLIKELY(not value.is_object())) { JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); } BasicJsonType result; // iterate the JSON object values for (const auto& element : *value.m_value.object) { if (JSON_UNLIKELY(not element.second.is_primitive())) { JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); } // assign value to reference pointed to by JSON pointer; Note that if // the JSON pointer is "" (i.e., points to the whole value), function // get_and_create returns a reference to result itself. An assignment // will then create a primitive value. json_pointer(element.first).get_and_create(result) = element.second; } return result; } friend bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept { return (lhs.reference_tokens == rhs.reference_tokens); } friend bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept { return not (lhs == rhs); } /// the reference tokens std::vector reference_tokens; }; } // #include #include // #include // #include namespace nlohmann { template struct adl_serializer { /*! @brief convert a JSON value to any value type This function is usually called by the `get()` function of the @ref basic_json class (either explicit or via conversion operators). @param[in] j JSON value to read from @param[in,out] val value to write to */ template static void from_json(BasicJsonType&& j, ValueType& val) noexcept( noexcept(::nlohmann::from_json(std::forward(j), val))) { ::nlohmann::from_json(std::forward(j), val); } /*! @brief convert any value type to a JSON value This function is usually called by the constructors of the @ref basic_json class. @param[in,out] j JSON value to write to @param[in] val value to read from */ template static void to_json(BasicJsonType& j, ValueType&& val) noexcept( noexcept(::nlohmann::to_json(j, std::forward(val)))) { ::nlohmann::to_json(j, std::forward(val)); } }; } /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @since version 1.0.0 */ namespace nlohmann { /*! @brief a class to store JSON values @tparam ObjectType type for JSON objects (`std::map` by default; will be used in @ref object_t) @tparam ArrayType type for JSON arrays (`std::vector` by default; will be used in @ref array_t) @tparam StringType type for JSON strings and object keys (`std::string` by default; will be used in @ref string_t) @tparam BooleanType type for JSON booleans (`bool` by default; will be used in @ref boolean_t) @tparam NumberIntegerType type for JSON integer numbers (`int64_t` by default; will be used in @ref number_integer_t) @tparam NumberUnsignedType type for JSON unsigned integer numbers (@c `uint64_t` by default; will be used in @ref number_unsigned_t) @tparam NumberFloatType type for JSON floating-point numbers (`double` by default; will be used in @ref number_float_t) @tparam AllocatorType type of the allocator to use (`std::allocator` by default) @tparam JSONSerializer the serializer to resolve internal calls to `to_json()` and `from_json()` (@ref adl_serializer by default) @requirement The class satisfies the following concept requirements: - Basic - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): JSON values can be default constructed. The result will be a JSON null value. - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): A JSON value can be constructed from an rvalue argument. - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): A JSON value can be copy-constructed from an lvalue expression. - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): A JSON value van be assigned from an rvalue argument. - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): A JSON value can be copy-assigned from an lvalue expression. - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): JSON values can be destructed. - Layout - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): JSON values have [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): All non-static data members are private and standard layout types, the class has no virtual functions or (virtual) base classes. - Library-wide - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): JSON values can be compared with `==`, see @ref operator==(const_reference,const_reference). - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): JSON values can be compared with `<`, see @ref operator<(const_reference,const_reference). - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of other compatible types, using unqualified function call @ref swap(). - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): JSON values can be compared against `std::nullptr_t` objects which are used to model the `null` value. - Container - [Container](http://en.cppreference.com/w/cpp/concept/Container): JSON values can be used like STL containers and provide iterator access. - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); JSON values can be used like STL containers and provide reverse iterator access. @invariant The member variables @a m_value and @a m_type have the following relationship: - If `m_type == value_t::object`, then `m_value.object != nullptr`. - If `m_type == value_t::array`, then `m_value.array != nullptr`. - If `m_type == value_t::string`, then `m_value.string != nullptr`. The invariants are checked by member function assert_invariant(). @internal @note ObjectType trick from http://stackoverflow.com/a/9860911 @endinternal @see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://rfc7159.net/rfc7159) @since version 1.0.0 @nosubgrouping */ NLOHMANN_BASIC_JSON_TPL_DECLARATION class basic_json { private: template friend struct detail::external_constructor; friend ::nlohmann::json_pointer; friend ::nlohmann::detail::parser; friend ::nlohmann::detail::serializer; template friend class ::nlohmann::detail::iter_impl; template friend class ::nlohmann::detail::binary_writer; template friend class ::nlohmann::detail::binary_reader; /// workaround type for MSVC using basic_json_t = NLOHMANN_BASIC_JSON_TPL; // convenience aliases for types residing in namespace detail; using lexer = ::nlohmann::detail::lexer; using parser = ::nlohmann::detail::parser; using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; template using internal_iterator = ::nlohmann::detail::internal_iterator; template using iter_impl = ::nlohmann::detail::iter_impl; template using iteration_proxy = ::nlohmann::detail::iteration_proxy; template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; template using output_adapter_t = ::nlohmann::detail::output_adapter_t; using binary_reader = ::nlohmann::detail::binary_reader; template using binary_writer = ::nlohmann::detail::binary_writer; using serializer = ::nlohmann::detail::serializer; public: using value_t = detail::value_t; /// @copydoc nlohmann::json_pointer using json_pointer = ::nlohmann::json_pointer; template using json_serializer = JSONSerializer; /// helper type for initializer lists of basic_json values using initializer_list_t = std::initializer_list>; //////////////// // exceptions // //////////////// /// @name exceptions /// Classes to implement user-defined exceptions. /// @{ /// @copydoc detail::exception using exception = detail::exception; /// @copydoc detail::parse_error using parse_error = detail::parse_error; /// @copydoc detail::invalid_iterator using invalid_iterator = detail::invalid_iterator; /// @copydoc detail::type_error using type_error = detail::type_error; /// @copydoc detail::out_of_range using out_of_range = detail::out_of_range; /// @copydoc detail::other_error using other_error = detail::other_error; /// @} ///////////////////// // container types // ///////////////////// /// @name container types /// The canonic container types to use @ref basic_json like any other STL /// container. /// @{ /// the type of elements in a basic_json container using value_type = basic_json; /// the type of an element reference using reference = value_type&; /// the type of an element const reference using const_reference = const value_type&; /// a type to represent differences between iterators using difference_type = std::ptrdiff_t; /// a type to represent container sizes using size_type = std::size_t; /// the allocator type using allocator_type = AllocatorType; /// the type of an element pointer using pointer = typename std::allocator_traits::pointer; /// the type of an element const pointer using const_pointer = typename std::allocator_traits::const_pointer; /// an iterator for a basic_json container using iterator = iter_impl; /// a const iterator for a basic_json container using const_iterator = iter_impl; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container using const_reverse_iterator = json_reverse_iterator; /// @} /*! @brief returns the allocator associated with the container */ static allocator_type get_allocator() { return allocator_type(); } /*! @brief returns version information on the library This function returns a JSON object with information about the library, including the version number and information on the platform and compiler. @return JSON object holding version information key | description ----------- | --------------- `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). `copyright` | The copyright line for the library as string. `name` | The name of the library as string. `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. `url` | The URL of the project as string. `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). @liveexample{The following code shows an example output of the `meta()` function.,meta} @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @complexity Constant. @since 2.1.0 */ static basic_json meta() { basic_json result; result["copyright"] = "(C) 2013-2017 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; result["version"]["string"] = std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + std::to_string(NLOHMANN_JSON_VERSION_PATCH); result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; #ifdef _WIN32 result["platform"] = "win32"; #elif defined __linux__ result["platform"] = "linux"; #elif defined __APPLE__ result["platform"] = "apple"; #elif defined __unix__ result["platform"] = "unix"; #else result["platform"] = "unknown"; #endif #if defined(__ICC) || defined(__INTEL_COMPILER) result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; #elif defined(__clang__) result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__GNUC__) || defined(__GNUG__) result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; #elif defined(__HP_cc) || defined(__HP_aCC) result["compiler"] = "hp" #elif defined(__IBMCPP__) result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; #elif defined(_MSC_VER) result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; #elif defined(__PGI) result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; #elif defined(__SUNPRO_CC) result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; #else result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; #endif #ifdef __cplusplus result["compiler"]["c++"] = std::to_string(__cplusplus); #else result["compiler"]["c++"] = "unknown"; #endif return result; } /////////////////////////// // JSON value data types // /////////////////////////// /// @name JSON value data types /// The data types to store a JSON value. These types are derived from /// the template arguments passed to class @ref basic_json. /// @{ #if defined(JSON_HAS_CPP_14) // Use transparent comparator if possible, combined with perfect forwarding // on find() and count() calls prevents unnecessary string construction. using object_comparator_t = std::less<>; #else using object_comparator_t = std::less; #endif /*! @brief a type for an object [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: > An object is an unordered collection of zero or more name/value pairs, > where a name is a string and a value is a string, number, boolean, null, > object, or array. To store objects in C++, a type is defined by the template parameters described below. @tparam ObjectType the container to store objects (e.g., `std::map` or `std::unordered_map`) @tparam StringType the type of the keys or names (e.g., `std::string`). The comparison function `std::less` is used to order elements inside the container. @tparam AllocatorType the allocator to use for objects (e.g., `std::allocator`) #### Default type With the default values for @a ObjectType (`std::map`), @a StringType (`std::string`), and @a AllocatorType (`std::allocator`), the default value for @a object_t is: @code {.cpp} std::map< std::string, // key_type basic_json, // value_type std::less, // key_compare std::allocator> // allocator_type > @endcode #### Behavior The choice of @a object_t influences the behavior of the JSON class. With the default type, objects have the following behavior: - When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings. - When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or `{"key": 2}`. - Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see @ref dump) in this order. For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored and serialized as `{"a": 2, "b": 1}`. - When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be treated as equal. #### Limits [RFC 7159](http://rfc7159.net/rfc7159) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the object's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the @ref max_size function of a JSON object. #### Storage Objects are stored as pointers in a @ref basic_json type. That is, for any access to object values, a pointer of type `object_t*` must be dereferenced. @sa @ref array_t -- type for an array value @since version 1.0.0 @note The order name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 7159](http://rfc7159.net/rfc7159), because any order implements the specified "unordered" nature of JSON objects. */ using object_t = ObjectType>>; /*! @brief a type for an array [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: > An array is an ordered sequence of zero or more values. To store objects in C++, a type is defined by the template parameters explained below. @tparam ArrayType container type to store arrays (e.g., `std::vector` or `std::list`) @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) #### Default type With the default values for @a ArrayType (`std::vector`) and @a AllocatorType (`std::allocator`), the default value for @a array_t is: @code {.cpp} std::vector< basic_json, // value_type std::allocator // allocator_type > @endcode #### Limits [RFC 7159](http://rfc7159.net/rfc7159) specifies: > An implementation may set limits on the maximum depth of nesting. In this class, the array's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the @ref max_size function of a JSON array. #### Storage Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of type `array_t*` must be dereferenced. @sa @ref object_t -- type for an object value @since version 1.0.0 */ using array_t = ArrayType>; /*! @brief a type for a string [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: > A string is a sequence of zero or more Unicode characters. To store objects in C++, a type is defined by the template parameter described below. Unicode values are split by the JSON class into byte-sized characters during deserialization. @tparam StringType the container to store strings (e.g., `std::string`). Note this container is used for keys/names in objects, see @ref object_t. #### Default type With the default values for @a StringType (`std::string`), the default value for @a string_t is: @code {.cpp} std::string @endcode #### Encoding Strings are stored in UTF-8 encoding. Therefore, functions like `std::string::size()` or `std::string::length()` return the number of bytes in the string rather than the number of characters or glyphs. #### String comparison [RFC 7159](http://rfc7159.net/rfc7159) states: > Software implementations are typically required to test names of object > members for equality. Implementations that transform the textual > representation into sequences of Unicode code units and then perform the > comparison numerically, code unit by code unit, are interoperable in the > sense that implementations will agree in all cases on equality or > inequality of two strings. For example, implementations that compare > strings with escaped characters unconverted may incorrectly find that > `"a\\b"` and `"a\u005Cb"` are not equal. This implementation is interoperable as it does compare strings code unit by code unit. #### Storage String values are stored as pointers in a @ref basic_json type. That is, for any access to string values, a pointer of type `string_t*` must be dereferenced. @since version 1.0.0 */ using string_t = StringType; /*! @brief a type for a boolean [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`. To store objects in C++, a type is defined by the template parameter @a BooleanType which chooses the type to use. #### Default type With the default values for @a BooleanType (`bool`), the default value for @a boolean_t is: @code {.cpp} bool @endcode #### Storage Boolean values are stored directly inside a @ref basic_json type. @since version 1.0.0 */ using boolean_t = BooleanType; /*! @brief a type for a number (integer) [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an > optional minus sign, which may be followed by a fraction part and/or an > exponent part. Leading zeros are not allowed. (...) Numeric values that > cannot be represented in the grammar below (such as Infinity and NaN) > are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template parameter @a NumberIntegerType which chooses the type to use. #### Default type With the default values for @a NumberIntegerType (`int64_t`), the default value for @a number_integer_t is: @code {.cpp} int64_t @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 7159](http://rfc7159.net/rfc7159) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as @ref number_unsigned_t or @ref number_float_t. [RFC 7159](http://rfc7159.net/rfc7159) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. As this range is a subrange of the exactly supported range [INT64_MIN, INT64_MAX], this class's integer type is interoperable. #### Storage Integer number values are stored directly inside a @ref basic_json type. @sa @ref number_float_t -- type for number values (floating-point) @sa @ref number_unsigned_t -- type for number values (unsigned integer) @since version 1.0.0 */ using number_integer_t = NumberIntegerType; /*! @brief a type for a number (unsigned) [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an > optional minus sign, which may be followed by a fraction part and/or an > exponent part. Leading zeros are not allowed. (...) Numeric values that > cannot be represented in the grammar below (such as Infinity and NaN) > are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store unsigned integer numbers in C++, a type is defined by the template parameter @a NumberUnsignedType which chooses the type to use. #### Default type With the default values for @a NumberUnsignedType (`uint64_t`), the default value for @a number_unsigned_t is: @code {.cpp} uint64_t @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as octal number. Internally, the value will be stored as decimal number. For instance, the C++ integer literal `010` will be serialized to `8`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 7159](http://rfc7159.net/rfc7159) specifies: > An implementation may set limits on the range and precision of numbers. When the default type is used, the maximal integer number that can be stored is `18446744073709551615` (UINT64_MAX) and the minimal integer number that can be stored is `0`. Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be automatically be stored as @ref number_integer_t or @ref number_float_t. [RFC 7159](http://rfc7159.net/rfc7159) further states: > Note that when such software is used, numbers that are integers and are > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense > that implementations will agree exactly on their numeric values. As this range is a subrange (when considered in conjunction with the number_integer_t type) of the exactly supported range [0, UINT64_MAX], this class's integer type is interoperable. #### Storage Integer number values are stored directly inside a @ref basic_json type. @sa @ref number_float_t -- type for number values (floating-point) @sa @ref number_integer_t -- type for number values (integer) @since version 2.0.0 */ using number_unsigned_t = NumberUnsignedType; /*! @brief a type for a number (floating-point) [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: > The representation of numbers is similar to that used in most > programming languages. A number is represented in base 10 using decimal > digits. It contains an integer component that may be prefixed with an > optional minus sign, which may be followed by a fraction part and/or an > exponent part. Leading zeros are not allowed. (...) Numeric values that > cannot be represented in the grammar below (such as Infinity and NaN) > are not permitted. This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer or a floating-point number. Therefore, three different types, @ref number_integer_t, @ref number_unsigned_t and @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template parameter @a NumberFloatType which chooses the type to use. #### Default type With the default values for @a NumberFloatType (`double`), the default value for @a number_float_t is: @code {.cpp} double @endcode #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in floating-point literals will be ignored. Internally, the value will be stored as decimal number. For instance, the C++ floating-point literal `01.2` will be serialized to `1.2`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits [RFC 7159](http://rfc7159.net/rfc7159) states: > This specification allows implementations to set limits on the range and > precision of numbers accepted. Since software that implements IEEE > 754-2008 binary64 (double precision) numbers is generally available and > widely used, good interoperability can be achieved by implementations > that expect no more precision or range than these provide, in the sense > that implementations will approximate JSON numbers within the expected > precision. This implementation does exactly follow this approach, as it uses double precision floating-point numbers. Note values smaller than `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` will be stored as NaN internally and be serialized to `null`. #### Storage Floating-point number values are stored directly inside a @ref basic_json type. @sa @ref number_integer_t -- type for number values (integer) @sa @ref number_unsigned_t -- type for number values (unsigned integer) @since version 1.0.0 */ using number_float_t = NumberFloatType; /// @} private: /// helper for exception-safe object creation template static T* create(Args&& ... args) { AllocatorType alloc; using AllocatorTraits = std::allocator_traits>; auto deleter = [&](T * object) { AllocatorTraits::deallocate(alloc, object, 1); }; std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); assert(object != nullptr); return object.release(); } //////////////////////// // JSON value storage // //////////////////////// /*! @brief a JSON value The actual storage for a JSON value of the @ref basic_json class. This union combines the different storage types for the JSON value types defined in @ref value_t. JSON type | value_t type | used type --------- | --------------- | ------------------------ object | object | pointer to @ref object_t array | array | pointer to @ref array_t string | string | pointer to @ref string_t boolean | boolean | @ref boolean_t number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as pointers. The size of the union should not exceed 64 bits if the default value types are used. @since version 1.0.0 */ union json_value { /// object (stored with pointer to save storage) object_t* object; /// array (stored with pointer to save storage) array_t* array; /// string (stored with pointer to save storage) string_t* string; /// boolean boolean_t boolean; /// number (integer) number_integer_t number_integer; /// number (unsigned integer) number_unsigned_t number_unsigned; /// number (floating-point) number_float_t number_float; /// default constructor (for null values) json_value() = default; /// constructor for booleans json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) noexcept : number_integer(v) {} /// constructor for numbers (unsigned) json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} /// constructor for numbers (floating-point) json_value(number_float_t v) noexcept : number_float(v) {} /// constructor for empty values of a given type json_value(value_t t) { switch (t) { case value_t::object: { object = create(); break; } case value_t::array: { array = create(); break; } case value_t::string: { string = create(""); break; } case value_t::boolean: { boolean = boolean_t(false); break; } case value_t::number_integer: { number_integer = number_integer_t(0); break; } case value_t::number_unsigned: { number_unsigned = number_unsigned_t(0); break; } case value_t::number_float: { number_float = number_float_t(0.0); break; } case value_t::null: { object = nullptr; // silence warning, see #821 break; } default: { object = nullptr; // silence warning, see #821 if (JSON_UNLIKELY(t == value_t::null)) { JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.2")); // LCOV_EXCL_LINE } break; } } } /// constructor for strings json_value(const string_t& value) { string = create(value); } /// constructor for rvalue strings json_value(string_t&& value) { string = create(std::move(value)); } /// constructor for objects json_value(const object_t& value) { object = create(value); } /// constructor for rvalue objects json_value(object_t&& value) { object = create(std::move(value)); } /// constructor for arrays json_value(const array_t& value) { array = create(value); } /// constructor for rvalue arrays json_value(array_t&& value) { array = create(std::move(value)); } void destroy(value_t t) noexcept { switch (t) { case value_t::object: { AllocatorType alloc; std::allocator_traits::destroy(alloc, object); std::allocator_traits::deallocate(alloc, object, 1); break; } case value_t::array: { AllocatorType alloc; std::allocator_traits::destroy(alloc, array); std::allocator_traits::deallocate(alloc, array, 1); break; } case value_t::string: { AllocatorType alloc; std::allocator_traits::destroy(alloc, string); std::allocator_traits::deallocate(alloc, string, 1); break; } default: { break; } } } }; /*! @brief checks the class invariants This function asserts the class invariants. It needs to be called at the end of every constructor to make sure that created objects respect the invariant. Furthermore, it has to be called each time the type of a JSON value is changed, because the invariant expresses a relationship between @a m_type and @a m_value. */ void assert_invariant() const noexcept { assert(m_type != value_t::object or m_value.object != nullptr); assert(m_type != value_t::array or m_value.array != nullptr); assert(m_type != value_t::string or m_value.string != nullptr); } public: ////////////////////////// // JSON parser callback // ////////////////////////// /*! @brief parser event types The parser callback distinguishes the following events: - `object_start`: the parser read `{` and started to process a JSON object - `key`: the parser read a key of a value in an object - `object_end`: the parser read `}` and finished processing a JSON object - `array_start`: the parser read `[` and started to process a JSON array - `array_end`: the parser read `]` and finished processing a JSON array - `value`: the parser finished reading a JSON value @image html callback_events.png "Example when certain parse events are triggered" @sa @ref parser_callback_t for more information and examples */ using parse_event_t = typename parser::parse_event_t; /*! @brief per-element parser callback type With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse, it is called on certain events (passed as @ref parse_event_t via parameter @a event) with a set recursion depth @a depth and context JSON value @a parsed. The return value of the callback function is a boolean indicating whether the element that emitted the callback shall be kept or not. We distinguish six scenarios (determined by the event type) in which the callback function can be called. The following table describes the values of the parameters @a depth, @a event, and @a parsed. parameter @a event | description | parameter @a depth | parameter @a parsed ------------------ | ----------- | ------------------ | ------------------- parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value @image html callback_events.png "Example when certain parse events are triggered" Discarding a value (i.e., returning `false`) has different effects depending on the context in which function was called: - Discarded values in structured types are skipped. That is, the parser will behave as if the discarded value was never read. - In case a value outside a structured type is skipped, it is replaced with `null`. This case happens if the top-level element is skipped. @param[in] depth the depth of the recursion during parsing @param[in] event an event of type parse_event_t indicating the context in the callback function has been called @param[in,out] parsed the current intermediate parse result; note that writing to this value has no effect for parse_event_t::key events @return Whether the JSON value which called the function during parsing should be kept (`true`) or not (`false`). In the latter case, it is either skipped completely or replaced by an empty discarded object. @sa @ref parse for examples @since version 1.0.0 */ using parser_callback_t = typename parser::parser_callback_t; ////////////////// // constructors // ////////////////// /// @name constructors and destructors /// Constructors of class @ref basic_json, copy/move constructor, copy /// assignment, static functions creating objects, and the destructor. /// @{ /*! @brief create an empty value with a given type Create an empty JSON value with a given type. The value will be default initialized with an empty value which depends on the type: Value type | initial value ----------- | ------------- null | `null` boolean | `false` string | `""` number | `0` object | `{}` array | `[]` @param[in] v the type of the value to create @complexity Constant. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows the constructor for different @ref value_t values,basic_json__value_t} @sa @ref clear() -- restores the postcondition of this constructor @since version 1.0.0 */ basic_json(const value_t v) : m_type(v), m_value(v) { assert_invariant(); } /*! @brief create a null object Create a `null` JSON value. It either takes a null pointer as parameter (explicitly creating `null`) or no parameter (implicitly creating `null`). The passed null pointer itself is not read -- it is only used to choose the right constructor. @complexity Constant. @exceptionsafety No-throw guarantee: this constructor never throws exceptions. @liveexample{The following code shows the constructor with and without a null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); } /*! @brief create a JSON value This is a "catch all" constructor for all compatible JSON types; that is, types for which a `to_json()` method exists. The constructor forwards the parameter @a val to that method (to `json_serializer::to_json` method with `U = uncvref_t`, to be exact). Template type @a CompatibleType includes, but is not limited to, the following types: - **arrays**: @ref array_t and all kinds of compatible containers such as `std::vector`, `std::deque`, `std::list`, `std::forward_list`, `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, `std::multiset`, and `std::unordered_multiset` with a `value_type` from which a @ref basic_json value can be constructed. - **objects**: @ref object_t and all kinds of compatible associative containers such as `std::map`, `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with a `key_type` compatible to @ref string_t and a `value_type` from which a @ref basic_json value can be constructed. - **strings**: @ref string_t, string literals, and all compatible string containers can be used. - **numbers**: @ref number_integer_t, @ref number_unsigned_t, @ref number_float_t, and all convertible number types such as `int`, `size_t`, `int64_t`, `float` or `double` can be used. - **boolean**: @ref boolean_t / `bool` can be used. See the examples below. @tparam CompatibleType a type such that: - @a CompatibleType is not derived from `std::istream`, - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move constructors), - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) - @a CompatibleType is not a @ref basic_json nested type (e.g., @ref json_pointer, @ref iterator, etc ...) - @ref @ref json_serializer has a `to_json(basic_json_t&, CompatibleType&&)` method @tparam U = `uncvref_t` @param[in] val the value to be forwarded to the respective constructor @complexity Usually linear in the size of the passed @a val, also depending on the implementation of the called `to_json()` method. @exceptionsafety Depends on the called constructor. For types directly supported by the library (i.e., all types for which no `to_json()` function was provided), strong guarantee holds: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows the constructor with several compatible types.,basic_json__CompatibleType} @since version 2.1.0 */ template , detail::enable_if_t< detail::is_compatible_type::value, int> = 0> basic_json(CompatibleType && val) noexcept(noexcept( JSONSerializer::to_json(std::declval(), std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); assert_invariant(); } /*! @brief create a JSON value from an existing one This is a constructor for existing @ref basic_json types. It does not hijack copy/move constructors, since the parameter has different template arguments than the current ones. The constructor tries to convert the internal @ref m_value of the parameter. @tparam BasicJsonType a type such that: - @a BasicJsonType is a @ref basic_json type. - @a BasicJsonType has different template arguments than @ref basic_json_t. @param[in] val the @ref basic_json value to be converted. @complexity Usually linear in the size of the passed @a val, also depending on the implementation of the called `to_json()` method. @exceptionsafety Depends on the called constructor. For types directly supported by the library (i.e., all types for which no `to_json()` function was provided), strong guarantee holds: if an exception is thrown, there are no changes to any JSON value. @since version 3.1.2 */ template ::value and not std::is_same::value, int> = 0> basic_json(const BasicJsonType& val) { using other_boolean_t = typename BasicJsonType::boolean_t; using other_number_float_t = typename BasicJsonType::number_float_t; using other_number_integer_t = typename BasicJsonType::number_integer_t; using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; switch (val.type()) { case value_t::boolean: JSONSerializer::to_json(*this, val.template get()); break; case value_t::number_float: JSONSerializer::to_json(*this, val.template get()); break; case value_t::number_integer: JSONSerializer::to_json(*this, val.template get()); break; case value_t::number_unsigned: JSONSerializer::to_json(*this, val.template get()); break; case value_t::string: JSONSerializer::to_json(*this, val.template get_ref()); break; case value_t::object: JSONSerializer::to_json(*this, val.template get_ref()); break; case value_t::array: JSONSerializer::to_json(*this, val.template get_ref()); break; case value_t::null: *this = nullptr; break; case value_t::discarded: m_type = value_t::discarded; break; } assert_invariant(); } /*! @brief create a container (array or object) from an initializer list Creates a JSON value of type array or object from the passed initializer list @a init. In case @a type_deduction is `true` (default), the type of the JSON value to be created is deducted from the initializer list @a init according to the following rules: 1. If the list is empty, an empty JSON object value `{}` is created. 2. If the list consists of pairs whose first element is a string, a JSON object value is created where the first elements of the pairs are treated as keys and the second elements are as values. 3. In all other cases, an array is created. The rules aim to create the best fit between a C++ initializer list and JSON values. The rationale is as follows: 1. The empty initializer list is written as `{}` which is exactly an empty JSON object. 2. C++ has no way of describing mapped types other than to list a list of pairs. As JSON requires that keys must be of type string, rule 2 is the weakest constraint one can pose on initializer lists to interpret them as an object. 3. In all other cases, the initializer list could not be interpreted as JSON object type, so interpreting it as JSON array type is safe. With the rules described above, the following JSON values cannot be expressed by an initializer list: - the empty array (`[]`): use @ref array(initializer_list_t) with an empty initializer list in this case - arrays whose elements satisfy rule 2: use @ref array(initializer_list_t) with the same initializer list in this case @note When used without parentheses around an empty initializer list, @ref basic_json() is called instead of this function, yielding the JSON null value. @param[in] init initializer list with JSON values @param[in] type_deduction internal parameter; when set to `true`, the type of the JSON value is deducted from the initializer list @a init; when set to `false`, the type provided via @a manual_type is forced. This mode is used by the functions @ref array(initializer_list_t) and @ref object(initializer_list_t). @param[in] manual_type internal parameter; when @a type_deduction is set to `false`, the created JSON value will use the provided type (only @ref value_t::array and @ref value_t::object are valid); when @a type_deduction is set to `true`, this parameter has no effect @throw type_error.301 if @a type_deduction is `false`, @a manual_type is `value_t::object`, but @a init contains an element which is not a pair whose first element is a string. In this case, the constructor could not create an object. If @a type_deduction would have be `true`, an array would have been created. See @ref object(initializer_list_t) for an example. @complexity Linear in the size of the initializer list @a init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The example below shows how JSON values are created from initializer lists.,basic_json__list_init_t} @sa @ref array(initializer_list_t) -- create a JSON array value from an initializer list @sa @ref object(initializer_list_t) -- create a JSON object value from an initializer list @since version 1.0.0 */ basic_json(initializer_list_t init, bool type_deduction = true, value_t manual_type = value_t::array) { // check if each element is an array with two elements whose first // element is a string bool is_an_object = std::all_of(init.begin(), init.end(), [](const detail::json_ref& element_ref) { return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string()); }); // adjust type if type deduction is not wanted if (not type_deduction) { // if array is wanted, do not create an object though possible if (manual_type == value_t::array) { is_an_object = false; } // if object is wanted but impossible, throw an exception if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object)) { JSON_THROW(type_error::create(301, "cannot create object from initializer list")); } } if (is_an_object) { // the initializer list is a list of pairs -> create object m_type = value_t::object; m_value = value_t::object; std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); m_value.object->emplace( std::move(*((*element.m_value.array)[0].m_value.string)), std::move((*element.m_value.array)[1])); }); } else { // the initializer list describes an array -> create array m_type = value_t::array; m_value.array = create(init.begin(), init.end()); } assert_invariant(); } /*! @brief explicitly create an array from an initializer list Creates a JSON array value from a given initializer list. That is, given a list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the initializer list is empty, the empty array `[]` is created. @note This function is only needed to express two edge cases that cannot be realized with the initializer list constructor (@ref basic_json(initializer_list_t, bool, value_t)). These cases are: 1. creating an array whose elements are all pairs whose first element is a string -- in this case, the initializer list constructor would create an object, taking the first elements as keys 2. creating an empty array -- passing the empty initializer list to the initializer list constructor yields an empty object @param[in] init initializer list with JSON values to create an array from (optional) @return JSON array value @complexity Linear in the size of @a init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows an example for the `array` function.,array} @sa @ref basic_json(initializer_list_t, bool, value_t) -- create a JSON value from an initializer list @sa @ref object(initializer_list_t) -- create a JSON object value from an initializer list @since version 1.0.0 */ static basic_json array(initializer_list_t init = {}) { return basic_json(init, false, value_t::array); } /*! @brief explicitly create an object from an initializer list Creates a JSON object value from a given initializer list. The initializer lists elements must be pairs, and their first elements must be strings. If the initializer list is empty, the empty object `{}` is created. @note This function is only added for symmetry reasons. In contrast to the related function @ref array(initializer_list_t), there are no cases which can only be expressed by this function. That is, any initializer list @a init can also be passed to the initializer list constructor @ref basic_json(initializer_list_t, bool, value_t). @param[in] init initializer list to create an object from (optional) @return JSON object value @throw type_error.301 if @a init is not a list of pairs whose first elements are strings. In this case, no object can be created. When such a value is passed to @ref basic_json(initializer_list_t, bool, value_t), an array would have been created from the passed initializer list @a init. See example below. @complexity Linear in the size of @a init. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows an example for the `object` function.,object} @sa @ref basic_json(initializer_list_t, bool, value_t) -- create a JSON value from an initializer list @sa @ref array(initializer_list_t) -- create a JSON array value from an initializer list @since version 1.0.0 */ static basic_json object(initializer_list_t init = {}) { return basic_json(init, false, value_t::object); } /*! @brief construct an array with count copies of given value Constructs a JSON array value by creating @a cnt copies of a passed value. In case @a cnt is `0`, an empty array is created. @param[in] cnt the number of JSON copies of @a val to create @param[in] val the JSON value to copy @post `std::distance(begin(),end()) == cnt` holds. @complexity Linear in @a cnt. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The following code shows examples for the @ref basic_json(size_type\, const basic_json&) constructor.,basic_json__size_type_basic_json} @since version 1.0.0 */ basic_json(size_type cnt, const basic_json& val) : m_type(value_t::array) { m_value.array = create(cnt, val); assert_invariant(); } /*! @brief construct a JSON container given an iterator range Constructs the JSON value with the contents of the range `[first, last)`. The semantics depends on the different types a JSON value can have: - In case of a null type, invalid_iterator.206 is thrown. - In case of other primitive types (number, boolean, or string), @a first must be `begin()` and @a last must be `end()`. In this case, the value is copied. Otherwise, invalid_iterator.204 is thrown. - In case of structured types (array, object), the constructor behaves as similar versions for `std::vector` or `std::map`; that is, a JSON array or object is constructed from the values in the range. @tparam InputIT an input iterator type (@ref iterator or @ref const_iterator) @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) @pre Iterators @a first and @a last must be initialized. **This precondition is enforced with an assertion (see warning).** If assertions are switched off, a violation of this precondition yields undefined behavior. @pre Range `[first, last)` is valid. Usually, this precondition cannot be checked efficiently. Only certain edge cases are detected; see the description of the exceptions below. A violation of this precondition yields undefined behavior. @warning A precondition is enforced with a runtime assertion that will result in calling `std::abort` if this precondition is not met. Assertions can be disabled by defining `NDEBUG` at compile time. See http://en.cppreference.com/w/cpp/error/assert for more information. @throw invalid_iterator.201 if iterators @a first and @a last are not compatible (i.e., do not belong to the same JSON value). In this case, the range `[first, last)` is undefined. @throw invalid_iterator.204 if iterators @a first and @a last belong to a primitive type (number, boolean, or string), but @a first does not point to the first element any more. In this case, the range `[first, last)` is undefined. See example code below. @throw invalid_iterator.206 if iterators @a first and @a last belong to a null value. In this case, the range `[first, last)` is undefined. @complexity Linear in distance between @a first and @a last. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @liveexample{The example below shows several ways to create JSON values by specifying a subrange with iterators.,basic_json__InputIt_InputIt} @since version 1.0.0 */ template::value or std::is_same::value, int>::type = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); assert(last.m_object != nullptr); // make sure iterator fits the current value if (JSON_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); } // copy type from first iterator m_type = first.m_object->m_type; // check if iterator range is complete for primitive values switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::number_unsigned: case value_t::string: { if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } break; } default: break; } switch (m_type) { case value_t::number_integer: { m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_unsigned: { m_value.number_unsigned = first.m_object->m_value.number_unsigned; break; } case value_t::number_float: { m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { m_value = *first.m_object->m_value.string; break; } case value_t::object: { m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); break; } default: JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()))); } assert_invariant(); } /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// /// @private basic_json(const detail::json_ref& ref) : basic_json(ref.moved_or_copied()) {} /*! @brief copy constructor Creates a copy of a given JSON value. @param[in] other the JSON value to copy @post `*this == other` @complexity Linear in the size of @a other. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes to any JSON value. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. @liveexample{The following code shows an example for the copy constructor.,basic_json__basic_json} @since version 1.0.0 */ basic_json(const basic_json& other) : m_type(other.m_type) { // check of passed value is valid other.assert_invariant(); switch (m_type) { case value_t::object: { m_value = *other.m_value.object; break; } case value_t::array: { m_value = *other.m_value.array; break; } case value_t::string: { m_value = *other.m_value.string; break; } case value_t::boolean: { m_value = other.m_value.boolean; break; } case value_t::number_integer: { m_value = other.m_value.number_integer; break; } case value_t::number_unsigned: { m_value = other.m_value.number_unsigned; break; } case value_t::number_float: { m_value = other.m_value.number_float; break; } default: break; } assert_invariant(); } /*! @brief move constructor Move constructor. Constructs a JSON value with the contents of the given value @a other using move semantics. It "steals" the resources from @a other and leaves it as JSON null value. @param[in,out] other value to move to this object @post `*this` has the same value as @a other before the call. @post @a other is a JSON null value. @complexity Constant. @exceptionsafety No-throw guarantee: this constructor never throws exceptions. @requirement This function helps `basic_json` satisfying the [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible) requirements. @liveexample{The code below shows the move constructor explicitly called via std::move.,basic_json__moveconstructor} @since version 1.0.0 */ basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { // check that passed value is valid other.assert_invariant(); // invalidate payload other.m_type = value_t::null; other.m_value = {}; assert_invariant(); } /*! @brief copy assignment Copy assignment operator. Copies a JSON value via the "copy and swap" strategy: It is expressed in terms of the copy constructor, destructor, and the `swap()` member function. @param[in] other value to copy from @complexity Linear. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is linear. @liveexample{The code below shows and example for the copy assignment. It creates a copy of value `a` which is then swapped with `b`. Finally\, the copy of `a` (which is the null value after the swap) is destroyed.,basic_json__copyassignment} @since version 1.0.0 */ reference& operator=(basic_json other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value ) { // check that passed value is valid other.assert_invariant(); using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); assert_invariant(); return *this; } /*! @brief destructor Destroys the JSON value and frees all allocated memory. @complexity Linear. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is linear. - All stored elements are destroyed and all memory is freed. @since version 1.0.0 */ ~basic_json() noexcept { assert_invariant(); m_value.destroy(m_type); } /// @} public: /////////////////////// // object inspection // /////////////////////// /// @name object inspection /// Functions to inspect the type of a JSON value. /// @{ /*! @brief serialization Serialization function for JSON values. The function tries to mimic Python's `json.dumps()` function, and currently supports its @a indent and @a ensure_ascii parameters. @param[in] indent If indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of `0` will only insert newlines. `-1` (the default) selects the most compact representation. @param[in] indent_char The character to use for indentation if @a indent is greater than `0`. The default is ` ` (space). @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters in the output are escaped with `\uXXXX` sequences, and the result consists of ASCII characters only. @return string containing the serialization of the JSON value @throw type_error.316 if a string stored inside the JSON value is not UTF-8 encoded @complexity Linear. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @liveexample{The following example shows the effect of different @a indent\, @a indent_char\, and @a ensure_ascii parameters to the result of the serialization.,dump} @see https://docs.python.org/2/library/json.html#json.dump @since version 1.0.0; indentation character @a indent_char, option @a ensure_ascii and exceptions added in version 3.0.0 */ string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false) const { string_t result; serializer s(detail::output_adapter(result), indent_char); if (indent >= 0) { s.dump(*this, true, ensure_ascii, static_cast(indent)); } else { s.dump(*this, false, ensure_ascii, 0); } return result; } /*! @brief return the type of the JSON value (explicit) Return the type of the JSON value as a value from the @ref value_t enumeration. @return the type of the JSON value Value type | return value ------------------------- | ------------------------- null | value_t::null boolean | value_t::boolean string | value_t::string number (integer) | value_t::number_integer number (unsigned integer) | value_t::number_unsigned number (floating-point) | value_t::number_float object | value_t::object array | value_t::array discarded | value_t::discarded @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `type()` for all JSON types.,type} @sa @ref operator value_t() -- return the type of the JSON value (implicit) @sa @ref type_name() -- return the type as string @since version 1.0.0 */ constexpr value_t type() const noexcept { return m_type; } /*! @brief return whether type is primitive This function returns true if and only if the JSON type is primitive (string, number, boolean, or null). @return `true` if type is primitive (string, number, boolean, or null), `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_primitive()` for all JSON types.,is_primitive} @sa @ref is_structured() -- returns whether JSON value is structured @sa @ref is_null() -- returns whether JSON value is `null` @sa @ref is_string() -- returns whether JSON value is a string @sa @ref is_boolean() -- returns whether JSON value is a boolean @sa @ref is_number() -- returns whether JSON value is a number @since version 1.0.0 */ constexpr bool is_primitive() const noexcept { return is_null() or is_string() or is_boolean() or is_number(); } /*! @brief return whether type is structured This function returns true if and only if the JSON type is structured (array or object). @return `true` if type is structured (array or object), `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_structured()` for all JSON types.,is_structured} @sa @ref is_primitive() -- returns whether value is primitive @sa @ref is_array() -- returns whether value is an array @sa @ref is_object() -- returns whether value is an object @since version 1.0.0 */ constexpr bool is_structured() const noexcept { return is_array() or is_object(); } /*! @brief return whether value is null This function returns true if and only if the JSON value is null. @return `true` if type is null, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_null()` for all JSON types.,is_null} @since version 1.0.0 */ constexpr bool is_null() const noexcept { return (m_type == value_t::null); } /*! @brief return whether value is a boolean This function returns true if and only if the JSON value is a boolean. @return `true` if type is boolean, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_boolean()` for all JSON types.,is_boolean} @since version 1.0.0 */ constexpr bool is_boolean() const noexcept { return (m_type == value_t::boolean); } /*! @brief return whether value is a number This function returns true if and only if the JSON value is a number. This includes both integer (signed and unsigned) and floating-point values. @return `true` if type is number (regardless whether integer, unsigned integer or floating-type), `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number()` for all JSON types.,is_number} @sa @ref is_number_integer() -- check if value is an integer or unsigned integer number @sa @ref is_number_unsigned() -- check if value is an unsigned integer number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ constexpr bool is_number() const noexcept { return is_number_integer() or is_number_float(); } /*! @brief return whether value is an integer number This function returns true if and only if the JSON value is a signed or unsigned integer number. This excludes floating-point values. @return `true` if type is an integer or unsigned integer number, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number_integer()` for all JSON types.,is_number_integer} @sa @ref is_number() -- check if value is a number @sa @ref is_number_unsigned() -- check if value is an unsigned integer number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ constexpr bool is_number_integer() const noexcept { return (m_type == value_t::number_integer or m_type == value_t::number_unsigned); } /*! @brief return whether value is an unsigned integer number This function returns true if and only if the JSON value is an unsigned integer number. This excludes floating-point and signed integer values. @return `true` if type is an unsigned integer number, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number_unsigned()` for all JSON types.,is_number_unsigned} @sa @ref is_number() -- check if value is a number @sa @ref is_number_integer() -- check if value is an integer or unsigned integer number @sa @ref is_number_float() -- check if value is a floating-point number @since version 2.0.0 */ constexpr bool is_number_unsigned() const noexcept { return (m_type == value_t::number_unsigned); } /*! @brief return whether value is a floating-point number This function returns true if and only if the JSON value is a floating-point number. This excludes signed and unsigned integer values. @return `true` if type is a floating-point number, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_number_float()` for all JSON types.,is_number_float} @sa @ref is_number() -- check if value is number @sa @ref is_number_integer() -- check if value is an integer number @sa @ref is_number_unsigned() -- check if value is an unsigned integer number @since version 1.0.0 */ constexpr bool is_number_float() const noexcept { return (m_type == value_t::number_float); } /*! @brief return whether value is an object This function returns true if and only if the JSON value is an object. @return `true` if type is object, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_object()` for all JSON types.,is_object} @since version 1.0.0 */ constexpr bool is_object() const noexcept { return (m_type == value_t::object); } /*! @brief return whether value is an array This function returns true if and only if the JSON value is an array. @return `true` if type is array, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_array()` for all JSON types.,is_array} @since version 1.0.0 */ constexpr bool is_array() const noexcept { return (m_type == value_t::array); } /*! @brief return whether value is a string This function returns true if and only if the JSON value is a string. @return `true` if type is string, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_string()` for all JSON types.,is_string} @since version 1.0.0 */ constexpr bool is_string() const noexcept { return (m_type == value_t::string); } /*! @brief return whether value is discarded This function returns true if and only if the JSON value was discarded during parsing with a callback function (see @ref parser_callback_t). @note This function will always be `false` for JSON values after parsing. That is, discarded values can only occur during parsing, but will be removed when inside a structured value or replaced by null in other cases. @return `true` if type is discarded, `false` otherwise. @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies `is_discarded()` for all JSON types.,is_discarded} @since version 1.0.0 */ constexpr bool is_discarded() const noexcept { return (m_type == value_t::discarded); } /*! @brief return the type of the JSON value (implicit) Implicitly return the type of the JSON value as a value from the @ref value_t enumeration. @return the type of the JSON value @complexity Constant. @exceptionsafety No-throw guarantee: this member function never throws exceptions. @liveexample{The following code exemplifies the @ref value_t operator for all JSON types.,operator__value_t} @sa @ref type() -- return the type of the JSON value (explicit) @sa @ref type_name() -- return the type as string @since version 1.0.0 */ constexpr operator value_t() const noexcept { return m_type; } /// @} private: ////////////////// // value access // ////////////////// /// get a boolean (explicit) boolean_t get_impl(boolean_t* /*unused*/) const { if (JSON_LIKELY(is_boolean())) { return m_value.boolean; } JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); } /// get a pointer to the value (object) object_t* get_impl_ptr(object_t* /*unused*/) noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) array_t* get_impl_ptr(array_t* /*unused*/) noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) string_t* get_impl_ptr(string_t* /*unused*/) noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (unsigned number) constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (floating-point number) number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /*! @brief helper function to implement get_ref() This function helps to implement get_ref() without code duplication for const and non-const overloads @tparam ThisType will be deduced as `basic_json` or `const basic_json` @throw type_error.303 if ReferenceType does not match underlying value type of the current JSON */ template static ReferenceType get_ref_impl(ThisType& obj) { // delegate the call to get_ptr<>() auto ptr = obj.template get_ptr::type>(); if (JSON_LIKELY(ptr != nullptr)) { return *ptr; } JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); } public: /// @name value access /// Direct access to the stored value of a JSON value. /// @{ /*! @brief get special-case overload This overloads avoids a lot of template boilerplate, it can be seen as the identity method @tparam BasicJsonType == @ref basic_json @return a copy of *this @complexity Constant. @since version 2.1.0 */ template::type, basic_json_t>::value, int> = 0> basic_json get() const { return *this; } /*! @brief get special-case overload This overloads converts the current @ref basic_json in a different @ref basic_json type @tparam BasicJsonType == @ref basic_json @return a copy of *this, converted into @tparam BasicJsonType @complexity Depending on the implementation of the called `from_json()` method. @since version 3.1.2 */ template::value and detail::is_basic_json::value, int> = 0> BasicJsonType get() const { return *this; } /*! @brief get a value (explicit) Explicit type conversion between the JSON value and a compatible value which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). The value is converted by calling the @ref json_serializer `from_json()` method. The function is equivalent to executing @code {.cpp} ValueType ret; JSONSerializer::from_json(*this, ret); return ret; @endcode This overloads is chosen if: - @a ValueType is not @ref basic_json, - @ref json_serializer has a `from_json()` method of the form `void from_json(const basic_json&, ValueType&)`, and - @ref json_serializer does not have a `from_json()` method of the form `ValueType from_json(const basic_json&)` @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @throw what @ref json_serializer `from_json()` method throws @liveexample{The example below shows several conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can be converted to integers\, (2) A JSON array can be converted to a standard `std::vector`\, (3) A JSON object can be converted to C++ associative containers such as `std::unordered_map`.,get__ValueType_const} @since version 2.1.0 */ template, detail::enable_if_t < not detail::is_basic_json::value and detail::has_from_json::value and not detail::has_non_default_from_json::value, int> = 0> ValueType get() const noexcept(noexcept( JSONSerializer::from_json(std::declval(), std::declval()))) { // we cannot static_assert on ValueTypeCV being non-const, because // there is support for get(), which is why we // still need the uncvref static_assert(not std::is_reference::value, "get() cannot be used with reference types, you might want to use get_ref()"); static_assert(std::is_default_constructible::value, "types must be DefaultConstructible when used with get()"); ValueType ret; JSONSerializer::from_json(*this, ret); return ret; } /*! @brief get a value (explicit); special case Explicit type conversion between the JSON value and a compatible value which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). The value is converted by calling the @ref json_serializer `from_json()` method. The function is equivalent to executing @code {.cpp} return JSONSerializer::from_json(*this); @endcode This overloads is chosen if: - @a ValueType is not @ref basic_json and - @ref json_serializer has a `from_json()` method of the form `ValueType from_json(const basic_json&)` @note If @ref json_serializer has both overloads of `from_json()`, this one is chosen. @tparam ValueTypeCV the provided value type @tparam ValueType the returned value type @return copy of the JSON value, converted to @a ValueType @throw what @ref json_serializer `from_json()` method throws @since version 2.1.0 */ template, detail::enable_if_t::value and detail::has_non_default_from_json::value, int> = 0> ValueType get() const noexcept(noexcept( JSONSerializer::from_json(std::declval()))) { static_assert(not std::is_reference::value, "get() cannot be used with reference types, you might want to use get_ref()"); return JSONSerializer::from_json(*this); } /*! @brief get a pointer value (explicit) Explicit pointer access to the internally stored JSON value. No copies are made. @warning The pointer becomes invalid if the underlying JSON object changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. @liveexample{The example below shows how pointers to internal values of a JSON value can be requested. Note that no type conversions are made and a `nullptr` is returned if the value and the requested pointer type does not match.,get__PointerType} @sa @ref get_ptr() for explicit pointer-member access @since version 1.0.0 */ template::value, int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr return get_ptr(); } /*! @brief get a pointer value (explicit) @copydoc get() */ template::value, int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr return get_ptr(); } /*! @brief get a pointer value (implicit) Implicit pointer access to the internally stored JSON value. No copies are made. @warning Writing data to the pointee of the result yields an undefined state. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @ref number_unsigned_t, or @ref number_float_t. Enforced by a static assertion. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @complexity Constant. @liveexample{The example below shows how pointers to internal values of a JSON value can be requested. Note that no type conversions are made and a `nullptr` is returned if the value and the requested pointer type does not match.,get_ptr} @since version 1.0.0 */ template::value, int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value , "incompatible pointer type"); // delegate the call to get_impl_ptr<>() return get_impl_ptr(static_cast(nullptr)); } /*! @brief get a pointer value (implicit) @copydoc get_ptr() */ template::value and std::is_const::type>::value, int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) using pointee_t = typename std::remove_const::type>::type>::type; // make sure the type matches the allowed types static_assert( std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value or std::is_same::value , "incompatible pointer type"); // delegate the call to get_impl_ptr<>() const return get_impl_ptr(static_cast(nullptr)); } /*! @brief get a reference value (implicit) Implicit reference access to the internally stored JSON value. No copies are made. @warning Writing data to the referee of the result yields an undefined state. @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref number_float_t. Enforced by static assertion. @return reference to the internally stored JSON value if the requested reference type @a ReferenceType fits to the JSON value; throws type_error.303 otherwise @throw type_error.303 in case passed type @a ReferenceType is incompatible with the stored JSON value; see example below @complexity Constant. @liveexample{The example shows several calls to `get_ref()`.,get_ref} @since version 1.1.0 */ template::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl return get_ref_impl(*this); } /*! @brief get a reference value (implicit) @copydoc get_ref() */ template::value and std::is_const::type>::value, int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl return get_ref_impl(*this); } /*! @brief get a value (implicit) Implicit type conversion between the JSON value and a compatible value. The call is realized by calling @ref get() const. @tparam ValueType non-pointer type compatible to the JSON value, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. The character type of @ref string_t as well as an initializer list of this type is excluded to avoid ambiguities as these types implicitly convert to `std::string`. @return copy of the JSON value, converted to type @a ValueType @throw type_error.302 in case passed type @a ValueType is incompatible to the JSON value type (e.g., the JSON value is of type boolean, but a string is requested); see example below @complexity Linear in the size of the JSON value. @liveexample{The example below shows several conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can be converted to integers\, (2) A JSON array can be converted to a standard `std::vector`\, (3) A JSON object can be converted to C++ associative containers such as `std::unordered_map`.,operator__ValueType} @since version 1.0.0 */ template < typename ValueType, typename std::enable_if < not std::is_pointer::value and not std::is_same>::value and not std::is_same::value and not detail::is_basic_json::value #ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 and not std::is_same>::value #endif #if defined(JSON_HAS_CPP_17) and not std::is_same::value #endif , int >::type = 0 > operator ValueType() const { // delegate the call to get<>() const return get(); } /// @} //////////////////// // element access // //////////////////// /// @name element access /// Access to the JSON value. /// @{ /*! @brief access specified array element with bounds checking Returns a reference to the element at specified location @a idx, with bounds checking. @param[in] idx index of the element to access @return reference to the element at index @a idx @throw type_error.304 if the JSON value is not an array; in this case, calling `at` with an index makes no sense. See example below. @throw out_of_range.401 if the index @a idx is out of range of the array; that is, `idx >= size()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 1.0.0 @liveexample{The example below shows how array elements can be read and written using `at()`. It also demonstrates the different exceptions that can be thrown.,at__size_type} */ reference at(size_type idx) { // at only works for arrays if (JSON_LIKELY(is_array())) { JSON_TRY { return m_value.array->at(idx); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } /*! @brief access specified array element with bounds checking Returns a const reference to the element at specified location @a idx, with bounds checking. @param[in] idx index of the element to access @return const reference to the element at index @a idx @throw type_error.304 if the JSON value is not an array; in this case, calling `at` with an index makes no sense. See example below. @throw out_of_range.401 if the index @a idx is out of range of the array; that is, `idx >= size()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 1.0.0 @liveexample{The example below shows how array elements can be read using `at()`. It also demonstrates the different exceptions that can be thrown., at__size_type_const} */ const_reference at(size_type idx) const { // at only works for arrays if (JSON_LIKELY(is_array())) { JSON_TRY { return m_value.array->at(idx); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } /*! @brief access specified object element with bounds checking Returns a reference to the element at with specified key @a key, with bounds checking. @param[in] key key of the element to access @return reference to the element at key @a key @throw type_error.304 if the JSON value is not an object; in this case, calling `at` with a key makes no sense. See example below. @throw out_of_range.403 if the key @a key is is not stored in the object; that is, `find(key) == end()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Logarithmic in the size of the container. @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 @liveexample{The example below shows how object elements can be read and written using `at()`. It also demonstrates the different exceptions that can be thrown.,at__object_t_key_type} */ reference at(const typename object_t::key_type& key) { // at only works for objects if (JSON_LIKELY(is_object())) { JSON_TRY { return m_value.object->at(key); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } /*! @brief access specified object element with bounds checking Returns a const reference to the element at with specified key @a key, with bounds checking. @param[in] key key of the element to access @return const reference to the element at key @a key @throw type_error.304 if the JSON value is not an object; in this case, calling `at` with a key makes no sense. See example below. @throw out_of_range.403 if the key @a key is is not stored in the object; that is, `find(key) == end()`. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Logarithmic in the size of the container. @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 @liveexample{The example below shows how object elements can be read using `at()`. It also demonstrates the different exceptions that can be thrown., at__object_t_key_type_const} */ const_reference at(const typename object_t::key_type& key) const { // at only works for objects if (JSON_LIKELY(is_object())) { JSON_TRY { return m_value.object->at(key); } JSON_CATCH (std::out_of_range&) { // create better exception explanation JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); } } else { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); } } /*! @brief access specified array element Returns a reference to the element at specified location @a idx. @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), then the array is silently filled up with `null` values to make `idx` a valid reference to the last stored element. @param[in] idx index of the element to access @return reference to the element at index @a idx @throw type_error.305 if the JSON value is not an array or null; in that cases, using the [] operator with an index makes no sense. @complexity Constant if @a idx is in the range of the array. Otherwise linear in `idx - size()`. @liveexample{The example below shows how array elements can be read and written using `[]` operator. Note the addition of `null` values.,operatorarray__size_type} @since version 1.0.0 */ reference operator[](size_type idx) { // implicitly convert null value to an empty array if (is_null()) { m_type = value_t::array; m_value.array = create(); assert_invariant(); } // operator[] only works for arrays if (JSON_LIKELY(is_array())) { // fill up array with null values if given idx is outside range if (idx >= m_value.array->size()) { m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); } return m_value.array->operator[](idx); } JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @brief access specified array element Returns a const reference to the element at specified location @a idx. @param[in] idx index of the element to access @return const reference to the element at index @a idx @throw type_error.305 if the JSON value is not an array; in that case, using the [] operator with an index makes no sense. @complexity Constant. @liveexample{The example below shows how array elements can be read using the `[]` operator.,operatorarray__size_type_const} @since version 1.0.0 */ const_reference operator[](size_type idx) const { // const operator[] only works for arrays if (JSON_LIKELY(is_array())) { return m_value.array->operator[](idx); } JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @brief access specified object element Returns a reference to the element at with specified key @a key. @note If @a key is not found in the object, then it is silently added to the object and filled with a `null` value to make `key` a valid reference. In case the value was `null` before, it is converted to an object. @param[in] key key of the element to access @return reference to the element at key @a key @throw type_error.305 if the JSON value is not an object or null; in that cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.0.0 */ reference operator[](const typename object_t::key_type& key) { // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create(); assert_invariant(); } // operator[] only works for objects if (JSON_LIKELY(is_object())) { return m_value.object->operator[](key); } JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @brief read-only access specified object element Returns a const reference to the element at with specified key @a key. No bounds checking is performed. @warning If the element with key @a key does not exist, the behavior is undefined. @param[in] key key of the element to access @return const reference to the element at key @a key @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** @throw type_error.305 if the JSON value is not an object; in that case, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.0.0 */ const_reference operator[](const typename object_t::key_type& key) const { // const operator[] only works for objects if (JSON_LIKELY(is_object())) { assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @brief access specified object element Returns a reference to the element at with specified key @a key. @note If @a key is not found in the object, then it is silently added to the object and filled with a `null` value to make `key` a valid reference. In case the value was `null` before, it is converted to an object. @param[in] key key of the element to access @return reference to the element at key @a key @throw type_error.305 if the JSON value is not an object or null; in that cases, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.1.0 */ template reference operator[](T* key) { // implicitly convert null to object if (is_null()) { m_type = value_t::object; m_value = value_t::object; assert_invariant(); } // at only works for objects if (JSON_LIKELY(is_object())) { return m_value.object->operator[](key); } JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @brief read-only access specified object element Returns a const reference to the element at with specified key @a key. No bounds checking is performed. @warning If the element with key @a key does not exist, the behavior is undefined. @param[in] key key of the element to access @return const reference to the element at key @a key @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** @throw type_error.305 if the JSON value is not an object; in that case, using the [] operator with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref value() for access by value with a default value @since version 1.1.0 */ template const_reference operator[](T* key) const { // at only works for objects if (JSON_LIKELY(is_object())) { assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); } /*! @brief access specified object element with default value Returns either a copy of an object's element at the specified key @a key or a given default value if no element with key @a key exists. The function is basically equivalent to executing @code {.cpp} try { return at(key); } catch(out_of_range) { return default_value; } @endcode @note Unlike @ref at(const typename object_t::key_type&), this function does not throw if the given key @a key was not found. @note Unlike @ref operator[](const typename object_t::key_type& key), this function does not implicitly add an element to the position defined by @a key. This function is furthermore also applicable to const objects. @param[in] key key of the element to access @param[in] default_value the value to return if @a key is not found @tparam ValueType type compatible to JSON values, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. Note the type of the expected value at @a key and the default value @a default_value must be compatible. @return copy of the element at key @a key or @a default_value if @a key is not found @throw type_error.306 if the JSON value is not an object; in that case, using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be queried with a default value.,basic_json__value} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @since version 1.0.0 */ template::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // at only works for objects if (JSON_LIKELY(is_object())) { // if key is found, return value and given default value otherwise const auto it = find(key); if (it != end()) { return *it; } return default_value; } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const */ string_t value(const typename object_t::key_type& key, const char* default_value) const { return value(key, string_t(default_value)); } /*! @brief access specified object element via JSON Pointer with default value Returns either a copy of an object's element at the specified key @a key or a given default value if no element with key @a key exists. The function is basically equivalent to executing @code {.cpp} try { return at(ptr); } catch(out_of_range) { return default_value; } @endcode @note Unlike @ref at(const json_pointer&), this function does not throw if the given key @a key was not found. @param[in] ptr a JSON pointer to the element to access @param[in] default_value the value to return if @a ptr found no value @tparam ValueType type compatible to JSON values, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for JSON arrays. Note the type of the expected value at @a key and the default value @a default_value must be compatible. @return copy of the element at key @a key or @a default_value if @a key is not found @throw type_error.306 if the JSON value is not an objec; in that case, using `value()` with a key makes no sense. @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be queried with a default value.,basic_json__value_ptr} @sa @ref operator[](const json_pointer&) for unchecked access by reference @since version 2.0.2 */ template::value, int>::type = 0> ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // at only works for objects if (JSON_LIKELY(is_object())) { // if pointer resolves a value, return it or use default value JSON_TRY { return ptr.get_checked(this); } JSON_CATCH (out_of_range&) { return default_value; } } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const json_pointer&, ValueType) const */ string_t value(const json_pointer& ptr, const char* default_value) const { return value(ptr, string_t(default_value)); } /*! @brief access the first element Returns a reference to the first element in the container. For a JSON container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) or an empty array or object (undefined behavior, **guarded by assertions**). @post The JSON value remains unchanged. @throw invalid_iterator.214 when called on `null` value @liveexample{The following code shows an example for `front()`.,front} @sa @ref back() -- access the last element @since version 1.0.0 */ reference front() { return *begin(); } /*! @copydoc basic_json::front() */ const_reference front() const { return *cbegin(); } /*! @brief access the last element Returns a reference to the last element in the container. For a JSON container `c`, the expression `c.back()` is equivalent to @code {.cpp} auto tmp = c.end(); --tmp; return *tmp; @endcode @return In case of a structured type (array or object), a reference to the last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) or an empty array or object (undefined behavior, **guarded by assertions**). @post The JSON value remains unchanged. @throw invalid_iterator.214 when called on a `null` value. See example below. @liveexample{The following code shows an example for `back()`.,back} @sa @ref front() -- access the first element @since version 1.0.0 */ reference back() { auto tmp = end(); --tmp; return *tmp; } /*! @copydoc basic_json::back() */ const_reference back() const { auto tmp = cend(); --tmp; return *tmp; } /*! @brief remove element given an iterator Removes the element specified by iterator @a pos. The iterator @a pos must be valid and dereferenceable. Thus the `end()` iterator (which is valid, but is not dereferenceable) cannot be used as a value for @a pos. If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] pos iterator to the element to remove @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the `end()` iterator is returned. @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @throw type_error.307 if called on a `null` value; example: `"cannot use erase() with null"` @throw invalid_iterator.202 if called on an iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"` @throw invalid_iterator.205 if called on a primitive type with invalid iterator (i.e., any iterator which is not `begin()`); example: `"iterator out of range"` @complexity The complexity depends on the type: - objects: amortized constant - arrays: linear in distance between @a pos and the end of the container - strings: linear in the length of the string - other types: constant @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ template::value or std::is_same::value, int>::type = 0> IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (JSON_UNLIKELY(this != pos.m_object)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } IteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::number_unsigned: case value_t::string: { if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) { JSON_THROW(invalid_iterator::create(205, "iterator out of range")); } if (is_string()) { AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.string); std::allocator_traits::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; assert_invariant(); break; } case value_t::object: { result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } default: JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } return result; } /*! @brief remove elements given an iterator range Removes the element specified by the range `[first; last)`. The iterator @a first does not need to be dereferenceable if `first == last`: erasing an empty range is a no-op. If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] first iterator to the beginning of the range to remove @param[in] last iterator past the end of the range to remove @return Iterator following the last removed element. If the iterator @a second refers to the last element, the `end()` iterator is returned. @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @throw type_error.307 if called on a `null` value; example: `"cannot use erase() with null"` @throw invalid_iterator.203 if called on iterators which does not belong to the current JSON value; example: `"iterators do not fit current value"` @throw invalid_iterator.204 if called on a primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out of range"` @complexity The complexity depends on the type: - objects: `log(size()) + std::distance(first, last)` - arrays: linear in the distance between @a first and @a last, plus linear in the distance between @a last and end of the container - strings: linear in the length of the string - other types: constant @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ template::value or std::is_same::value, int>::type = 0> IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (JSON_UNLIKELY(this != first.m_object or this != last.m_object)) { JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); } IteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: case value_t::number_unsigned: case value_t::string: { if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())) { JSON_THROW(invalid_iterator::create(204, "iterators out of range")); } if (is_string()) { AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.string); std::allocator_traits::deallocate(alloc, m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; assert_invariant(); break; } case value_t::object: { result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; } default: JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } return result; } /*! @brief remove element from a JSON object given a key Removes elements from a JSON object with the key value @a key. @param[in] key value of the elements to remove @return Number of elements removed. If @a ObjectType is the default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). @post References and iterators to the erased elements are invalidated. Other references and iterators are not affected. @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` @liveexample{The example shows the effect of `erase()`.,erase__key_type} @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const size_type) -- removes the element from an array at the given index @since version 1.0.0 */ size_type erase(const typename object_t::key_type& key) { // this erase only works for objects if (JSON_LIKELY(is_object())) { return m_value.object->erase(key); } JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } /*! @brief remove element from a JSON array given an index Removes element from a JSON array at the index @a idx. @param[in] idx index of the element to remove @throw type_error.307 when called on a type other than JSON object; example: `"cannot use erase() with null"` @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 is out of range"` @complexity Linear in distance between @a idx and the end of the container. @liveexample{The example shows the effect of `erase()`.,erase__size_type} @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @since version 1.0.0 */ void erase(const size_type idx) { // this erase only works for arrays if (JSON_LIKELY(is_array())) { if (JSON_UNLIKELY(idx >= size())) { JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); } } /// @} //////////// // lookup // //////////// /// @name lookup /// @{ /*! @brief find an element in a JSON object Finds an element in a JSON object with key equivalent to @a key. If the element is not found or the JSON value is not an object, end() is returned. @note This method always returns @ref end() when executed on a JSON type that is not an object. @param[in] key key value of the element to search for. @return Iterator to an element with key equivalent to @a key. If no such element is found or the JSON value is not an object, past-the-end (see @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @liveexample{The example shows how `find()` is used.,find__key_type} @since version 1.0.0 */ template iterator find(KeyT&& key) { auto result = end(); if (is_object()) { result.m_it.object_iterator = m_value.object->find(std::forward(key)); } return result; } /*! @brief find an element in a JSON object @copydoc find(KeyT&&) */ template const_iterator find(KeyT&& key) const { auto result = cend(); if (is_object()) { result.m_it.object_iterator = m_value.object->find(std::forward(key)); } return result; } /*! @brief returns the number of occurrences of a key in a JSON object Returns the number of elements with key @a key. If ObjectType is the default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). @note This method always returns `0` when executed on a JSON type that is not an object. @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an object, the return value will be `0`. @complexity Logarithmic in the size of the JSON object. @liveexample{The example shows how `count()` is used.,count} @since version 1.0.0 */ template size_type count(KeyT&& key) const { // return 0 for all nonobject types return is_object() ? m_value.object->count(std::forward(key)) : 0; } /// @} /////////////// // iterators // /////////////// /// @name iterators /// @{ /*! @brief returns an iterator to the first element Returns an iterator to the first element. @image html range-begin-end.svg "Illustration from cppreference.com" @return iterator to the first element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is constant. @liveexample{The following code shows an example for `begin()`.,begin} @sa @ref cbegin() -- returns a const iterator to the beginning @sa @ref end() -- returns an iterator to the end @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ iterator begin() noexcept { iterator result(this); result.set_begin(); return result; } /*! @copydoc basic_json::cbegin() */ const_iterator begin() const noexcept { return cbegin(); } /*! @brief returns a const iterator to the first element Returns a const iterator to the first element. @image html range-begin-end.svg "Illustration from cppreference.com" @return const iterator to the first element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).begin()`. @liveexample{The following code shows an example for `cbegin()`.,cbegin} @sa @ref begin() -- returns an iterator to the beginning @sa @ref end() -- returns an iterator to the end @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ const_iterator cbegin() const noexcept { const_iterator result(this); result.set_begin(); return result; } /*! @brief returns an iterator to one past the last element Returns an iterator to one past the last element. @image html range-begin-end.svg "Illustration from cppreference.com" @return iterator one past the last element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is constant. @liveexample{The following code shows an example for `end()`.,end} @sa @ref cend() -- returns a const iterator to the end @sa @ref begin() -- returns an iterator to the beginning @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ iterator end() noexcept { iterator result(this); result.set_end(); return result; } /*! @copydoc basic_json::cend() */ const_iterator end() const noexcept { return cend(); } /*! @brief returns a const iterator to one past the last element Returns a const iterator to one past the last element. @image html range-begin-end.svg "Illustration from cppreference.com" @return const iterator one past the last element @complexity Constant. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).end()`. @liveexample{The following code shows an example for `cend()`.,cend} @sa @ref end() -- returns an iterator to the end @sa @ref begin() -- returns an iterator to the beginning @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ const_iterator cend() const noexcept { const_iterator result(this); result.set_end(); return result; } /*! @brief returns an iterator to the reverse-beginning Returns an iterator to the reverse-beginning; that is, the last element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(end())`. @liveexample{The following code shows an example for `rbegin()`.,rbegin} @sa @ref crbegin() -- returns a const reverse iterator to the beginning @sa @ref rend() -- returns a reverse iterator to the end @sa @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } /*! @copydoc basic_json::crbegin() */ const_reverse_iterator rbegin() const noexcept { return crbegin(); } /*! @brief returns an iterator to the reverse-end Returns an iterator to the reverse-end; that is, one before the first element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(begin())`. @liveexample{The following code shows an example for `rend()`.,rend} @sa @ref crend() -- returns a const reverse iterator to the end @sa @ref rbegin() -- returns a reverse iterator to the beginning @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ reverse_iterator rend() noexcept { return reverse_iterator(begin()); } /*! @copydoc basic_json::crend() */ const_reverse_iterator rend() const noexcept { return crend(); } /*! @brief returns a const reverse iterator to the last element Returns a const iterator to the reverse-beginning; that is, the last element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rbegin()`. @liveexample{The following code shows an example for `crbegin()`.,crbegin} @sa @ref rbegin() -- returns a reverse iterator to the beginning @sa @ref rend() -- returns a reverse iterator to the end @sa @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } /*! @brief returns a const reverse iterator to one before the first Returns a const reverse iterator to the reverse-end; that is, one before the first element. @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. @requirement This function helps `basic_json` satisfying the [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rend()`. @liveexample{The following code shows an example for `crend()`.,crend} @sa @ref rend() -- returns a reverse iterator to the end @sa @ref rbegin() -- returns a reverse iterator to the beginning @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } public: /*! @brief wrapper to access iterator member functions in range-based for This function allows to access @ref iterator::key() and @ref iterator::value() during range-based for loops. In these loops, a reference to the JSON values is returned, so there is no access to the underlying iterator. For loop without iterator_wrapper: @code{cpp} for (auto it = j_object.begin(); it != j_object.end(); ++it) { std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; } @endcode Range-based for loop without iterator proxy: @code{cpp} for (auto it : j_object) { // "it" is of type json::reference and has no key() member std::cout << "value: " << it << '\n'; } @endcode Range-based for loop with iterator proxy: @code{cpp} for (auto it : json::iterator_wrapper(j_object)) { std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; } @endcode @note When iterating over an array, `key()` will return the index of the element as string (see example). @param[in] ref reference to a JSON value @return iteration proxy object wrapping @a ref with an interface to use in range-based for loops @liveexample{The following code shows how the wrapper is used,iterator_wrapper} @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @note The name of this function is not yet final and may change in the future. @deprecated This stream operator is deprecated and will be removed in future 4.0.0 of the library. Please use @ref items() instead; that is, replace `json::iterator_wrapper(j)` with `j.items()`. */ JSON_DEPRECATED static iteration_proxy iterator_wrapper(reference ref) noexcept { return ref.items(); } /*! @copydoc iterator_wrapper(reference) */ JSON_DEPRECATED static iteration_proxy iterator_wrapper(const_reference ref) noexcept { return ref.items(); } /*! @brief helper to access iterator member functions in range-based for This function allows to access @ref iterator::key() and @ref iterator::value() during range-based for loops. In these loops, a reference to the JSON values is returned, so there is no access to the underlying iterator. For loop without `items()` function: @code{cpp} for (auto it = j_object.begin(); it != j_object.end(); ++it) { std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; } @endcode Range-based for loop without `items()` function: @code{cpp} for (auto it : j_object) { // "it" is of type json::reference and has no key() member std::cout << "value: " << it << '\n'; } @endcode Range-based for loop with `items()` function: @code{cpp} for (auto it : j_object.items()) { std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; } @endcode @note When iterating over an array, `key()` will return the index of the element as string (see example). For primitive types (e.g., numbers), `key()` returns an empty string. @return iteration proxy object wrapping @a ref with an interface to use in range-based for loops @liveexample{The following code shows how the function is used.,items} @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 3.x.x. */ iteration_proxy items() noexcept { return iteration_proxy(*this); } /*! @copydoc items() */ iteration_proxy items() const noexcept { return iteration_proxy(*this); } /// @} ////////////// // capacity // ////////////// /// @name capacity /// @{ /*! @brief checks whether the container is empty. Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | `true` boolean | `false` string | `false` number | `false` object | result of function `object_t::empty()` array | result of function `array_t::empty()` @liveexample{The following code uses `empty()` to check if a JSON object contains any elements.,empty} @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their `empty()` functions have constant complexity. @iterators No changes. @exceptionsafety No-throw guarantee: this function never throws exceptions. @note This function does not return whether a string stored as JSON value is empty - it returns whether the JSON container itself is empty which is false in the case of a string. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is constant. - Has the semantics of `begin() == end()`. @sa @ref size() -- returns the number of elements @since version 1.0.0 */ bool empty() const noexcept { switch (m_type) { case value_t::null: { // null values are empty return true; } case value_t::array: { // delegate call to array_t::empty() return m_value.array->empty(); } case value_t::object: { // delegate call to object_t::empty() return m_value.object->empty(); } default: { // all other types are nonempty return false; } } } /*! @brief returns the number of elements Returns the number of elements in a JSON value. @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | `0` boolean | `1` string | `1` number | `1` object | result of function object_t::size() array | result of function array_t::size() @liveexample{The following code calls `size()` on the different value types.,size} @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their size() functions have constant complexity. @iterators No changes. @exceptionsafety No-throw guarantee: this function never throws exceptions. @note This function does not return the length of a string stored as JSON value - it returns the number of elements in the JSON value which is 1 in the case of a string. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is constant. - Has the semantics of `std::distance(begin(), end())`. @sa @ref empty() -- checks whether the container is empty @sa @ref max_size() -- returns the maximal number of elements @since version 1.0.0 */ size_type size() const noexcept { switch (m_type) { case value_t::null: { // null values are empty return 0; } case value_t::array: { // delegate call to array_t::size() return m_value.array->size(); } case value_t::object: { // delegate call to object_t::size() return m_value.object->size(); } default: { // all other types have size 1 return 1; } } } /*! @brief returns the maximum possible number of elements Returns the maximum number of elements a JSON value is able to hold due to system or library implementation limitations, i.e. `std::distance(begin(), end())` for the JSON value. @return The return value depends on the different types and is defined as follows: Value type | return value ----------- | ------------- null | `0` (same as `size()`) boolean | `1` (same as `size()`) string | `1` (same as `size()`) number | `1` (same as `size()`) object | result of function `object_t::max_size()` array | result of function `array_t::max_size()` @liveexample{The following code calls `max_size()` on the different value types. Note the output is implementation specific.,max_size} @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their `max_size()` functions have constant complexity. @iterators No changes. @exceptionsafety No-throw guarantee: this function never throws exceptions. @requirement This function helps `basic_json` satisfying the [Container](http://en.cppreference.com/w/cpp/concept/Container) requirements: - The complexity is constant. - Has the semantics of returning `b.size()` where `b` is the largest possible JSON value. @sa @ref size() -- returns the number of elements @since version 1.0.0 */ size_type max_size() const noexcept { switch (m_type) { case value_t::array: { // delegate call to array_t::max_size() return m_value.array->max_size(); } case value_t::object: { // delegate call to object_t::max_size() return m_value.object->max_size(); } default: { // all other types have max_size() == size() return size(); } } } /// @} /////////////// // modifiers // /////////////// /// @name modifiers /// @{ /*! @brief clears the contents Clears the content of a JSON value and resets it to the default value as if @ref basic_json(value_t) would have been called with the current value type from @ref type(): Value type | initial value ----------- | ------------- null | `null` boolean | `false` string | `""` number | `0` object | `{}` array | `[]` @post Has the same effect as calling @code {.cpp} *this = basic_json(type()); @endcode @liveexample{The example below shows the effect of `clear()` to different JSON types.,clear} @complexity Linear in the size of the JSON value. @iterators All iterators, pointers and references related to this container are invalidated. @exceptionsafety No-throw guarantee: this function never throws exceptions. @sa @ref basic_json(value_t) -- constructor that creates an object with the same value than calling `clear()` @since version 1.0.0 */ void clear() noexcept { switch (m_type) { case value_t::number_integer: { m_value.number_integer = 0; break; } case value_t::number_unsigned: { m_value.number_unsigned = 0; break; } case value_t::number_float: { m_value.number_float = 0.0; break; } case value_t::boolean: { m_value.boolean = false; break; } case value_t::string: { m_value.string->clear(); break; } case value_t::array: { m_value.array->clear(); break; } case value_t::object: { m_value.object->clear(); break; } default: break; } } /*! @brief add an object to an array Appends the given element @a val to the end of the JSON value. If the function is called on a JSON null value, an empty array is created before appending @a val. @param[in] val the value to add to the JSON array @throw type_error.308 when called on a type other than JSON array or null; example: `"cannot use push_back() with number"` @complexity Amortized constant. @liveexample{The example shows how `push_back()` and `+=` can be used to add elements to a JSON array. Note how the `null` value was silently converted to a JSON array.,push_back} @since version 1.0.0 */ void push_back(basic_json&& val) { // push_back only works for null objects or arrays if (JSON_UNLIKELY(not(is_null() or is_array()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; assert_invariant(); } // add element to array (move semantics) m_value.array->push_back(std::move(val)); // invalidate object val.m_type = value_t::null; } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ reference operator+=(basic_json&& val) { push_back(std::move(val)); return *this; } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ void push_back(const basic_json& val) { // push_back only works for null objects or arrays if (JSON_UNLIKELY(not(is_null() or is_array()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; assert_invariant(); } // add element to array m_value.array->push_back(val); } /*! @brief add an object to an array @copydoc push_back(basic_json&&) */ reference operator+=(const basic_json& val) { push_back(val); return *this; } /*! @brief add an object to an object Inserts the given element @a val to the JSON object. If the function is called on a JSON null value, an empty object is created before inserting @a val. @param[in] val the value to add to the JSON object @throw type_error.308 when called on a type other than JSON object or null; example: `"cannot use push_back() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @liveexample{The example shows how `push_back()` and `+=` can be used to add elements to a JSON object. Note how the `null` value was silently converted to a JSON object.,push_back__object_t__value} @since version 1.0.0 */ void push_back(const typename object_t::value_type& val) { // push_back only works for null objects or objects if (JSON_UNLIKELY(not(is_null() or is_object()))) { JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); } // transform null object into an object if (is_null()) { m_type = value_t::object; m_value = value_t::object; assert_invariant(); } // add element to array m_value.object->insert(val); } /*! @brief add an object to an object @copydoc push_back(const typename object_t::value_type&) */ reference operator+=(const typename object_t::value_type& val) { push_back(val); return *this; } /*! @brief add an object to an object This function allows to use `push_back` with an initializer list. In case 1. the current value is an object, 2. the initializer list @a init contains only two elements, and 3. the first element of @a init is a string, @a init is converted into an object element and added using @ref push_back(const typename object_t::value_type&). Otherwise, @a init is converted to a JSON value and added using @ref push_back(basic_json&&). @param[in] init an initializer list @complexity Linear in the size of the initializer list @a init. @note This function is required to resolve an ambiguous overload error, because pairs like `{"key", "value"}` can be both interpreted as `object_t::value_type` or `std::initializer_list`, see https://github.com/nlohmann/json/issues/235 for more information. @liveexample{The example shows how initializer lists are treated as objects when possible.,push_back__initializer_list} */ void push_back(initializer_list_t init) { if (is_object() and init.size() == 2 and (*init.begin())->is_string()) { basic_json&& key = init.begin()->moved_or_copied(); push_back(typename object_t::value_type( std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); } else { push_back(basic_json(init)); } } /*! @brief add an object to an object @copydoc push_back(initializer_list_t) */ reference operator+=(initializer_list_t init) { push_back(init); return *this; } /*! @brief add an object to an array Creates a JSON value from the passed parameters @a args to the end of the JSON value. If the function is called on a JSON null value, an empty array is created before appending the value created from @a args. @param[in] args arguments to forward to a constructor of @ref basic_json @tparam Args compatible types to create a @ref basic_json object @throw type_error.311 when called on a type other than JSON array or null; example: `"cannot use emplace_back() with number"` @complexity Amortized constant. @liveexample{The example shows how `push_back()` can be used to add elements to a JSON array. Note how the `null` value was silently converted to a JSON array.,emplace_back} @since version 2.0.8 */ template void emplace_back(Args&& ... args) { // emplace_back only works for null objects or arrays if (JSON_UNLIKELY(not(is_null() or is_array()))) { JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); } // transform null object into an array if (is_null()) { m_type = value_t::array; m_value = value_t::array; assert_invariant(); } // add element to array (perfect forwarding) m_value.array->emplace_back(std::forward(args)...); } /*! @brief add an object to an object if key does not exist Inserts a new element into a JSON object constructed in-place with the given @a args if there is no element with the key in the container. If the function is called on a JSON null value, an empty object is created before appending the value created from @a args. @param[in] args arguments to forward to a constructor of @ref basic_json @tparam Args compatible types to create a @ref basic_json object @return a pair consisting of an iterator to the inserted element, or the already-existing element if no insertion happened, and a bool denoting whether the insertion took place. @throw type_error.311 when called on a type other than JSON object or null; example: `"cannot use emplace() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @liveexample{The example shows how `emplace()` can be used to add elements to a JSON object. Note how the `null` value was silently converted to a JSON object. Further note how no value is added if there was already one value stored with the same key.,emplace} @since version 2.0.8 */ template std::pair emplace(Args&& ... args) { // emplace only works for null objects or arrays if (JSON_UNLIKELY(not(is_null() or is_object()))) { JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); } // transform null object into an object if (is_null()) { m_type = value_t::object; m_value = value_t::object; assert_invariant(); } // add element to array (perfect forwarding) auto res = m_value.object->emplace(std::forward(args)...); // create result iterator and set iterator to the result of emplace auto it = begin(); it.m_it.object_iterator = res.first; // return pair of iterator and boolean return {it, res.second}; } /*! @brief inserts element Inserts element @a val before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] val element to insert @return iterator pointing to the inserted @a val. @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @complexity Constant plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert} @since version 1.0.0 */ iterator insert(const_iterator pos, const basic_json& val) { // insert only works for arrays if (JSON_LIKELY(is_array())) { // check if iterator pos fits to this JSON value if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } /*! @brief inserts element @copydoc insert(const_iterator, const basic_json&) */ iterator insert(const_iterator pos, basic_json&& val) { return insert(pos, val); } /*! @brief inserts elements Inserts @a cnt copies of @a val before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] cnt number of copies of @a val to insert @param[in] val element to insert @return iterator pointing to the first element inserted, or @a pos if `cnt==0` @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert__count} @since version 1.0.0 */ iterator insert(const_iterator pos, size_type cnt, const basic_json& val) { // insert only works for arrays if (JSON_LIKELY(is_array())) { // check if iterator pos fits to this JSON value if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } /*! @brief inserts elements Inserts elements from range `[first, last)` before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @throw invalid_iterator.210 if @a first and @a last do not belong to the same JSON value; example: `"iterators do not fit"` @throw invalid_iterator.211 if @a first or @a last are iterators into container for which insert is called; example: `"passed iterators may not belong to container"` @return iterator pointing to the first element inserted, or @a pos if `first==last` @complexity Linear in `std::distance(first, last)` plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert__range} @since version 1.0.0 */ iterator insert(const_iterator pos, const_iterator first, const_iterator last) { // insert only works for arrays if (JSON_UNLIKELY(not is_array())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (JSON_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } if (JSON_UNLIKELY(first.m_object == this)) { JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert( pos.m_it.array_iterator, first.m_it.array_iterator, last.m_it.array_iterator); return result; } /*! @brief inserts elements Inserts elements from initializer list @a ilist before iterator @a pos. @param[in] pos iterator before which the content will be inserted; may be the end() iterator @param[in] ilist initializer list to insert the values from @throw type_error.309 if called on JSON values other than arrays; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` @return iterator pointing to the first element inserted, or @a pos if `ilist` is empty @complexity Linear in `ilist.size()` plus linear in the distance between @a pos and end of the container. @liveexample{The example shows how `insert()` is used.,insert__ilist} @since version 1.0.0 */ iterator insert(const_iterator pos, initializer_list_t ilist) { // insert only works for arrays if (JSON_UNLIKELY(not is_array())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } // check if iterator pos fits to this JSON value if (JSON_UNLIKELY(pos.m_object != this)) { JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end()); return result; } /*! @brief inserts elements Inserts elements from range `[first, last)`. @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert @throw type_error.309 if called on JSON values other than objects; example: `"cannot use insert() with string"` @throw invalid_iterator.202 if iterator @a first or @a last does does not point to an object; example: `"iterators first and last must point to objects"` @throw invalid_iterator.210 if @a first and @a last do not belong to the same JSON value; example: `"iterators do not fit"` @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number of elements to insert. @liveexample{The example shows how `insert()` is used.,insert__range_object} @since version 3.0.0 */ void insert(const_iterator first, const_iterator last) { // insert only works for objects if (JSON_UNLIKELY(not is_object())) { JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); } // check if range iterators belong to the same JSON object if (JSON_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } // passed iterators must belong to objects if (JSON_UNLIKELY(not first.m_object->is_object())) { JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); } m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); } /*! @brief updates a JSON object from another object, overwriting existing keys Inserts all values from JSON object @a j and overwrites existing keys. @param[in] j JSON object to read values from @throw type_error.312 if called on JSON values other than objects; example: `"cannot use update() with string"` @complexity O(N*log(size() + N)), where N is the number of elements to insert. @liveexample{The example shows how `update()` is used.,update} @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @since version 3.0.0 */ void update(const_reference j) { // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create(); assert_invariant(); } if (JSON_UNLIKELY(not is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); } if (JSON_UNLIKELY(not j.is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); } for (auto it = j.cbegin(); it != j.cend(); ++it) { m_value.object->operator[](it.key()) = it.value(); } } /*! @brief updates a JSON object from another object, overwriting existing keys Inserts all values from from range `[first, last)` and overwrites existing keys. @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert @throw type_error.312 if called on JSON values other than objects; example: `"cannot use update() with string"` @throw invalid_iterator.202 if iterator @a first or @a last does does not point to an object; example: `"iterators first and last must point to objects"` @throw invalid_iterator.210 if @a first and @a last do not belong to the same JSON value; example: `"iterators do not fit"` @complexity O(N*log(size() + N)), where N is the number of elements to insert. @liveexample{The example shows how `update()` is used__range.,update} @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @since version 3.0.0 */ void update(const_iterator first, const_iterator last) { // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create(); assert_invariant(); } if (JSON_UNLIKELY(not is_object())) { JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); } // check if range iterators belong to the same JSON object if (JSON_UNLIKELY(first.m_object != last.m_object)) { JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); } // passed iterators must belong to objects if (JSON_UNLIKELY(not first.m_object->is_object() or not last.m_object->is_object())) { JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); } for (auto it = first; it != last; ++it) { m_value.object->operator[](it.key()) = it.value(); } } /*! @brief exchanges the values Exchanges the contents of the JSON value with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other JSON value to exchange the contents with @complexity Constant. @liveexample{The example below shows how JSON values can be swapped with `swap()`.,swap__reference} @since version 1.0.0 */ void swap(reference other) noexcept ( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); assert_invariant(); } /*! @brief exchanges the values Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other array to exchange the contents with @throw type_error.310 when JSON value is not an array; example: `"cannot use swap() with string"` @complexity Constant. @liveexample{The example below shows how arrays can be swapped with `swap()`.,swap__array_t} @since version 1.0.0 */ void swap(array_t& other) { // swap only works for arrays if (JSON_LIKELY(is_array())) { std::swap(*(m_value.array), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); } } /*! @brief exchanges the values Exchanges the contents of a JSON object with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other object to exchange the contents with @throw type_error.310 when JSON value is not an object; example: `"cannot use swap() with string"` @complexity Constant. @liveexample{The example below shows how objects can be swapped with `swap()`.,swap__object_t} @since version 1.0.0 */ void swap(object_t& other) { // swap only works for objects if (JSON_LIKELY(is_object())) { std::swap(*(m_value.object), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); } } /*! @brief exchanges the values Exchanges the contents of a JSON string with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is invalidated. @param[in,out] other string to exchange the contents with @throw type_error.310 when JSON value is not a string; example: `"cannot use swap() with boolean"` @complexity Constant. @liveexample{The example below shows how strings can be swapped with `swap()`.,swap__string_t} @since version 1.0.0 */ void swap(string_t& other) { // swap only works for strings if (JSON_LIKELY(is_string())) { std::swap(*(m_value.string), other); } else { JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); } } /// @} public: ////////////////////////////////////////// // lexicographical comparison operators // ////////////////////////////////////////// /// @name lexicographical comparison operators /// @{ /*! @brief comparison: equal Compares two JSON values for equality according to the following rules: - Two JSON values are equal if (1) they are from the same type and (2) their stored values are the same according to their respective `operator==`. - Integer and floating-point numbers are automatically converted before comparison. Note than two NaN values are always treated as unequal. - Two JSON null values are equal. @note Floating-point inside JSON values numbers are compared with `json::number_float_t::operator==` which is `double::operator==` by default. To compare floating-point while respecting an epsilon, an alternative [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39) could be used, for instance @code {.cpp} template::value, T>::type> inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept { return std::abs(a - b) <= epsilon; } @endcode @note NaN values never compare equal to themselves or to other NaN values. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether the values @a lhs and @a rhs are equal @exceptionsafety No-throw guarantee: this function never throws exceptions. @complexity Linear. @liveexample{The example demonstrates comparing several JSON types.,operator__equal} @since version 1.0.0 */ friend bool operator==(const_reference lhs, const_reference rhs) noexcept { const auto lhs_type = lhs.type(); const auto rhs_type = rhs.type(); if (lhs_type == rhs_type) { switch (lhs_type) { case value_t::array: return (*lhs.m_value.array == *rhs.m_value.array); case value_t::object: return (*lhs.m_value.object == *rhs.m_value.object); case value_t::null: return true; case value_t::string: return (*lhs.m_value.string == *rhs.m_value.string); case value_t::boolean: return (lhs.m_value.boolean == rhs.m_value.boolean); case value_t::number_integer: return (lhs.m_value.number_integer == rhs.m_value.number_integer); case value_t::number_unsigned: return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned); case value_t::number_float: return (lhs.m_value.number_float == rhs.m_value.number_float); default: return false; } } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { return (static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float); } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { return (lhs.m_value.number_float == static_cast(rhs.m_value.number_integer)); } else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { return (static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float); } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { return (lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned)); } else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { return (static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer); } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { return (lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned)); } return false; } /*! @brief comparison: equal @copydoc operator==(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept { return (lhs == basic_json(rhs)); } /*! @brief comparison: equal @copydoc operator==(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) == rhs); } /*! @brief comparison: not equal Compares two JSON values for inequality by calculating `not (lhs == rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether the values @a lhs and @a rhs are not equal @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__notequal} @since version 1.0.0 */ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { return not (lhs == rhs); } /*! @brief comparison: not equal @copydoc operator!=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept { return (lhs != basic_json(rhs)); } /*! @brief comparison: not equal @copydoc operator!=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) != rhs); } /*! @brief comparison: less than Compares whether one JSON value @a lhs is less than another JSON value @a rhs according to the following rules: - If @a lhs and @a rhs have the same type, the values are compared using the default `<` operator. - Integer and floating-point numbers are automatically converted before comparison - In case @a lhs and @a rhs have different types, the values are ignored and the order of the types is considered, see @ref operator<(const value_t, const value_t). @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is less than @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__less} @since version 1.0.0 */ friend bool operator<(const_reference lhs, const_reference rhs) noexcept { const auto lhs_type = lhs.type(); const auto rhs_type = rhs.type(); if (lhs_type == rhs_type) { switch (lhs_type) { case value_t::array: return (*lhs.m_value.array) < (*rhs.m_value.array); case value_t::object: return *lhs.m_value.object < *rhs.m_value.object; case value_t::null: return false; case value_t::string: return *lhs.m_value.string < *rhs.m_value.string; case value_t::boolean: return lhs.m_value.boolean < rhs.m_value.boolean; case value_t::number_integer: return lhs.m_value.number_integer < rhs.m_value.number_integer; case value_t::number_unsigned: return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; case value_t::number_float: return lhs.m_value.number_float < rhs.m_value.number_float; default: return false; } } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); } else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); } else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; } // We only reach this line if we cannot compare values. In that case, // we compare types. Note we have to call the operator explicitly, // because MSVC has problems otherwise. return operator<(lhs_type, rhs_type); } /*! @brief comparison: less than @copydoc operator<(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept { return (lhs < basic_json(rhs)); } /*! @brief comparison: less than @copydoc operator<(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) < rhs); } /*! @brief comparison: less than or equal Compares whether one JSON value @a lhs is less than or equal to another JSON value by calculating `not (rhs < lhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is less than or equal to @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__greater} @since version 1.0.0 */ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { return not (rhs < lhs); } /*! @brief comparison: less than or equal @copydoc operator<=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept { return (lhs <= basic_json(rhs)); } /*! @brief comparison: less than or equal @copydoc operator<=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) <= rhs); } /*! @brief comparison: greater than Compares whether one JSON value @a lhs is greater than another JSON value by calculating `not (lhs <= rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is greater than to @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__lessequal} @since version 1.0.0 */ friend bool operator>(const_reference lhs, const_reference rhs) noexcept { return not (lhs <= rhs); } /*! @brief comparison: greater than @copydoc operator>(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept { return (lhs > basic_json(rhs)); } /*! @brief comparison: greater than @copydoc operator>(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) > rhs); } /*! @brief comparison: greater than or equal Compares whether one JSON value @a lhs is greater than or equal to another JSON value by calculating `not (lhs < rhs)`. @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether @a lhs is greater than or equal to @a rhs @complexity Linear. @exceptionsafety No-throw guarantee: this function never throws exceptions. @liveexample{The example demonstrates comparing several JSON types.,operator__greaterequal} @since version 1.0.0 */ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { return not (lhs < rhs); } /*! @brief comparison: greater than or equal @copydoc operator>=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept { return (lhs >= basic_json(rhs)); } /*! @brief comparison: greater than or equal @copydoc operator>=(const_reference, const_reference) */ template::value, int>::type = 0> friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) >= rhs); } /// @} /////////////////// // serialization // /////////////////// /// @name serialization /// @{ /*! @brief serialize to stream Serialize the given JSON value @a j to the output stream @a o. The JSON value will be serialized using the @ref dump member function. - The indentation of the output can be controlled with the member variable `width` of the output stream @a o. For instance, using the manipulator `std::setw(4)` on @a o sets the indentation level to `4` and the serialization result is the same as calling `dump(4)`. - The indentation character can be controlled with the member variable `fill` of the output stream @a o. For instance, the manipulator `std::setfill('\\t')` sets indentation to use a tab character rather than the default space character. @param[in,out] o stream to serialize to @param[in] j JSON value to serialize @return the stream @a o @throw type_error.316 if a string stored inside the JSON value is not UTF-8 encoded @complexity Linear. @liveexample{The example below shows the serialization with different parameters to `width` to adjust the indentation level.,operator_serialize} @since version 1.0.0; indentation character added in version 3.0.0 */ friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { // read width member and use it as indentation parameter if nonzero const bool pretty_print = (o.width() > 0); const auto indentation = (pretty_print ? o.width() : 0); // reset width to 0 for subsequent calls to this stream o.width(0); // do the actual serialization serializer s(detail::output_adapter(o), o.fill()); s.dump(j, pretty_print, false, static_cast(indentation)); return o; } /*! @brief serialize to stream @deprecated This stream operator is deprecated and will be removed in future 4.0.0 of the library. Please use @ref operator<<(std::ostream&, const basic_json&) instead; that is, replace calls like `j >> o;` with `o << j;`. @since version 1.0.0; deprecated since version 3.0.0 */ JSON_DEPRECATED friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { return o << j; } /// @} ///////////////////// // deserialization // ///////////////////// /// @name deserialization /// @{ /*! @brief deserialize from a compatible input This function reads from a compatible input. Examples are: - an array of 1-byte values - strings with character/literal type with size of 1 byte - input streams - container with contiguous storage of 1-byte values. Compatible container types include `std::vector`, `std::string`, `std::array`, `std::valarray`, and `std::initializer_list`. Furthermore, C-style arrays can be used with `std::begin()`/`std::end()`. User-defined containers can be used as long as they implement random-access iterators and a contiguous storage. @pre Each element of the container has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced with a static assertion.** @pre The container storage is contiguous. Violating this precondition yields undefined behavior. **This precondition is enforced with an assertion.** @pre Each element of the container has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced with a static assertion.** @warning There is no way to enforce all preconditions at compile-time. If the function is called with a noncompliant container and with assertions switched off, the behavior is undefined and will most likely yield segmentation violation. @param[in] i input to read from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @return result of the deserialization @throw parse_error.101 if a parse error occurs; example: `""unexpected end of input; expected string literal""` @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below demonstrates the `parse()` function reading from an array.,parse__array__parser_callback_t} @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} @liveexample{The example below demonstrates the `parse()` function reading from a contiguous container.,parse__contiguouscontainer__parser_callback_t} @since version 2.0.3 (contiguous containers) */ static basic_json parse(detail::input_adapter i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) { basic_json result; parser(i, cb, allow_exceptions).parse(true, result); return result; } /*! @copydoc basic_json parse(detail::input_adapter, const parser_callback_t) */ static basic_json parse(detail::input_adapter& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) { basic_json result; parser(i, cb, allow_exceptions).parse(true, result); return result; } static bool accept(detail::input_adapter i) { return parser(i).accept(true); } static bool accept(detail::input_adapter& i) { return parser(i).accept(true); } /*! @brief deserialize from an iterator range with contiguous storage This function reads from an iterator range of a container with contiguous storage of 1-byte values. Compatible container types include `std::vector`, `std::string`, `std::array`, `std::valarray`, and `std::initializer_list`. Furthermore, C-style arrays can be used with `std::begin()`/`std::end()`. User-defined containers can be used as long as they implement random-access iterators and a contiguous storage. @pre The iterator range is contiguous. Violating this precondition yields undefined behavior. **This precondition is enforced with an assertion.** @pre Each element in the range has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced with a static assertion.** @warning There is no way to enforce all preconditions at compile-time. If the function is called with noncompliant iterators and with assertions switched off, the behavior is undefined and will most likely yield segmentation violation. @tparam IteratorType iterator of container with contiguous storage @param[in] first begin of the range to parse (included) @param[in] last end of the range to parse (excluded) @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) @return result of the deserialization @throw parse_error.101 in case of an unexpected token @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. The complexity can be higher if the parser callback function @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below demonstrates the `parse()` function reading from an iterator range.,parse__iteratortype__parser_callback_t} @since version 2.0.3 */ template::iterator_category>::value, int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) { basic_json result; parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result); return result; } template::iterator_category>::value, int>::type = 0> static bool accept(IteratorType first, IteratorType last) { return parser(detail::input_adapter(first, last)).accept(true); } /*! @brief deserialize from stream @deprecated This stream operator is deprecated and will be removed in version 4.0.0 of the library. Please use @ref operator>>(std::istream&, basic_json&) instead; that is, replace calls like `j << i;` with `i >> j;`. @since version 1.0.0; deprecated since version 3.0.0 */ JSON_DEPRECATED friend std::istream& operator<<(basic_json& j, std::istream& i) { return operator>>(i, j); } /*! @brief deserialize from stream Deserializes an input stream to a JSON value. @param[in,out] i input stream to read a serialized JSON value from @param[in,out] j JSON value to write the deserialized input to @throw parse_error.101 in case of an unexpected token @throw parse_error.102 if to_unicode fails or surrogate error @throw parse_error.103 if to_unicode fails @complexity Linear in the length of the input. The parser is a predictive LL(1) parser. @note A UTF-8 byte order mark is silently ignored. @liveexample{The example below shows how a JSON value is constructed by reading a serialization from a stream.,operator_deserialize} @sa parse(std::istream&, const parser_callback_t) for a variant with a parser callback function to filter values while parsing @since version 1.0.0 */ friend std::istream& operator>>(std::istream& i, basic_json& j) { parser(detail::input_adapter(i)).parse(false, j); return i; } /// @} /////////////////////////// // convenience functions // /////////////////////////// /*! @brief return the type as string Returns the type name as string to be used in error messages - usually to indicate that a function was called on a wrong JSON type. @return a string representation of a the @a m_type member: Value type | return value ----------- | ------------- null | `"null"` boolean | `"boolean"` string | `"string"` number | `"number"` (for all number types) object | `"object"` array | `"array"` discarded | `"discarded"` @exceptionsafety No-throw guarantee: this function never throws exceptions. @complexity Constant. @liveexample{The following code exemplifies `type_name()` for all JSON types.,type_name} @sa @ref type() -- return the type of the JSON value @sa @ref operator value_t() -- return the type of the JSON value (implicit) @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` since 3.0.0 */ const char* type_name() const noexcept { { switch (m_type) { case value_t::null: return "null"; case value_t::object: return "object"; case value_t::array: return "array"; case value_t::string: return "string"; case value_t::boolean: return "boolean"; case value_t::discarded: return "discarded"; default: return "number"; } } } private: ////////////////////// // member variables // ////////////////////// /// the type of the current element value_t m_type = value_t::null; /// the value of the current element json_value m_value = {}; ////////////////////////////////////////// // binary serialization/deserialization // ////////////////////////////////////////// /// @name binary serialization/deserialization support /// @{ public: /*! @brief create a CBOR serialization of a given JSON value Serializes a given JSON value @a j to a byte vector using the CBOR (Concise Binary Object Representation) serialization format. CBOR is a binary serialization format which aims to be more compact than JSON itself, yet more efficient to parse. The library uses the following mapping from JSON values types to CBOR types according to the CBOR specification (RFC 7049): JSON value type | value/range | CBOR type | first byte --------------- | ------------------------------------------ | ---------------------------------- | --------------- null | `null` | Null | 0xF6 boolean | `true` | True | 0xF5 boolean | `false` | False | 0xF4 number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 number_integer | -24..-1 | Negative integer | 0x20..0x37 number_integer | 0..23 | Integer | 0x00..0x17 number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B number_unsigned | 0..23 | Integer | 0x00..0x17 number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B number_float | *any value* | Double-Precision Float | 0xFB string | *length*: 0..23 | UTF-8 string | 0x60..0x77 string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B array | *size*: 0..23 | array | 0x80..0x97 array | *size*: 23..255 | array (1 byte follow) | 0x98 array | *size*: 256..65535 | array (2 bytes follow) | 0x99 array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B object | *size*: 0..23 | map | 0xA0..0xB7 object | *size*: 23..255 | map (1 byte follow) | 0xB8 object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB @note The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value. @note If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the @ref dump() function which serializes NaN or Infinity to `null`. @note The following CBOR types are not used in the conversion: - byte strings (0x40..0x5F) - UTF-8 strings terminated by "break" (0x7F) - arrays terminated by "break" (0x9F) - maps terminated by "break" (0xBF) - date/time (0xC0..0xC1) - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - half and single-precision floats (0xF9-0xFA) - break (0xFF) @param[in] j JSON value to serialize @return MessagePack serialization as byte vector @complexity Linear in the size of the JSON value @a j. @liveexample{The example shows the serialization of a JSON value to a byte vector in CBOR format.,to_cbor} @sa http://cbor.io @sa @ref from_cbor(detail::input_adapter, const bool strict) for the analogous deserialization @sa @ref to_msgpack(const basic_json&) for the related MessagePack format @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format @since version 2.0.9 */ static std::vector to_cbor(const basic_json& j) { std::vector result; to_cbor(j, result); return result; } static void to_cbor(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_cbor(j); } static void to_cbor(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_cbor(j); } /*! @brief create a MessagePack serialization of a given JSON value Serializes a given JSON value @a j to a byte vector using the MessagePack serialization format. MessagePack is a binary serialization format which aims to be more compact than JSON itself, yet more efficient to parse. The library uses the following mapping from JSON values types to MessagePack types according to the MessagePack specification: JSON value type | value/range | MessagePack type | first byte --------------- | --------------------------------- | ---------------- | ---------- null | `null` | nil | 0xC0 boolean | `true` | true | 0xC3 boolean | `false` | false | 0xC2 number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 number_integer | -2147483648..-32769 | int32 | 0xD2 number_integer | -32768..-129 | int16 | 0xD1 number_integer | -128..-33 | int8 | 0xD0 number_integer | -32..-1 | negative fixint | 0xE0..0xFF number_integer | 0..127 | positive fixint | 0x00..0x7F number_integer | 128..255 | uint 8 | 0xCC number_integer | 256..65535 | uint 16 | 0xCD number_integer | 65536..4294967295 | uint 32 | 0xCE number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF number_unsigned | 0..127 | positive fixint | 0x00..0x7F number_unsigned | 128..255 | uint 8 | 0xCC number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF number_float | *any value* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA string | *length*: 65536..4294967295 | str 32 | 0xDB array | *size*: 0..15 | fixarray | 0x90..0x9F array | *size*: 16..65535 | array 16 | 0xDC array | *size*: 65536..4294967295 | array 32 | 0xDD object | *size*: 0..15 | fix map | 0x80..0x8F object | *size*: 16..65535 | map 16 | 0xDE object | *size*: 65536..4294967295 | map 32 | 0xDF @note The mapping is **complete** in the sense that any JSON value type can be converted to a MessagePack value. @note The following values can **not** be converted to a MessagePack value: - strings with more than 4294967295 bytes - arrays with more than 4294967295 elements - objects with more than 4294967295 elements @note The following MessagePack types are not used in the conversion: - bin 8 - bin 32 (0xC4..0xC6) - ext 8 - ext 32 (0xC7..0xC9) - float 32 (0xCA) - fixext 1 - fixext 16 (0xD4..0xD8) @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. @note If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the @ref dump() function which serializes NaN or Infinity to `null`. @param[in] j JSON value to serialize @return MessagePack serialization as byte vector @complexity Linear in the size of the JSON value @a j. @liveexample{The example shows the serialization of a JSON value to a byte vector in MessagePack format.,to_msgpack} @sa http://msgpack.org @sa @ref from_msgpack(const std::vector&, const size_t) for the analogous deserialization @sa @ref to_cbor(const basic_json& for the related CBOR format @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format @since version 2.0.9 */ static std::vector to_msgpack(const basic_json& j) { std::vector result; to_msgpack(j, result); return result; } static void to_msgpack(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_msgpack(j); } static void to_msgpack(const basic_json& j, detail::output_adapter o) { binary_writer(o).write_msgpack(j); } /*! @brief create a UBJSON serialization of a given JSON value Serializes a given JSON value @a j to a byte vector using the UBJSON (Universal Binary JSON) serialization format. UBJSON aims to be more compact than JSON itself, yet more efficient to parse. The library uses the following mapping from JSON values types to UBJSON types according to the UBJSON specification: JSON value type | value/range | UBJSON type | marker --------------- | --------------------------------- | ----------- | ------ null | `null` | null | `Z` boolean | `true` | true | `T` boolean | `false` | false | `F` number_integer | -9223372036854775808..-2147483649 | int64 | `L` number_integer | -2147483648..-32769 | int32 | `l` number_integer | -32768..-129 | int16 | `I` number_integer | -128..127 | int8 | `i` number_integer | 128..255 | uint8 | `U` number_integer | 256..32767 | int16 | `I` number_integer | 32768..2147483647 | int32 | `l` number_integer | 2147483648..9223372036854775807 | int64 | `L` number_unsigned | 0..127 | int8 | `i` number_unsigned | 128..255 | uint8 | `U` number_unsigned | 256..32767 | int16 | `I` number_unsigned | 32768..2147483647 | int32 | `l` number_unsigned | 2147483648..9223372036854775807 | int64 | `L` number_float | *any value* | float64 | `D` string | *with shortest length indicator* | string | `S` array | *see notes on optimized format* | array | `[` object | *see notes on optimized format* | map | `{` @note The mapping is **complete** in the sense that any JSON value type can be converted to a UBJSON value. @note The following values can **not** be converted to a UBJSON value: - strings with more than 9223372036854775807 bytes (theoretical) - unsigned integer numbers above 9223372036854775807 @note The following markers are not used in the conversion: - `Z`: no-op values are not created. - `C`: single-byte strings are serialized with `S` markers. @note Any UBJSON output created @ref to_ubjson can be successfully parsed by @ref from_ubjson. @note If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the @ref dump() function which serializes NaN or Infinity to `null`. @note The optimized formats for containers are supported: Parameter @a use_size adds size information to the beginning of a container and removes the closing marker. Parameter @a use_type further checks whether all elements of a container have the same type and adds the type marker to the beginning of the container. The @a use_type parameter must only be used together with @a use_size = true. Note that @a use_size = true alone may result in larger representations - the benefit of this parameter is that the receiving side is immediately informed on the number of elements of the container. @param[in] j JSON value to serialize @param[in] use_size whether to add size annotations to container types @param[in] use_type whether to add type annotations to container types (must be combined with @a use_size = true) @return UBJSON serialization as byte vector @complexity Linear in the size of the JSON value @a j. @liveexample{The example shows the serialization of a JSON value to a byte vector in UBJSON format.,to_ubjson} @sa http://ubjson.org @sa @ref from_ubjson(detail::input_adapter, const bool strict) for the analogous deserialization @sa @ref to_cbor(const basic_json& for the related CBOR format @sa @ref to_msgpack(const basic_json&) for the related MessagePack format @since version 3.1.0 */ static std::vector to_ubjson(const basic_json& j, const bool use_size = false, const bool use_type = false) { std::vector result; to_ubjson(j, result, use_size, use_type); return result; } static void to_ubjson(const basic_json& j, detail::output_adapter o, const bool use_size = false, const bool use_type = false) { binary_writer(o).write_ubjson(j, use_size, use_type); } static void to_ubjson(const basic_json& j, detail::output_adapter o, const bool use_size = false, const bool use_type = false) { binary_writer(o).write_ubjson(j, use_size, use_type); } /*! @brief create a JSON value from an input in CBOR format Deserializes a given input @a i to a JSON value using the CBOR (Concise Binary Object Representation) serialization format. The library maps CBOR types to JSON value types as follows: CBOR type | JSON value type | first byte ---------------------- | --------------- | ---------- Integer | number_unsigned | 0x00..0x17 Unsigned integer | number_unsigned | 0x18 Unsigned integer | number_unsigned | 0x19 Unsigned integer | number_unsigned | 0x1A Unsigned integer | number_unsigned | 0x1B Negative integer | number_integer | 0x20..0x37 Negative integer | number_integer | 0x38 Negative integer | number_integer | 0x39 Negative integer | number_integer | 0x3A Negative integer | number_integer | 0x3B Negative integer | number_integer | 0x40..0x57 UTF-8 string | string | 0x60..0x77 UTF-8 string | string | 0x78 UTF-8 string | string | 0x79 UTF-8 string | string | 0x7A UTF-8 string | string | 0x7B UTF-8 string | string | 0x7F array | array | 0x80..0x97 array | array | 0x98 array | array | 0x99 array | array | 0x9A array | array | 0x9B array | array | 0x9F map | object | 0xA0..0xB7 map | object | 0xB8 map | object | 0xB9 map | object | 0xBA map | object | 0xBB map | object | 0xBF False | `false` | 0xF4 True | `true` | 0xF5 Nill | `null` | 0xF6 Half-Precision Float | number_float | 0xF9 Single-Precision Float | number_float | 0xFA Double-Precision Float | number_float | 0xFB @warning The mapping is **incomplete** in the sense that not all CBOR types can be converted to a JSON value. The following CBOR types are not supported and will yield parse errors (parse_error.112): - byte strings (0x40..0x5F) - date/time (0xC0..0xC1) - bignum (0xC2..0xC3) - decimal fraction (0xC4) - bigfloat (0xC5) - tagged items (0xC6..0xD4, 0xD8..0xDB) - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) @warning CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected (parse_error.113). @note Any CBOR output created @ref to_cbor can be successfully parsed by @ref from_cbor. @param[in] i an input in CBOR format convertible to an input adapter @param[in] strict whether to expect the input to be consumed until EOF (true by default) @return deserialized JSON value @throw parse_error.110 if the given input ends prematurely or the end of file was not reached when @a strict was set to true @throw parse_error.112 if unsupported features from CBOR were used in the given input @a v or if the input is not valid CBOR @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the input @a i. @liveexample{The example shows the deserialization of a byte vector in CBOR format to a JSON value.,from_cbor} @sa http://cbor.io @sa @ref to_cbor(const basic_json&) for the analogous serialization @sa @ref from_msgpack(detail::input_adapter, const bool) for the related MessagePack format @sa @ref from_ubjson(detail::input_adapter, const bool) for the related UBJSON format @since version 2.0.9; parameter @a start_index since 2.1.1; changed to consume input adapters, removed start_index parameter, and added @a strict parameter since 3.0.0 */ static basic_json from_cbor(detail::input_adapter i, const bool strict = true) { return binary_reader(i).parse_cbor(strict); } /*! @copydoc from_cbor(detail::input_adapter, const bool) */ template::value, int> = 0> static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true) { return binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).parse_cbor(strict); } /*! @brief create a JSON value from an input in MessagePack format Deserializes a given input @a i to a JSON value using the MessagePack serialization format. The library maps MessagePack types to JSON value types as follows: MessagePack type | JSON value type | first byte ---------------- | --------------- | ---------- positive fixint | number_unsigned | 0x00..0x7F fixmap | object | 0x80..0x8F fixarray | array | 0x90..0x9F fixstr | string | 0xA0..0xBF nil | `null` | 0xC0 false | `false` | 0xC2 true | `true` | 0xC3 float 32 | number_float | 0xCA float 64 | number_float | 0xCB uint 8 | number_unsigned | 0xCC uint 16 | number_unsigned | 0xCD uint 32 | number_unsigned | 0xCE uint 64 | number_unsigned | 0xCF int 8 | number_integer | 0xD0 int 16 | number_integer | 0xD1 int 32 | number_integer | 0xD2 int 64 | number_integer | 0xD3 str 8 | string | 0xD9 str 16 | string | 0xDA str 32 | string | 0xDB array 16 | array | 0xDC array 32 | array | 0xDD map 16 | object | 0xDE map 32 | object | 0xDF negative fixint | number_integer | 0xE0-0xFF @warning The mapping is **incomplete** in the sense that not all MessagePack types can be converted to a JSON value. The following MessagePack types are not supported and will yield parse errors: - bin 8 - bin 32 (0xC4..0xC6) - ext 8 - ext 32 (0xC7..0xC9) - fixext 1 - fixext 16 (0xD4..0xD8) @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. @param[in] i an input in MessagePack format convertible to an input adapter @param[in] strict whether to expect the input to be consumed until EOF (true by default) @throw parse_error.110 if the given input ends prematurely or the end of file was not reached when @a strict was set to true @throw parse_error.112 if unsupported features from MessagePack were used in the given input @a i or if the input is not valid MessagePack @throw parse_error.113 if a string was expected as map key, but not found @complexity Linear in the size of the input @a i. @liveexample{The example shows the deserialization of a byte vector in MessagePack format to a JSON value.,from_msgpack} @sa http://msgpack.org @sa @ref to_msgpack(const basic_json&) for the analogous serialization @sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR format @sa @ref from_ubjson(detail::input_adapter, const bool) for the related UBJSON format @since version 2.0.9; parameter @a start_index since 2.1.1; changed to consume input adapters, removed start_index parameter, and added @a strict parameter since 3.0.0 */ static basic_json from_msgpack(detail::input_adapter i, const bool strict = true) { return binary_reader(i).parse_msgpack(strict); } /*! @copydoc from_msgpack(detail::input_adapter, const bool) */ template::value, int> = 0> static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true) { return binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).parse_msgpack(strict); } /*! @brief create a JSON value from an input in UBJSON format Deserializes a given input @a i to a JSON value using the UBJSON (Universal Binary JSON) serialization format. The library maps UBJSON types to JSON value types as follows: UBJSON type | JSON value type | marker ----------- | --------------------------------------- | ------ no-op | *no value, next value is read* | `N` null | `null` | `Z` false | `false` | `F` true | `true` | `T` float32 | number_float | `d` float64 | number_float | `D` uint8 | number_unsigned | `U` int8 | number_integer | `i` int16 | number_integer | `I` int32 | number_integer | `l` int64 | number_integer | `L` string | string | `S` char | string | `C` array | array (optimized values are supported) | `[` object | object (optimized values are supported) | `{` @note The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value. @param[in] i an input in UBJSON format convertible to an input adapter @param[in] strict whether to expect the input to be consumed until EOF (true by default) @throw parse_error.110 if the given input ends prematurely or the end of file was not reached when @a strict was set to true @throw parse_error.112 if a parse error occurs @throw parse_error.113 if a string could not be parsed successfully @complexity Linear in the size of the input @a i. @liveexample{The example shows the deserialization of a byte vector in UBJSON format to a JSON value.,from_ubjson} @sa http://ubjson.org @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the analogous serialization @sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR format @sa @ref from_msgpack(detail::input_adapter, const bool) for the related MessagePack format @since version 3.1.0 */ static basic_json from_ubjson(detail::input_adapter i, const bool strict = true) { return binary_reader(i).parse_ubjson(strict); } template::value, int> = 0> static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true) { return binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).parse_ubjson(strict); } /// @} ////////////////////////// // JSON Pointer support // ////////////////////////// /// @name JSON Pointer functions /// @{ /*! @brief access specified element via JSON Pointer Uses a JSON pointer to retrieve a reference to the respective JSON value. No bound checking is performed. Similar to @ref operator[](const typename object_t::key_type&), `null` values are created in arrays and objects if necessary. In particular: - If the JSON pointer points to an object key that does not exist, it is created an filled with a `null` value before a reference to it is returned. - If the JSON pointer points to an array index that does not exist, it is created an filled with a `null` value before a reference to it is returned. All indices between the current maximum and the given index are also filled with `null`. - The special value `-` is treated as a synonym for the index past the end. @param[in] ptr a JSON pointer @return reference to the element pointed to by @a ptr @complexity Constant. @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer} @since version 2.0.0 */ reference operator[](const json_pointer& ptr) { return ptr.get_unchecked(this); } /*! @brief access specified element via JSON Pointer Uses a JSON pointer to retrieve a reference to the respective JSON value. No bound checking is performed. The function does not change the JSON value; no `null` values are created. In particular, the the special value `-` yields an exception. @param[in] ptr JSON pointer to the desired element @return const reference to the element pointed to by @a ptr @complexity Constant. @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} @since version 2.0.0 */ const_reference operator[](const json_pointer& ptr) const { return ptr.get_unchecked(this); } /*! @brief access specified element via JSON Pointer Returns a reference to the element at with specified JSON pointer @a ptr, with bounds checking. @param[in] ptr JSON pointer to the desired element @return reference to the element pointed to by @a ptr @throw parse_error.106 if an array index in the passed JSON pointer @a ptr begins with '0'. See example below. @throw parse_error.109 if an array index in the passed JSON pointer @a ptr is not a number. See example below. @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr is out of range. See example below. @throw out_of_range.402 if the array index '-' is used in the passed JSON pointer @a ptr. As `at` provides checked access (and no elements are implicitly inserted), the index '-' is always invalid. See example below. @throw out_of_range.403 if the JSON pointer describes a key of an object which cannot be found. See example below. @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer} */ reference at(const json_pointer& ptr) { return ptr.get_checked(this); } /*! @brief access specified element via JSON Pointer Returns a const reference to the element at with specified JSON pointer @a ptr, with bounds checking. @param[in] ptr JSON pointer to the desired element @return reference to the element pointed to by @a ptr @throw parse_error.106 if an array index in the passed JSON pointer @a ptr begins with '0'. See example below. @throw parse_error.109 if an array index in the passed JSON pointer @a ptr is not a number. See example below. @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr is out of range. See example below. @throw out_of_range.402 if the array index '-' is used in the passed JSON pointer @a ptr. As `at` provides checked access (and no elements are implicitly inserted), the index '-' is always invalid. See example below. @throw out_of_range.403 if the JSON pointer describes a key of an object which cannot be found. See example below. @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. See example below. @exceptionsafety Strong guarantee: if an exception is thrown, there are no changes in the JSON value. @complexity Constant. @since version 2.0.0 @liveexample{The behavior is shown in the example.,at_json_pointer_const} */ const_reference at(const json_pointer& ptr) const { return ptr.get_checked(this); } /*! @brief return flattened JSON value The function creates a JSON object whose keys are JSON pointers (see [RFC 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all primitive. The original JSON value can be restored using the @ref unflatten() function. @return an object that maps JSON pointers to primitive values @note Empty objects and arrays are flattened to `null` and will not be reconstructed correctly by the @ref unflatten() function. @complexity Linear in the size the JSON value. @liveexample{The following code shows how a JSON object is flattened to an object whose keys consist of JSON pointers.,flatten} @sa @ref unflatten() for the reverse function @since version 2.0.0 */ basic_json flatten() const { basic_json result(value_t::object); json_pointer::flatten("", *this, result); return result; } /*! @brief unflatten a previously flattened JSON value The function restores the arbitrary nesting of a JSON value that has been flattened before using the @ref flatten() function. The JSON value must meet certain constraints: 1. The value must be an object. 2. The keys must be JSON pointers (see [RFC 6901](https://tools.ietf.org/html/rfc6901)) 3. The mapped values must be primitive JSON types. @return the original JSON from a flattened version @note Empty objects and arrays are flattened by @ref flatten() to `null` values and can not unflattened to their original type. Apart from this example, for a JSON value `j`, the following is always true: `j == j.flatten().unflatten()`. @complexity Linear in the size the JSON value. @throw type_error.314 if value is not an object @throw type_error.315 if object values are not primitive @liveexample{The following code shows how a flattened JSON object is unflattened into the original nested JSON object.,unflatten} @sa @ref flatten() for the reverse function @since version 2.0.0 */ basic_json unflatten() const { return json_pointer::unflatten(*this); } /// @} ////////////////////////// // JSON Patch functions // ////////////////////////// /// @name JSON Patch functions /// @{ /*! @brief applies a JSON patch [JSON Patch](http://jsonpatch.com) defines a JSON document structure for expressing a sequence of operations to apply to a JSON) document. With this function, a JSON Patch is applied to the current JSON value by executing all operations from the patch. @param[in] json_patch JSON patch document @return patched document @note The application of a patch is atomic: Either all operations succeed and the patched document is returned or an exception is thrown. In any case, the original value is not changed: the patch is applied to a copy of the value. @throw parse_error.104 if the JSON patch does not consist of an array of objects @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory attributes are missing); example: `"operation add must have member path"` @throw out_of_range.401 if an array index is out of range. @throw out_of_range.403 if a JSON pointer inside the patch could not be resolved successfully in the current JSON value; example: `"key baz not found"` @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", "move") @throw other_error.501 if "test" operation was unsuccessful @complexity Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is affected by the patch, the complexity can usually be neglected. @liveexample{The following code shows how a JSON patch is applied to a value.,patch} @sa @ref diff -- create a JSON patch by comparing two JSON values @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) @since version 2.0.0 */ basic_json patch(const basic_json& json_patch) const { // make a working copy to apply the patch to basic_json result = *this; // the valid JSON Patch operations enum class patch_operations {add, remove, replace, move, copy, test, invalid}; const auto get_op = [](const std::string & op) { if (op == "add") { return patch_operations::add; } if (op == "remove") { return patch_operations::remove; } if (op == "replace") { return patch_operations::replace; } if (op == "move") { return patch_operations::move; } if (op == "copy") { return patch_operations::copy; } if (op == "test") { return patch_operations::test; } return patch_operations::invalid; }; // wrapper for "add" operation; add value at ptr const auto operation_add = [&result](json_pointer & ptr, basic_json val) { // adding to the root of the target document means replacing it if (ptr.is_root()) { result = val; } else { // make sure the top element of the pointer exists json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { result.at(top_pointer); } // get reference to parent of JSON pointer ptr const auto last_path = ptr.pop_back(); basic_json& parent = result[ptr]; switch (parent.m_type) { case value_t::null: case value_t::object: { // use operator[] to add value parent[last_path] = val; break; } case value_t::array: { if (last_path == "-") { // special case: append to back parent.push_back(val); } else { const auto idx = json_pointer::array_index(last_path); if (JSON_UNLIKELY(static_cast(idx) > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } else { // default case: insert add offset parent.insert(parent.begin() + static_cast(idx), val); } } break; } default: { // if there exists a parent it cannot be primitive assert(false); // LCOV_EXCL_LINE } } } }; // wrapper for "remove" operation; remove value at ptr const auto operation_remove = [&result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr const auto last_path = ptr.pop_back(); basic_json& parent = result.at(ptr); // remove child if (parent.is_object()) { // perform range check auto it = parent.find(last_path); if (JSON_LIKELY(it != parent.end())) { parent.erase(it); } else { JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); } } else if (parent.is_array()) { // note erase performs range check parent.erase(static_cast(json_pointer::array_index(last_path))); } }; // type check: top level value must be an array if (JSON_UNLIKELY(not json_patch.is_array())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // iterate and apply the operations for (const auto& val : json_patch) { // wrapper to get a value for an operation const auto get_value = [&val](const std::string & op, const std::string & member, bool string_type) -> basic_json & { // find value auto it = val.m_value.object->find(member); // context-sensitive error message const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; // check if desired value is present if (JSON_UNLIKELY(it == val.m_value.object->end())) { JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); } // check if result is of type string if (JSON_UNLIKELY(string_type and not it->second.is_string())) { JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); } // no error: return value return it->second; }; // type check: every element of the array must be an object if (JSON_UNLIKELY(not val.is_object())) { JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } // collect mandatory members const std::string op = get_value("op", "op", true); const std::string path = get_value(op, "path", true); json_pointer ptr(path); switch (get_op(op)) { case patch_operations::add: { operation_add(ptr, get_value("add", "value", false)); break; } case patch_operations::remove: { operation_remove(ptr); break; } case patch_operations::replace: { // the "path" location must exist - use at() result.at(ptr) = get_value("replace", "value", false); break; } case patch_operations::move: { const std::string from_path = get_value("move", "from", true); json_pointer from_ptr(from_path); // the "from" location must exist - use at() basic_json v = result.at(from_ptr); // The move operation is functionally identical to a // "remove" operation on the "from" location, followed // immediately by an "add" operation at the target // location with the value that was just removed. operation_remove(from_ptr); operation_add(ptr, v); break; } case patch_operations::copy: { const std::string from_path = get_value("copy", "from", true); const json_pointer from_ptr(from_path); // the "from" location must exist - use at() basic_json v = result.at(from_ptr); // The copy is functionally identical to an "add" // operation at the target location using the value // specified in the "from" member. operation_add(ptr, v); break; } case patch_operations::test: { bool success = false; JSON_TRY { // check if "value" matches the one at "path" // the "path" location must exist - use at() success = (result.at(ptr) == get_value("test", "value", false)); } JSON_CATCH (out_of_range&) { // ignore out of range errors: success remains false } // throw an exception if test fails if (JSON_UNLIKELY(not success)) { JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); } break; } case patch_operations::invalid: { // op must be "add", "remove", "replace", "move", "copy", or // "test" JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); } } } return result; } /*! @brief creates a diff as a JSON patch Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can be changed into the value @a target by calling @ref patch function. @invariant For two JSON values @a source and @a target, the following code yields always `true`: @code {.cpp} source.patch(diff(source, target)) == target; @endcode @note Currently, only `remove`, `add`, and `replace` operations are generated. @param[in] source JSON value to compare from @param[in] target JSON value to compare against @param[in] path helper value to create JSON pointers @return a JSON patch to convert the @a source to @a target @complexity Linear in the lengths of @a source and @a target. @liveexample{The following code shows how a JSON patch is created as a diff for two JSON values.,diff} @sa @ref patch -- apply a JSON patch @sa @ref merge_patch -- apply a JSON Merge Patch @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) @since version 2.0.0 */ static basic_json diff(const basic_json& source, const basic_json& target, const std::string& path = "") { // the patch basic_json result(value_t::array); // if the values are the same, return empty patch if (source == target) { return result; } if (source.type() != target.type()) { // different types: replace value result.push_back( { {"op", "replace"}, {"path", path}, {"value", target} }); } else { switch (source.type()) { case value_t::array: { // first pass: traverse common elements std::size_t i = 0; while (i < source.size() and i < target.size()) { // recursive call to compare array values at index i auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); ++i; } // i now reached the end of at least one array // in a second pass, traverse the remaining elements // remove my remaining elements const auto end_index = static_cast(result.size()); while (i < source.size()) { // add operations in reverse order to avoid invalid // indices result.insert(result.begin() + end_index, object( { {"op", "remove"}, {"path", path + "/" + std::to_string(i)} })); ++i; } // add other remaining elements while (i < target.size()) { result.push_back( { {"op", "add"}, {"path", path + "/" + std::to_string(i)}, {"value", target[i]} }); ++i; } break; } case value_t::object: { // first pass: traverse this object's elements for (auto it = source.cbegin(); it != source.cend(); ++it) { // escape the key name to be used in a JSON patch const auto key = json_pointer::escape(it.key()); if (target.find(it.key()) != target.end()) { // recursive call to compare object values at key it auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); result.insert(result.end(), temp_diff.begin(), temp_diff.end()); } else { // found a key that is not in o -> remove it result.push_back(object( { {"op", "remove"}, {"path", path + "/" + key} })); } } // second pass: traverse other object's elements for (auto it = target.cbegin(); it != target.cend(); ++it) { if (source.find(it.key()) == source.end()) { // found a key that is not in this -> add it const auto key = json_pointer::escape(it.key()); result.push_back( { {"op", "add"}, {"path", path + "/" + key}, {"value", it.value()} }); } } break; } default: { // both primitive type: replace value result.push_back( { {"op", "replace"}, {"path", path}, {"value", target} }); break; } } } return result; } /// @} //////////////////////////////// // JSON Merge Patch functions // //////////////////////////////// /// @name JSON Merge Patch functions /// @{ /*! @brief applies a JSON Merge Patch The merge patch format is primarily intended for use with the HTTP PATCH method as a means of describing a set of modifications to a target resource's content. This function applies a merge patch to the current JSON value. The function implements the following algorithm from Section 2 of [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): ``` define MergePatch(Target, Patch): if Patch is an Object: if Target is not an Object: Target = {} // Ignore the contents and set it to an empty Object for each Name/Value pair in Patch: if Value is null: if Name exists in Target: remove the Name/Value pair from Target else: Target[Name] = MergePatch(Target[Name], Value) return Target else: return Patch ``` Thereby, `Target` is the current object; that is, the patch is applied to the current value. @param[in] patch the patch to apply @complexity Linear in the lengths of @a patch. @liveexample{The following code shows how a JSON Merge Patch is applied to a JSON document.,merge_patch} @sa @ref patch -- apply a JSON patch @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) @since version 3.0.0 */ void merge_patch(const basic_json& patch) { if (patch.is_object()) { if (not is_object()) { *this = object(); } for (auto it = patch.begin(); it != patch.end(); ++it) { if (it.value().is_null()) { erase(it.key()); } else { operator[](it.key()).merge_patch(it.value()); } } } else { *this = patch; } } /// @} }; } // namespace nlohmann /////////////////////// // nonmember support // /////////////////////// // specialization of std::swap, and std::hash namespace std { /*! @brief exchanges the values of two JSON objects @since version 1.0.0 */ template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and is_nothrow_move_assignable::value ) { j1.swap(j2); } /// hash value for JSON objects template<> struct hash { /*! @brief return a hash value for a JSON object @since version 1.0.0 */ std::size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation const auto& h = hash(); return h(j.dump()); } }; /// specialization for std::less /// @note: do not remove the space after '<', /// see https://github.com/nlohmann/json/pull/679 template<> struct less< ::nlohmann::detail::value_t> { /*! @brief compare two value_t enum values @since version 3.0.0 */ bool operator()(nlohmann::detail::value_t lhs, nlohmann::detail::value_t rhs) const noexcept { return nlohmann::detail::operator<(lhs, rhs); } }; } // namespace std /*! @brief user-defined string literal for JSON values This operator implements a user-defined string literal for JSON objects. It can be used by adding `"_json"` to a string literal and returns a JSON object if no parse error occurred. @param[in] s a string representation of a JSON object @param[in] n the length of string @a s @return a JSON object @since version 1.0.0 */ inline nlohmann::json operator "" _json(const char* s, std::size_t n) { return nlohmann::json::parse(s, s + n); } /*! @brief user-defined string literal for JSON pointer This operator implements a user-defined string literal for JSON Pointers. It can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer object if no parse error occurred. @param[in] s a string representation of a JSON Pointer @param[in] n the length of string @a s @return a JSON pointer object @since version 2.0.0 */ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { return nlohmann::json::json_pointer(std::string(s, n)); } // #include // restore GCC/clang diagnostic settings #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop #endif #if defined(__clang__) #pragma GCC diagnostic pop #endif // clean up #undef JSON_CATCH #undef JSON_THROW #undef JSON_TRY #undef JSON_LIKELY #undef JSON_UNLIKELY #undef JSON_DEPRECATED #undef JSON_HAS_CPP_14 #undef JSON_HAS_CPP_17 #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL #undef NLOHMANN_JSON_HAS_HELPER #endif pvpgn-1.99.7.2.1+dfsg/src/test/000077500000000000000000000000001462640461400157465ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/test/CMakeLists.txt000066400000000000000000000003441462640461400205070ustar00rootroot00000000000000add_executable(bnetsrp3_test bnetsrp3_test.cpp ) target_link_libraries(bnetsrp3_test common) ADD_TEST(bnetsrp3_test bnetsrp3_test) add_executable(bigint bigint.cpp ) target_link_libraries(bigint common) ADD_TEST(bigint bigint) pvpgn-1.99.7.2.1+dfsg/src/test/bigint.cpp000066400000000000000000000127751462640461400177420ustar00rootroot00000000000000/* * Copyright (C) 1999,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "common/bigint.h" #include #include #include #include #include "common/xalloc.h" #include "common/setup_after.h" using namespace pvpgn; const unsigned char data1[] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef }; const unsigned char data2[] = { 0x12, 0xff, 0x34, 0xff, 0x56, 0xff, 0x78, 0xff, 0x90, 0xff, 0xab, 0xff, 0xcd, 0xff, 0xef, 0xff }; const unsigned char data3[] = { 0x12, 0x34, 0x56, 0x78 }; const unsigned char data4[] = { 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef }; void constructorTests() { // std::cout << __FUNCTION__ << "\n"; BigInt* number; number = new BigInt(); assert(number->toHexString() == "00"); delete number; number = new BigInt((std::uint8_t)0xFF); assert(number->toHexString() == "ff"); delete number; number = new BigInt((std::uint16_t)0xFFFF); assert(number->toHexString() == "ffff"); delete number; number = new BigInt((std::uint32_t)0xFFFFFFFF); assert(number->toHexString() == "ffffffff"); delete number; number = new BigInt(data1, 8); assert(number->toHexString() == "1234567890abcdef"); delete number; number = new BigInt(data1, 7); assert(number->toHexString() == "1234567890abcd"); delete number; number = new BigInt(data2, 16); assert(number->toHexString() == "12ff34ff56ff78ff90ffabffcdffefff"); delete number; } void getDataTests() { int i; unsigned char* data; data = BigInt(data4, 16).getData(16); for (i = 0; i < 16; i++) { assert(data[i] = data4[i]); } xfree(data); data = BigInt((std::uint32_t)0x12345678).getData(3); assert(BigInt(data, 3) == BigInt((std::uint32_t)0x345678)); xfree(data); data = BigInt((std::uint32_t)0x12345678).getData(2); assert(BigInt(data, 2) == BigInt((std::uint16_t)0x5678)); xfree(data); data = BigInt((std::uint32_t)0x12345678).getData(1); assert(BigInt(data, 1) == BigInt((std::uint8_t)0x78)); xfree(data); } void compareOperatorsTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt((std::uint32_t)0x12345678) == BigInt(data3, 4)); assert(BigInt((std::uint16_t)0x27a2) < BigInt((std::uint16_t)0x9876)); assert(BigInt() < BigInt((std::uint16_t)0x9876)); assert(BigInt((std::uint16_t)0x9876) > BigInt()); } void addTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt((std::uint32_t)0x12344321) + BigInt((std::uint32_t)0x43211234) == BigInt((std::uint32_t)0x55555555)); assert(BigInt((std::uint16_t)0xFFFF) + BigInt((std::uint8_t)0x01) == BigInt((std::uint32_t)0x00010000)); } void subTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt((std::uint32_t)0x00010000) - BigInt((std::uint8_t)0x01) == BigInt((std::uint16_t)0xFFFF)); assert(BigInt((std::uint32_t)0x12345678) - BigInt((std::uint16_t)0x9876) == BigInt((std::uint32_t)0x1233BE02)); assert(BigInt((std::uint8_t)0x10) - BigInt((std::uint8_t)0xFF) == BigInt((std::uint8_t)0x00)); } void mulTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt((std::uint8_t)0x02) * BigInt((std::uint8_t)0xff) == BigInt((std::uint16_t)0x01fe)); const unsigned char data4[] = { 0x01, 0x4b, 0x66, 0xdc, 0x1d, 0xf4, 0xd8, 0x40 }; assert(BigInt(data3, 4) * BigInt(data3, 4) == BigInt(data4, 8)); } void divTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt((std::uint16_t)0x9876) / BigInt((std::uint32_t)0x98765432) == BigInt()); assert(BigInt((std::uint32_t)0x1e0f7fbc) / BigInt((std::uint16_t)0x1e2f) == BigInt((std::uint16_t)0xfef4)); assert(BigInt((std::uint32_t)0x01000000) / BigInt((std::uint32_t)0x00FFFFFF) == BigInt((std::uint8_t)0x01)); assert(BigInt(data4, 16) / BigInt((std::uint8_t)0x02) > BigInt()); } void modTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt((std::uint32_t)0x1e0f7fbc) % BigInt((std::uint16_t)0x1e2f) == BigInt((std::uint16_t)0x18f0)); assert(BigInt((std::uint32_t)0x01000000) % BigInt((std::uint32_t)0x00FFFFFF) == BigInt((std::uint8_t)0x01)); assert(BigInt((std::uint32_t)0x80000000) % BigInt((std::uint32_t)0xFFFFFFFF) == BigInt((std::uint32_t)0x80000000)); } void randTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt::random(8) > BigInt()); assert(BigInt::random(16) > BigInt()); assert(BigInt::random(32) > BigInt()); assert(BigInt::random(64) > BigInt()); assert(BigInt::random(128) > BigInt()); } void powmTests() { // std::cout << __FUNCTION__ << "\n"; assert(BigInt((std::uint8_t)0x02).powm(BigInt((std::uint8_t)0x1F), BigInt((std::uint32_t)0xFFFFFFFF)) == BigInt((std::uint32_t)0x80000000)); BigInt mod = BigInt::random(32); assert(BigInt((std::uint8_t)0x2f).powm(BigInt::random(32), mod) < mod); } int main() { constructorTests(); getDataTests(); compareOperatorsTests(); addTests(); subTests(); mulTests(); divTests(); modTests(); randTests(); powmTests(); return 0; } pvpgn-1.99.7.2.1+dfsg/src/test/bnetsrp3_test.cpp000066400000000000000000000051261462640461400212550ustar00rootroot00000000000000/* * Copyright (C) 1999,2001 Ross Combs (rocombs@cs.nmsu.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include #include #include "common/xalloc.h" #include "common/util.h" #include "common/bnetsrp3.h" #include "common/setup_after.h" using namespace pvpgn; int main() { const unsigned char salt[] = { 0xB3, 0x46, 0x25, 0x10, 0x1D, 0xEA, 0x80, 0xB9, 0x92, 0xEB, 0x50, 0x4E, 0x84, 0x00, 0x06, 0xA1, 0x7E, 0x77, 0x58, 0x66, 0x73, 0x89, 0x27, 0xF7, 0x14, 0x90, 0x2D, 0xA6, 0x3F, 0xCC, 0xF8, 0x52 }; BnetSRP3 nls1("regen", "bogen"); nls1.setSalt(BigInt(salt, 32)); BigInt v = nls1.getVerifier(); BigInt s = nls1.getSalt(); BnetSRP3 nls2("power", s); //output= (char *)xmalloc(40*3+1); //s.getData(data,32); //str_to_hex(output,(const char*)data,32); //std::printf("salt: %s\n",output); //v.getData(data,32,4,false); //str_to_hex(output,(const char*)data,32); //std::printf("verifier: %s\n",output); BigInt A = nls1.getClientSessionPublicKey(); //A.getData(data,32,4,false); //str_to_hex(output,(const char*)data,32); //std::printf("A: %s\n",output); BigInt B = nls2.getServerSessionPublicKey(v); //B.getData(data,32,4,false); //str_to_hex(output,(const char*)data,32); //std::printf("B: %s\n",output); BigInt K1 = nls1.getHashedClientSecret(B); //K1.getData(data,40,4,false); //str_to_hex(output,(const char*)data,40); //std::printf("K1: %s\n",output); BigInt K2 = nls2.getHashedServerSecret(A, v); //K2.getData(data,40,4,false); //str_to_hex(output,(const char*)data,40); //std::printf("K2: %s\n",output); assert(K1 == K2); //BigInt M1 = nls2.getClientPasswordProof(A, B, K1); //M1.getData(data,20,4,false); //str_to_hex(output,(const char*)data,20); //std::printf("M1: %s\n",output); //BigInt M2 = nls1.getServerPasswordProof(A, M1, K2); //M2.getData(data,20,4,false); //str_to_hex(output,(const char*)data,20); //std::printf("M2: %s\n",output); return 0; } pvpgn-1.99.7.2.1+dfsg/src/win32/000077500000000000000000000000001462640461400157315ustar00rootroot00000000000000pvpgn-1.99.7.2.1+dfsg/src/win32/CMakeLists.txt000066400000000000000000000002241462640461400204670ustar00rootroot00000000000000set(WIN32_SOURCES service.cpp service.h console_output.h console_output.cpp dirent.h windump.cpp windump.h ) add_library(win32 ${WIN32_SOURCES}) pvpgn-1.99.7.2.1+dfsg/src/win32/console_output.cpp000066400000000000000000000055751462640461400215330ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2004 by Richard Moore (rich@kde.org * * Copyright (C) 2007 by Olaf Freyer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "common/setup_before.h" #ifdef WIN32 #include "console_output.h" #include #include #include #include #include #include "common/setup_after.h" namespace pvpgn { Console::Console(){ initialised = false; } Console::~Console() throw () { if (initialised) FreeConsole(); } void Console::RedirectIOToConsole() { const WORD MAX_CONSOLE_LINES = 500; const WORD CONSOLE_COLUMNS = 140; int hConHandle; long lStdHandle; std::FILE *fp; CONSOLE_SCREEN_BUFFER_INFO coninfo; if (initialised) return; AllocConsole(); GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); coninfo.dwSize.X = CONSOLE_COLUMNS; coninfo.dwSize.Y = MAX_CONSOLE_LINES; SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); fp = _fdopen(hConHandle, "r"); *stdin = *fp; setvbuf(stdin, NULL, _IONBF, 0);; lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); fp = _fdopen(hConHandle, "w"); *stdout = *fp; setvbuf(stdout, NULL, _IONBF, 0); lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); fp = _fdopen(hConHandle, "w"); *stderr = *fp; setvbuf(stderr, NULL, _IONBF, 0); std::ios::sync_with_stdio(); initialised = true; } } #endif pvpgn-1.99.7.2.1+dfsg/src/win32/console_output.h000066400000000000000000000031371462640461400211700ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2007 by Olaf Freyer * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifdef WIN32 #ifndef CONSOLE_OUTPUT_INCLUDED #define CONSOLE_OUTPUT_INCLUDED namespace pvpgn { class Console { public: Console(); ~Console() throw (); void RedirectIOToConsole(); private: bool initialised; }; } #endif #endif pvpgn-1.99.7.2.1+dfsg/src/win32/console_resource.h000066400000000000000000000000551462640461400214530ustar00rootroot00000000000000#define IDI_ICON1 101 pvpgn-1.99.7.2.1+dfsg/src/win32/console_resource.rc000066400000000000000000000003011462640461400216220ustar00rootroot00000000000000#include "console_resource.h" #define APSTUDIO_READONLY_SYMBOLS #ifndef __BORLANDC__ #include "afxres.h" #endif /* Icon */ ID_ICON1 ICON DISCARDABLE "win32/logo01.ico" pvpgn-1.99.7.2.1+dfsg/src/win32/d2cs_resource.h000066400000000000000000000006201462640461400206420ustar00rootroot00000000000000#define ID_TRAY 4 #define ID_RESTORE 5 #define ID_EXIT 6 #define ID_ABOUT_BOX 101 #define ID_ICON1 102 #define ID_MENU 103 #define ID_START_D2CS 40001 #define ID_SHUTDOWN_D2CS 40002 #define ID_RESTART_D2CS 40003 #define ID_EDITCONFIG_D2CS 40004 #define ID_LOADCONFIG_D2CS 40005 #define ID_LADDER_LOAD 40006 #define ID_RESTART_D2GS 40007 #define ID_CLEAR 40008 #define ID_ABOUT 40009 pvpgn-1.99.7.2.1+dfsg/src/win32/d2cs_resource.rc000066400000000000000000000033021462640461400210170ustar00rootroot00000000000000#include "d2cs_resource.h" #include "d2cs/version.h" #define APSTUDIO_READONLY_SYMBOLS #ifndef __BORLANDC__ #ifdef __AFXRES_H__ #include "afxres.h" #else #include "winres.h" #endif //__AFXRES_H__ #endif /* Menu */ ID_MENU MENU DISCARDABLE BEGIN MENUITEM "&Exit", ID_EXIT MENUITEM "&Start", ID_START_D2CS MENUITEM "Shut&down", ID_SHUTDOWN_D2CS MENUITEM "&Restart", ID_RESTART_D2CS MENUITEM "&Edit Configuration", ID_EDITCONFIG_D2CS MENUITEM "Reload &Configuration", ID_LOADCONFIG_D2CS MENUITEM "Reload &Ladder", ID_LADDER_LOAD MENUITEM "Restart d2gs", ID_RESTART_D2GS MENUITEM "&Clear Window", ID_CLEAR MENUITEM "&About", ID_ABOUT END /* About Dialog Box */ ID_ABOUT_BOX DIALOGEX 120, 150, 240, 150 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About This Server" FONT 8, "MS Sans Serif" BEGIN GROUPBOX "",IDC_STATIC,5,5,230,140 CTEXT "D2CS Version",IDC_STATIC,15,15,210,15 CTEXT D2CS_VERSION_STRING,IDC_STATIC,15,25,210,15 CTEXT "coded by the PvPGN Team",IDC_STATIC,15,40,210,15 CTEXT "- - - - - - - - - - - - - - -",IDC_STATIC,15,55,210,15 CTEXT "visit us @ www.pvpgn.pro or",IDC_STATIC,15,70,210,15 CTEXT "irc.pvpgn.org #pvpgn",IDC_STATIC,15,80,210,15 CTEXT "©2002 ©2003 ©2004 PvPGN Is a Open Source Project",IDC_STATIC,15,100,210,15 CTEXT "As specified by the GPL",IDC_STATIC,15,110,210,15 DEFPUSHBUTTON "&OK",IDOK,95,125,50,14 END /* Icon */ ID_ICON1 ICON DISCARDABLE "win32/logo01.ico" pvpgn-1.99.7.2.1+dfsg/src/win32/d2cs_winmain.cpp000066400000000000000000000201121462640461400210060ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32_GUI #include "common/setup_before.h" #include #include #include #include #include #include "d2cs_resource.h" #include "common/gui_printf.h" #include "common/eventlog.h" #include "d2cs/version.h" #include "d2cs/handle_signal.h" #include "d2cs/cmdline.h" #include "service.h" #include "winmain.h" #include "console_output.h" #include "common/setup_after.h" #define WM_SHELLNOTIFY (WM_USER+1) extern int app_main(int argc, char **argv); /* d2cs main function in d2cs/main.c */ namespace pvpgn { extern HWND ghwndConsole; /* hwnd for eventlog output */ namespace d2cs { static void KillTrayIcon(HWND hwnd); static int OnShellNotify(HWND hwnd, int uID, int uMessage); static void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct); static void OnClose(HWND hwnd); static void OnSize(HWND hwnd, UINT state, int cx, int cy); LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL CALLBACK DlgProcAbout(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static int gui_run = TRUE; /* default state: run gui */ static int d2cs_run = TRUE; /* default state: run d2cs */ static int d2cs_running = FALSE; /* currect state: not running */ int fprintf(FILE *stream, const char *format, ...) { int temp = 0; va_list args; va_start(args, format); if (stream == stderr || stream == stdout) { char buf[1024] = {}; std::vsnprintf(buf, sizeof buf, format, args); gui_lvprintf(eventlog_level_error, "{}", buf); temp = 1; } else temp = vfprintf(stream, format, args); va_end(args); return temp; } static void KillTrayIcon(HWND hwnd) { NOTIFYICONDATA dta; dta.cbSize = sizeof(NOTIFYICONDATA); dta.hWnd = hwnd; dta.uID = ID_TRAY; dta.uFlags = 0; Shell_NotifyIcon(NIM_DELETE, &dta); } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_CREATE, OnCreate); HANDLE_MSG(hwnd, WM_SIZE, OnSize); HANDLE_MSG(hwnd, WM_COMMAND, OnCommand); HANDLE_MSG(hwnd, WM_CLOSE, OnClose); case WM_SHELLNOTIFY: return OnShellNotify(hwnd, wParam, lParam); } return DefWindowProc(hwnd, uMsg, wParam, lParam); } static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) { ghwndConsole = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_MULTILINE | WS_VSCROLL | WS_HSCROLL | ES_NOHIDESEL, 0, 0, 0, 0, hwnd, 0, 0, NULL); if (!ghwndConsole) return FALSE; return TRUE; } static void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case ID_RESTORE: OnShellNotify(hwnd, ID_TRAY, WM_LBUTTONDBLCLK); break; case ID_START_D2CS: d2cs::fprintf(stderr, "Sending Start Signal to d2cs\n"); d2cs_run = TRUE; break; case ID_SHUTDOWN_D2CS: d2cs::fprintf(stderr, "Sending Shutdown Signal to d2cs\n"); d2cs_run = FALSE; signal_quit_wrapper(); break; case ID_RESTART_D2CS: d2cs::fprintf(stderr, "Sending Restart Signal To d2cs\n"); d2cs_run = TRUE; signal_quit_wrapper(); break; case ID_EDITCONFIG_D2CS: ShellExecuteW(NULL, L"open", L"notepad.exe", L"conf\\d2cs.conf", NULL, SW_SHOW); break; case ID_LOADCONFIG_D2CS: d2cs::fprintf(stderr, "Sending Reload Config Signal To d2cs\n"); signal_reload_config_wrapper(); break; case ID_LADDER_LOAD: d2cs::fprintf(stderr, "Sending Reload Ladder Signal To d2cs\n"); signal_load_ladder_wrapper(); break; case ID_RESTART_D2GS: d2cs::fprintf(stderr, "Sending Restart d2gs Signal To d2cs\n"); signal_restart_d2gs_wrapper(); break; case ID_EXIT: OnClose(hwnd); break; case ID_CLEAR: SendMessage(ghwndConsole, WM_SETTEXT, 0, 0); break; case ID_ABOUT: DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ABOUT_BOX), hwnd, (DLGPROC)DlgProcAbout); break; } } static int OnShellNotify(HWND hwnd, int uID, int uMessage) { if (uID == ID_TRAY) { if (uMessage == WM_LBUTTONDBLCLK) { if (!IsWindowVisible(hwnd)) ShowWindow(hwnd, SW_RESTORE); SetForegroundWindow(hwnd); } } return 0; } static void OnClose(HWND hwnd) { d2cs::fprintf(stderr, "Sending Exit Signal To d2cs\n"); gui_run = FALSE; d2cs_run = FALSE; signal_exit_wrapper(); } static void OnSize(HWND hwnd, UINT state, int cx, int cy) { if (state == SIZE_MINIMIZED) { NOTIFYICONDATAW dta = {}; dta.cbSize = sizeof(NOTIFYICONDATAW); dta.hWnd = hwnd; dta.uID = ID_TRAY; dta.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; dta.uCallbackMessage = WM_SHELLNOTIFY; dta.hIcon = LoadIconW(GetWindowInstance(hwnd), MAKEINTRESOURCEW(ID_ICON1)); std::swprintf(dta.szTip, sizeof dta.szTip / sizeof *dta.szTip, L"D2CS Version " D2CS_VERSION_STRING); Shell_NotifyIconW(NIM_ADD, &dta); ShowWindow(hwnd, SW_HIDE); return; } MoveWindow(ghwndConsole, 0, 0, cx, cy, TRUE); } BOOL CALLBACK DlgProcAbout(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: EndDialog(hwnd, TRUE); return TRUE; case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch ((int)wParam) { case IDOK: EndDialog(hwnd, TRUE); return TRUE; } } return FALSE; } #define EXIT_ERROR -1 #define EXIT_OK 0 #define EXIT_SERVICE 1 static void d2cs(void * dummy) { switch (app_main(__argc, __argv)) { case EXIT_SERVICE: gui_run = FALSE; /* close gui */ case EXIT_ERROR: d2cs_run = FALSE; /* don't restart */ case EXIT_OK: ; /* do nothing */ } d2cs::fprintf(stderr, "Server Stopped\n"); d2cs_running = FALSE; } } } using namespace pvpgn; using namespace pvpgn::d2cs; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; Console console; if (cmdline_load(__argc, __argv) != 1) { return -1; } if (cmdline_get_console()){ console.RedirectIOToConsole(); return app_main(__argc, __argv); } if (LoadLibraryW(L"RichEd20.dll") == NULL) { return -1; } WNDCLASSEXW wc = {}; wc.cbSize = sizeof(WNDCLASSEXW); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(LPVOID); wc.hInstance = hInstance; wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(ID_ICON1)); wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = MAKEINTRESOURCEW(ID_MENU); wc.lpszClassName = L"BnetWndClass"; if (!RegisterClassExW(&wc)) RegisterClassW((LPWNDCLASS)&wc.style); hwnd = CreateWindowExW(0L, L"BnetWndClass", L"Diablo II Character Server", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, LoadMenuW(hInstance, MAKEINTRESOURCEW(ID_MENU)), hInstance, NULL); if (hwnd) { ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); } while (GetMessageW(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); if (!d2cs_running && d2cs_run && gui_run) { d2cs_running = TRUE; _beginthread(pvpgn::d2cs::d2cs, 0, NULL); } if (!gui_run && !d2cs_running) { KillTrayIcon(hwnd); exit(0); } } return ((int)msg.wParam); } #endif pvpgn-1.99.7.2.1+dfsg/src/win32/d2dbs_resource.h000066400000000000000000000005651462640461400210150ustar00rootroot00000000000000#define ID_TRAY 4 #define ID_RESTORE 5 #define ID_EXIT 6 #define ID_ABOUT_BOX 101 #define ID_ICON1 102 #define ID_MENU 103 #define ID_START_D2DBS 40001 #define ID_SHUTDOWN_D2DBS 40002 #define ID_RESTART_D2DBS 40003 #define ID_EDITCONFIG_D2DBS 40004 #define ID_LOADCONFIG_D2DBS 40005 #define ID_LADDER_SAVE 40006 #define ID_CLEAR 40007 #define ID_ABOUT 40008 pvpgn-1.99.7.2.1+dfsg/src/win32/d2dbs_resource.rc000066400000000000000000000032441462640461400211670ustar00rootroot00000000000000#include "d2dbs_resource.h" #include "d2dbs/version.h" #define APSTUDIO_READONLY_SYMBOLS #ifndef __BORLANDC__ #ifdef __AFXRES_H__ #include "afxres.h" #else #include "winres.h" #endif //__AFXRES_H__ #endif //__BORLANDC__ /* Menu */ ID_MENU MENU DISCARDABLE BEGIN MENUITEM "&Exit", ID_EXIT MENUITEM "&Start", ID_START_D2DBS MENUITEM "Shut&down", ID_SHUTDOWN_D2DBS MENUITEM "&Restart", ID_RESTART_D2DBS MENUITEM "&Edit Configuration", ID_EDITCONFIG_D2DBS MENUITEM "Reload &Configuration", ID_LOADCONFIG_D2DBS MENUITEM "Save &Ladder", ID_LADDER_SAVE MENUITEM "&Clear Window", ID_CLEAR MENUITEM "&About", ID_ABOUT END /* About Dialog Box */ ID_ABOUT_BOX DIALOGEX 120, 150, 240, 150 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About This Server" FONT 8, "MS Sans Serif" BEGIN GROUPBOX "",IDC_STATIC,5,5,230,140 CTEXT "D2DBS Version",IDC_STATIC,15,15,210,15 CTEXT D2DBS_VERSION_NUMBER,IDC_STATIC,15,25,210,15 CTEXT "coded by the PvPGN Team",IDC_STATIC,15,40,210,15 CTEXT "- - - - - - - - - - - - - - -",IDC_STATIC,15,55,210,15 CTEXT "visit us @ www.pvpgn.pro or",IDC_STATIC,15,70,210,15 CTEXT "irc.pvpgn.org #pvpgn",IDC_STATIC,15,80,210,15 CTEXT "©2002 ©2003 ©2004 PvPGN Is a Open Source Project",IDC_STATIC,15,100,210,15 CTEXT "As specified by the GPL",IDC_STATIC,15,110,210,15 DEFPUSHBUTTON "&OK",IDOK,95,125,50,14 END /* Icon */ ID_ICON1 ICON DISCARDABLE "win32/logo01.ico" pvpgn-1.99.7.2.1+dfsg/src/win32/d2dbs_winmain.cpp000066400000000000000000000200151462640461400211530ustar00rootroot00000000000000/* * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32_GUI #include "common/setup_before.h" #include #include #include #include #include #include "d2dbs_resource.h" #include "common/gui_printf.h" #include "common/eventlog.h" #include "d2dbs/version.h" #include "d2dbs/handle_signal.h" #include "d2dbs/cmdline.h" #include "service.h" #include "winmain.h" #include "console_output.h" #include "common/setup_after.h" #define WM_SHELLNOTIFY (WM_USER+1) extern int app_main(int argc, char **argv); /* d2dbs main function in d2dbs/main.c */ namespace pvpgn { extern HWND ghwndConsole; /* hwnd for eventlog output */ namespace d2dbs { static void KillTrayIcon(HWND hwnd); static int OnShellNotify(HWND hwnd, int uID, int uMessage); static void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct); static void OnClose(HWND hwnd); static void OnSize(HWND hwnd, UINT state, int cx, int cy); LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL CALLBACK DlgProcAbout(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static int gui_run = TRUE; /* default state: run gui */ static int d2dbs_run = TRUE; /* default state: run d2dbs */ static int d2dbs_running = FALSE; /* currect state: not running */ int fprintf(FILE *stream, const char *format, ...) { int temp = 0; va_list args; va_start(args, format); if (stream == stderr || stream == stdout) { char buf[1024] = {}; std::vsnprintf(buf, sizeof buf, format, args); gui_lvprintf(eventlog_level_error, "{}", buf); temp = 1; } else temp = vfprintf(stream, format, args); va_end(args); return temp; } static void KillTrayIcon(HWND hwnd) { NOTIFYICONDATA dta; dta.cbSize = sizeof(NOTIFYICONDATA); dta.hWnd = hwnd; dta.uID = ID_TRAY; dta.uFlags = 0; Shell_NotifyIcon(NIM_DELETE, &dta); } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_CREATE, OnCreate); HANDLE_MSG(hwnd, WM_SIZE, OnSize); HANDLE_MSG(hwnd, WM_COMMAND, OnCommand); HANDLE_MSG(hwnd, WM_CLOSE, OnClose); case WM_SHELLNOTIFY: return OnShellNotify(hwnd, wParam, lParam); } return DefWindowProc(hwnd, uMsg, wParam, lParam); } static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) { ghwndConsole = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_MULTILINE | WS_VSCROLL | WS_HSCROLL | ES_NOHIDESEL, 0, 0, 0, 0, hwnd, 0, 0, NULL); if (!ghwndConsole) return FALSE; return TRUE; } static void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case ID_RESTORE: OnShellNotify(hwnd, ID_TRAY, WM_LBUTTONDBLCLK); break; case ID_START_D2DBS: d2dbs::fprintf(stderr, "Sending Start Signal to d2dbs\n"); d2dbs_run = TRUE; break; case ID_SHUTDOWN_D2DBS: d2dbs::fprintf(stderr, "Sending Shutdown Signal to d2dbs\n"); d2dbs_run = FALSE; d2dbs_signal_quit_wrapper(); break; case ID_RESTART_D2DBS: d2dbs::fprintf(stderr, "Sending Restart Signal To d2dbs\n"); d2dbs_run = TRUE; d2dbs_signal_quit_wrapper(); break; case ID_EDITCONFIG_D2DBS: ShellExecuteW(nullptr, L"open", L"notepad.exe", L"conf\\d2dbs.conf", NULL, SW_SHOW); break; case ID_LOADCONFIG_D2DBS: d2dbs::fprintf(stderr, "Sending Reload Config Signal To d2dbs\n"); d2dbs_signal_reload_config_wrapper(); break; case ID_LADDER_SAVE: d2dbs::fprintf(stderr, "Sending Save Ladder Signal To d2dbs\n"); d2dbs_signal_save_ladder_wrapper(); break; case ID_EXIT: OnClose(hwnd); break; case ID_CLEAR: SendMessage(ghwndConsole, WM_SETTEXT, 0, 0); break; case ID_ABOUT: DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ABOUT_BOX), hwnd, (DLGPROC)DlgProcAbout); break; } } static int OnShellNotify(HWND hwnd, int uID, int uMessage) { if (uID == ID_TRAY) { if (uMessage == WM_LBUTTONDBLCLK) { if (!IsWindowVisible(hwnd)) ShowWindow(hwnd, SW_RESTORE); SetForegroundWindow(hwnd); } } return 0; } static void OnClose(HWND hwnd) { d2dbs::fprintf(stderr, "Sending Exit Signal To d2dbs\n"); gui_run = FALSE; d2dbs_run = FALSE; d2dbs_signal_exit_wrapper(); } static void OnSize(HWND hwnd, UINT state, int cx, int cy) { if (state == SIZE_MINIMIZED) { NOTIFYICONDATAW dta = {}; dta.cbSize = sizeof(NOTIFYICONDATAW); dta.hWnd = hwnd; dta.uID = ID_TRAY; dta.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; dta.uCallbackMessage = WM_SHELLNOTIFY; dta.hIcon = LoadIconW(GetWindowInstance(hwnd), MAKEINTRESOURCEW(ID_ICON1)); std::swprintf(dta.szTip, sizeof dta.szTip / sizeof *dta.szTip, L"D2DBS Version " D2DBS_VERSION_NUMBER); Shell_NotifyIconW(NIM_ADD, &dta); ShowWindow(hwnd, SW_HIDE); return; } MoveWindow(ghwndConsole, 0, 0, cx, cy, TRUE); } BOOL CALLBACK DlgProcAbout(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: EndDialog(hwnd, TRUE); return TRUE; case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch ((int)wParam) { case IDOK: EndDialog(hwnd, TRUE); return TRUE; } } return FALSE; } #define EXIT_ERROR -1 #define EXIT_OK 0 #define EXIT_SERVICE 1 static void d2dbs(void * dummy) { switch (app_main(__argc, __argv)) { case EXIT_SERVICE: gui_run = FALSE; /* close gui */ case EXIT_ERROR: d2dbs_run = FALSE; /* don't restart */ case EXIT_OK: ; /* do nothing */ } d2dbs::fprintf(stderr, "Server Stopped\n"); d2dbs_running = FALSE; } } } using namespace pvpgn; using namespace pvpgn::d2dbs; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { HWND hwnd; MSG msg; Console console; if (cmdline_load(__argc, __argv) != 1) { return -1; } if (cmdline_get_console()){ console.RedirectIOToConsole(); return app_main(__argc, __argv); } if (LoadLibraryW(L"RichEd20.dll") == NULL) { return -1; } WNDCLASSEXW wc = {}; wc.cbSize = sizeof(WNDCLASSEXW); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(LPVOID); wc.hInstance = hInstance; wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(ID_ICON1)); wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = MAKEINTRESOURCEW(ID_MENU); wc.lpszClassName = L"BnetWndClass"; if (!RegisterClassExW(&wc)) RegisterClassW((LPWNDCLASS)&wc.style); hwnd = CreateWindowExW(0L, L"BnetWndClass", L"Diablo II DataBase Server", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, LoadMenuW(hInstance, MAKEINTRESOURCEW(ID_MENU)), hInstance, NULL); if (hwnd) { ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); } while (GetMessageW(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); if (!d2dbs_running && d2dbs_run && gui_run) { d2dbs_running = TRUE; _beginthread(pvpgn::d2dbs::d2dbs, 0, NULL); } if (!gui_run && !d2dbs_running) { KillTrayIcon(hwnd); exit(0); } } return ((int)msg.wParam); } #endif pvpgn-1.99.7.2.1+dfsg/src/win32/dirent.h000066400000000000000000000163461462640461400174010ustar00rootroot00000000000000/***************************************************************************** * dirent.h - dirent API for Microsoft Visual Studio * * Copyright (C) 2006 Toni Ronkko * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * ``Software''), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Dec 15, 2009, John Cunningham * Added rewinddir member function * * Jan 18, 2008, Toni Ronkko * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string * between multi-byte and unicode representations. This makes the * code simpler and also allows the code to be compiled under MingW. Thanks * to Azriel Fasten for the suggestion. * * Mar 4, 2007, Toni Ronkko * Bug fix: due to the strncpy_s() function this file only compiled in * Visual Studio 2005. Using the new string functions only when the * compiler version allows. * * Nov 2, 2006, Toni Ronkko * Major update: removed support for Watcom C, MS-DOS and Turbo C to * simplify the file, updated the code to compile cleanly on Visual * Studio 2005 with both unicode and multi-byte character strings, * removed rewinddir() as it had a bug. * * Aug 20, 2006, Toni Ronkko * Removed all remarks about MSVC 1.0, which is antiqued now. Simplified * comments by removing SGML tags. * * May 14 2002, Toni Ronkko * Embedded the function definitions directly to the header so that no * source modules need to be included in the Visual Studio project. Removed * all the dependencies to other projects so that this very header can be * used independently. * * May 28 1998, Toni Ronkko * First version. *****************************************************************************/ #ifndef DIRENT_H #define DIRENT_H #include #include #include typedef struct dirent { char d_name[MAX_PATH + 1]; /* current dir entry (multi-byte char string) */ WIN32_FIND_DATAA data; /* file attributes */ } dirent; typedef struct DIR { dirent current; /* Current directory entry */ int cached; /* Indicates un-processed entry in memory */ HANDLE search_handle; /* File search handle */ char patt[MAX_PATH + 3]; /* search pattern (3 = pattern + "\\*\0") */ } DIR; /* Forward declarations */ static DIR *opendir(const char *dirname); static struct dirent *readdir(DIR *dirp); static int closedir(DIR *dirp); static void rewinddir(DIR* dirp); /* Use the new safe string functions introduced in Visual Studio 2005 */ #if defined(_MSC_VER) && _MSC_VER >= 1400 # define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE) #else # define STRNCPY(dest,src,size) strncpy((dest),(src),(size)) #endif /***************************************************************************** * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static DIR *opendir(const char *dirname) { DIR *dirp; assert(dirname != NULL); assert(strlen(dirname) < MAX_PATH); /* construct new DIR structure */ dirp = (DIR*)malloc(sizeof (struct DIR)); if (dirp != NULL) { char *p; /* take directory name... */ STRNCPY(dirp->patt, dirname, sizeof(dirp->patt)); dirp->patt[MAX_PATH] = '\0'; /* ... and append search pattern to it */ p = strchr(dirp->patt, '\0'); if (dirp->patt < p && *(p - 1) != '\\' && *(p - 1) != ':') { *p++ = '\\'; } *p++ = '*'; *p = '\0'; /* open stream and retrieve first file */ dirp->search_handle = FindFirstFileA(dirp->patt, &dirp->current.data); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* invalid search pattern? */ free(dirp); return NULL; } /* there is an un-processed directory entry in memory now */ dirp->cached = 1; } return dirp; } /***************************************************************************** * Read a directory entry, and return a pointer to a dirent structure * containing the name of the entry in d_name field. Individual directory * entries returned by this very function include regular files, * sub-directories, pseudo-directories "." and "..", but also volume labels, * hidden files and system files may be returned. */ static struct dirent *readdir(DIR *dirp) { assert(dirp != NULL); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* directory stream was opened/rewound incorrectly or ended normally */ return NULL; } /* get next directory entry */ if (dirp->cached != 0) { /* a valid directory entry already in memory */ dirp->cached = 0; } else { /* read next directory entry from disk */ if (FindNextFileA(dirp->search_handle, &dirp->current.data) == FALSE) { /* the very last file has been processed or an error occured */ FindClose(dirp->search_handle); dirp->search_handle = INVALID_HANDLE_VALUE; return NULL; } } /* copy as a multibyte character string */ STRNCPY(dirp->current.d_name, dirp->current.data.cFileName, sizeof(dirp->current.d_name)); dirp->current.d_name[MAX_PATH] = '\0'; return &dirp->current; } /***************************************************************************** * Close directory stream opened by opendir() function. Close of the * directory stream invalidates the DIR structure as well as any previously * read directory entry. */ static int closedir(DIR *dirp) { assert(dirp != NULL); /* release search handle */ if (dirp->search_handle != INVALID_HANDLE_VALUE) { FindClose(dirp->search_handle); dirp->search_handle = INVALID_HANDLE_VALUE; } /* release directory handle */ free(dirp); return 0; } /***************************************************************************** * Resets the position of the directory stream to which dirp refers to the * beginning of the directory. It also causes the directory stream to refer * to the current state of the corresponding directory, as a call to opendir() * would have done. If dirp does not refer to a directory stream, the effect * is undefined. */ static void rewinddir(DIR* dirp) { /* release search handle */ if (dirp->search_handle != INVALID_HANDLE_VALUE) { FindClose(dirp->search_handle); dirp->search_handle = INVALID_HANDLE_VALUE; } /* open new search handle and retrieve first file */ dirp->search_handle = FindFirstFileA(dirp->patt, &dirp->current.data); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* invalid search pattern? */ free(dirp); return; } /* there is an un-processed directory entry in memory now */ dirp->cached = 1; } #endif /*DIRENT_H*/pvpgn-1.99.7.2.1+dfsg/src/win32/logo01.ico000066400000000000000000000346461462640461400175430ustar00rootroot00000000000000hF ¨®00¨V00¨þ( @7pM¥&fËC� ?pSzª‘´Õx�ÄŒ­Ñ„µÚH¸ì3¬Ö…Øÿ >~J¨6yÍJt�"KQœ¬a¦ËZÃÚ -M%I’!2< Eƒ'9'< s�£PLI0�\!•~ÿÿÿÛÜÝÞßàáâãäåæç ÌÍÎÏÐÑÒÓÔÕÖר JÚ¾¿ÀÁÂÃÄÅÆÇÈÉÊ J ±²³´µ¶·¸¹º»¼½ ” ¢£¤¥¦§¨©ª«¬­® ¯°•–—˜™š›œ�žŸ ¡ v †‡ˆ‰Š‹Œ�Ž��‘’ “”wxyz{|}~€�‚ƒ … ijklmnopqrstu v Z[\]^_`abcdef ghKLMNOPQRSTUVW -Y<=>?@ABCDEFGH J./0123456789: ; !"#$%&'()*+ ,-  ( @€7pM¥&fËC� ?pSzª‘´Õx�ÄŒ­Ñ„µÚH¸ì3¬Ö…Øÿ >~J¨6yÍJt�"KQœ¬a¦ËZÃÚ -M%I’!2< Eƒ'9'< s�£PLI0�\!•~ÿÿÿÖÖ×רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááââ ÖÖ×רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááââ ÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐÐÑÑÒÒÓÓÔÔ IIÕÕÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐÐÑÑÒÒÓÓÔÔ IIÕÕ»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇ II »»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇ II ®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº ‘‘ ®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº ‘‘ ŸŸ  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª«« ¬¬­­ŸŸ  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª«« ¬¬­­’’““””••––——˜˜™™šš››œœ��žž tt ’’““””••––——˜˜™™šš››œœ��žž tt ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒ��ŽŽ�� ��‘‘ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒ��ŽŽ�� ��‘‘uuvvwwxxyyzz{{||}}~~€€�� ‚‚ uuvvwwxxyyzz{{||}}~~€€�� ‚‚ gghhiijjkkllmmnnooppqqrrss tt gghhiijjkkllmmnnooppqqrrss tt XXYYZZ[[\\]]^^__``aabbccdd eeffXXYYZZ[[\\]]^^__``aabbccdd eeffJJKKLLMMNNOOPPQQRRSSTTUUVV --WWJJKKLLMMNNOOPPQQRRSSTTUUVV --WW<<==>>??@@AABBCCDDEEFFGGHH II<<==>>??@@AABBCCDDEEFFGGHH II..//00112233445566778899:: ;;..//00112233445566778899:: ;; !!""##$$%%&&''(())**++ ,,-- !!""##$$%%&&''(())**++ ,,--    ??003333000000003300000000000000(0`€         #%& ("+$-$.$0#1 2346 :!;"<%>'@)A+F,H.L0N0O6LLBd7o8s=wEvNyVx\jaRfIjCm@o>oG.- *.! *üÎ_U 8®8786­+{G,)‚Š+zL0/‚|A)!F. )eýa]2223883¬,"$ ( }P0…ƒ, <$!H>!QÍÎ_W2336­73ÜYYYYYY+H *G' ‚‡(){(.†Gto´ï^178o·­­­ÓYYYYYY)G $,|‘/'+||„‹/Lkg´éP%9Ýø¯n¸«æécYY!@"+ .‹|*+>‹‡’{t½¼Û·!;éÿÿziÿý­ç®bYY+/   ƒ|**/Ž‹�‚eÊÖÝtoïüÿp¾ÿüð­æódYY -G |{..‡™—�Õñ°t[";èôÿ(0ýñøí®ææ®YYYH.  G= G�=•˜—kï]]\1§îÿ:Æîô÷ðçæ­­YYY-  -I'Š0‹˜—„ÁÓa\ $:äÿþ)ñëõöõÛ­­æ- 0< !ŠG?••�gÆË_U!F¥êÿŽ,,æãøüúÔèñëY  z ‹?*Š•–OdÂÅ^ '<âÿÿŽƒ€âãñüüûøÿóYYYU ! Š@y�–kgeeÕ_ 0Šþÿí¦¢£âäîñõÑÿðñYYYY  zx'‹˜„ÎÎ˾òa!C£÷ßÛÜ¢¦ââëëëûôïðYYY! " *yz�˜tÄÍÌÂÕc+€õàãù÷²¢ââäíìÑíéïYYYY% $ HI!‹—E\e½Ê½¿d/†Ýàéø÷ð²âââäéÒÞèëYYY%"$ HIz��[ºÂfeÄUD¨¨ªÛÑóîãââààëרëæY#$"% J„‹ƒihLNÖa ;ñ¨´¶Äùóæâ輦ªªíëæ$ ! ).„‹•-ptNQºd)ƒß¤ogiÄõëèí»Ž‹§ëíç%" ! %„‰(tNQUMÖc…ª�tttfÔïñÙ¼µŽ¤ëçëYYYYYY$"!H(PUÙÂ�ª±ÀÇÀirð𿼼¾ÂîèìYYYYYY&$"# /OïµÞ±ÈËÓÑÕ·í¹¼¼¾×Ú®¬YY'!"   P´ÑÚØÐûûÐÑÒéµµµ»ñ¸¬©YY""// ll´´¿ÌÌÍÑÐúíµ±±¸ñ·mµYY GŽlkkqvIIzxkÑùí³±±±×ظ¹YYY&•”$$Hw‚plllw/““žÕømòí²±ª°Ûïh×YYYz!–%EA('“rpkkp/„$%GJImÚ§¨©ÞîÛØ° E��„„/“C yqpik "-j»ÃÐÑôÚ¨¨ªîïï×Ù-'““‘‰�‰œ™�xi¿0f½Àrt½ÐúòÝ©ªèìëêñðYYYYYY „‘’“’’›™™œŠ³ÃÆÀÀjK*KM¾ÑôÛªàèèíéñóYYYYYY%ƒˆ‰““™˜�wkrÀÊÓ˽I*rsuÖòêªíäÞÚ×ÖóYY€„B†‰““™@oŽ�žÇÊËËiwkiÌüÔõòìªúíçÙÖÙïYYY@ˆ‰“’‹ŸšŸ´²³ÊÓ˽Lr¾ûþüøõìªÿóèÛÕ×ñYYY$$†E‡ˆ‘‰��™½i³ÀÀÓÒÓÃpKkÒÑýûõì©ÿüíÝÕÔöYYYBC†?‡ˆ’�‘²mk³ÇÇËÑÑÒÔ½ofÍÐýûÑí«ÿëÞ°ÛÔöYYYYY?††}ˆ‰”¡ƒØ>i³ÀÇÊÐÐÐʾigÌÏýúøííó䯯°ÍúYYYYY,D~�~-Š�•ȲÒp³²ÀÈÒÐÐÐÌÀheËÏýûøòíì寯ÜÍõ/}@€†~” žÓÐÕl³²ÇËÐÐúÐÔ´rrËÏüÑרÛìæ¯¯ÙÌóA}B‚€™²²Çûjk³ÀÉÒÓÊÁÀoLPrÅÍû¿kk×í鯸¿ÅõYYYYYY@D@~D‰ž²²Çû>k³ÀÁ¼iËÎnu*(¼ÆÆÆÄssÕØê¯¹¾ÂöYYYYYYCEƒ}C�žÙ²ÇúHlkr*t¶µÂPO&ÇÆÈÉÊÔËm,Úí¸¹¾ÂôYYYCB†„B‹Ÿ±²ÉÒIpu+ ÂI�ÊËÆÈÈÇÉÌÒÙì𸹹ÂóYYYB?……~‘¤²²ÙÀIvH *+qkK�ÕÏÉÆÆÆÆÈÄËòíô¹¹¿ÄöYYY@~€†‡�²�¡Ù´Owx�i³¾Œ˜ÑÐÊÆÁÁÆÃÅÆÕêëë°¸ÚÅôYYY|~…Š� žžnr,uvyp��ÀûúÌÇÂÃÃÆÃÅØÚèêëݯÙÙóYYYYYY|~~„Љž¡�–�MpnmÆÏÏÎËÆÁÂÂÃÃÉÆØÛÞíî®°ÙØõYYYYYY?B~…‹‰›ŸŸ~–‚*K“likÉÊÃÃÃÃÅÊËÕ××ÛÞçâ«°ÙÚö$~~�Љœ™™€–“|–J’»ÃÄÃÄÄÌÏÐÐÑÑÕÛÞáâ«ÙÜÛöÿÿ€€€€ƒƒƒƒƒÁÀÀàÀÀÁÇ€€€€ƒƒƒƒƒ€€€€€€€€€€€€€€€À€€€€(0`€"">*6^*P );laÈ'QŽ4W)QnƒGO`"=m :d2Y.L$8%3)@2Q 1('#1$/=¾Ùè§ÆÒvaE-0! 3C)Š^E¶•-šj‘S¨f$£L%¸�&F#B„2a(H"E*&M‹S£+D'K1J[:D,T"L�B�:h(>!35d0U",$1&8(5Vex¼éû…~`\L;% 6\g] q. ¦{©b ‰'(­~%J- 7A !D„&8N&2!6Q6!PœH–*J&6$7$Cl52"=e>r&3&*2A•·É˜½ÄuaD88%& 8d r0n6�Kª¦TŠ7X¾Æ999999999999999999"+F$sV¥!Q¡X¹!DvAYpn‹°b‰¯\²æO™½'$ )"/ HcSÆï¹þþÞþû!VzN‚ŸÎþýµùö¼·Mëæ=ÕÇFˆe999999*G)R- $%5"4  &N’B~,:-A/U%[¯!U¨![¸H†Teƒ©Î|»æT·å3Yg#%' %cˆpÙøªïþîûþ aƒq µÒöù³íþwáóº­<êÝ‚ðì<“w999999!-O!4_ ( G€$Cw"-0S3RP¡ \ÀX¼+Z°€°Ö}ËøO¦Ë7Vc=?> -HdMÇý‹Üúüþý/?;T½ï÷}ÝúšÓäcáì6ÄÀ.ãÜBíç6µ„999999999"6e1V&    7[@q!20ZD’@vZÀ[¿!XÀHm•qÉõLQE\O?BA8#&&# >C$ŒÌkÔúþþü%Ue %y¯ÍjÙý‹Üý—ÙèxÙèKËÉ1ÖÒ%̲Ê­999999999+Q    1L%C`%-"=R§5Q$V© ZÁWÃ#M—sšÇ‡»Å›€WEB:*-*(7Qz=ÃýøüþÌèú&?!(yßùRÑü�ãý•ÕìŽÝè`ÊÑ1ÊÇÌÅ$ØÜ+L ) 8P$El".1P°4Y#Q£Y¾XÁ*;P[f|™»{¨ÂdZI(,- )< Fp*¦ö÷þýÐùþb±U“Sоö/ÈýØü³æþ¹àì±ÕçžÒòºòù‡çú999999999$4;  --R¯;w!*LY¶UÅKn–iz”akƒapƒ|¶Ôj]F %7Vg¤ÖéÿÆîúcÃõ*�ÝkÌwǸû5ÌÿlÖþÞø�àü˜Ë߯öýyÎò×ü999999999999'-     'Dr1Ly$#9!V®[Æ-U–š°Õ�¹ØŒ©É•´†Åê�{^"2 Bjv¸˜ÞôB³î\µÜZžç$má‚ã¶ø'Áû[ÑþY×ûZÃø«ÙåŠÎçqÑøw×û9999999992! 1  )>)Fq'4""F|Wº [¾?Qh‚¢Å•µØŽ²Ø~™À€»Ûšˆq ,ETˆ�Øþ3›ð:»ö£åú™ØîJ…Ýpݲû³ù<ÈüfËù]Âø˜ÐàcÄæUÄînÕþ999999999999 : '7 "8T&Af+ 6"R¥XÃ6f?AKQb€hŽÄ„©Õj‹°rš»ŠŒ} 4S aŸW¬æ0¥ïTÃõ�ÜòžàöuÎóN‡Ö¬ö«ùºü>À÷W¼ðŒÆàC»ãPÆõUÍý999999999<,6* #8T#@_#*$J�W¶#Y·&%)==C_‚´{œÃNjŠVj†¥µ-/)%>h4‘×�æ. ä]±ß�Ìâ†ØëlØø8¾ø¨ý­ú)´í+­ð[Ãôt²ÌiÊÖPÍûCÌþ99947*: & ,@a* P—"V±H–#!',\u¦[w–9J]6FTz½à�w]#/ Lw|Íý%�Ý=†·W“²|³Ä¡Óä‚áïFÌø#¸ûMÅùe‹¤0„Ø6šâ'šÔcÏòTÓÿHÌû$ 9 +5" &<"3N'Q”#U§VÀ*M" 'Ia…ATm1@R'4Db™¹ƒ} (@ V“?¯ë$z¶:g…L{‘W‰œ³Ã‹ßê[ÒóOÅøfÈ÷e”¥$fª `§ ŒÔZÐödÚýOÊì :0(1 (9J•M®">!$7Kg2@S(2?&.90GVwÁß[fp`�$šÖ)n¡9Uj=[h4[iEw†€¾ÌpÕôÔôdµÚf�¤X�«%a°"xÃPÓþJÃÎ[ÒÓ999999999999999999$ 712 "  "9_%@ $##09O&/< )6"-%i«Ì‘™–i²4›ã>‘Ëf–ÆxªÓp¨¾[ˆŸ7dyyÖïuÏô` ¿c�¦bަk•©u§¸lÛúRÍÇ_ËÉ999999999999999999;8 /4 ")N&&'%%+6 &1$/"+=NpÈíL�¬MªïI�Î|¥Ï…¯Ò�»Ú•ÀÝ{³ÍP�°iÊõ`—¶c�¦d�¦n—­pÁäe¿Ý9»»$¨«999999"; 63 % +, ) *# $k'!W½U¾ 79#;i0Dv!H�8_ŠDh�Dh�Ah�-Qu)\'R¾L¾j©‰ÆêjËìA‹Ä<�Æ?—ÕL¢×]¹ÜtÆè\‰špºË999999999'Dy/VÊ L*b7o%8 #NS¾ 7O|;[„Kj‘Jo•Eaƒ'T@˜7 3 ?;\(D`$Bh=v�b²á*ŠÐ-�Õ,“ÙGªàkÆè`·àk²Ó[¤Ä 3e"O´&Xº"J•E™2SRÀ /j!+Gp2X…@_†X{£Gi�"2.#.QNrŸdˆ¶~žÇ ¾à–»áŽËó^©Ý+Ž×)�Ý›ènÊým×þrÖùn¶Øi¯Ð+L '#AP¼S½P·M¯V´K«%b½aÇ"\²-O{Ut›j�½!4I(7Keˆg�Åq”¿FZ|;Opi„ ¤Æß©Ðð�ÌòRªÝ#’à—âR¾ú`ÇûUÏöXÍì}Ïù{Îò999999999999999999  ( D™M¶N¸P¾Q¹P¸%`ÍcÍ!bÑ#bÄR¥_�Â{�È€¢Íp“Êl–ÆOn›-A`*>6Kb5?Wx“¬˜Èì‰Ðö]¹ì"™ä+§îNÇòO¼ôeÇéWÉè}Ëé…×ú999999999999999999 "AŠ@#G’K¨F®O¾R»^ÌYÅY´1OƒLnš5Q}nšÑ�ªÛ�³ÞŠ®ÖnŒ²&@a*;)DZyB\v6Orw´Û†Îô[ÀñžèeÅöBÄùI·ócÀárÆâ{Â݇Øý999999CŒB™5tCžG¯O¿R»!`Î9s1^‘!]°+c³2d¶wžÒ‚¬Ü‰®ÙŠ®Ö]{ /JkJk�e{ž‹²Î³Øô„·Ù�Êò�ÈîaÆ÷�è§êýfÅúL¼ðf¿ÚyÄÚhÄÛqÚû999999999 !=Œ8yA“F¦J­O½Rº"S©&fÃ"eÐ-h¾CtºI|ÌT¾‚¬ÝŒ±Û†­ÙlŒ°-?\=Xk�·¬ÏéÊåö»ß÷�Ñô‘Çñ_Ëú ˜Ý×ñþˆ×úUÂí]¹Ô¿ÒtÀÛÙý999999999$$$  7 6E 6dE¡F¦O¸K°(cÆ(]¼*XÓ~ˆ«Ts¯bˆÂr˜ÑošÔˆ¯Ü�¶âŒ²Ý} ÌEa‡-BcJh”’µß™ÀâÀàóªÔó�Æî`Ëö•ÜÏóù­ãùdÊîM³Ð|¾Ò„¾Ó“Þö999999999""""?m0qG ƒKªN¯T»1qÒD–m�Þ=rQx®bŠÅl”Îu�ׄªÚš·ãž¾â™¸ã„¬Üo’º_}ŸVq’�®ÕžºÜÂßó©Ïï›ÇðiËöhÉï†àù;ÌõC§ÑC©ÌN®Ë•¸Ä¥Õí999999999999999 -I 0h:…DŽ?Š'LP°'eÅW¼|£ÚR‚ÊŽ·ç6UˆX½`‹Èg”Ð|£Ú�²ß¡Àâ¤Ã䜾ä�³Üs˜ÀWwšKgƒ‰§Ð›¸ÛÉßö«ÊìœÌìƒÊúeÏù`Õû>ÒùE¨ÆJ§Ã[±Ç›¶½�×ï.Z=;rBŠG�?€"U½2nÇ1eÊ±à›½ì|«äCb•Z‚ÀbŽËx ×ˆ¬Ýœºá¥Äå§Æç›¼ã…¯ÜZ„±@\zC_zЍÑ�»Þ¾Ùô–¹àv´ân°Ûa¼é]ÈöBÎüD©ÁU§¾e±Â�²·†Ùõ9n9~8oC‘;ˆB”,[ÈI|ÍJÐz©å®ÐîJs©GiŸ`‡Ål”Φב³Þ‰°Ü‚©Öv�Ên•ÉGc†+@[(5K>Z|‚£Ì”´Ø¬Éëj“ÀKn–DlŽpºßbÅñTÉùF©½Z¦»o®½{°ºŽÚþ999999999999999999;q6g:x9‰5gL¬4fËWŒÕUŒ×w¢Ù¯Ïé>kGiŸ`‡Äs•Éu—Ãe‚§a�£Ž©È›¶Ô?a“4Jf+=%=gˆ°yŸÎ¢Îu£Îx¢Ç2Uw6XsÉái¿ç]ÆñI¤¹`¨·oªµx­¶“Øÿ9999999999999999996n4bD•8„4jQ´4eÌc–ÖR�Ôt›Þ§Åí$:dDb•Gi›=V~ ,>ANdC•µZ‘¹œ¢+6J);R!Y2Io(<]#1)D+E4T�Io¤.Eh*W²·Þ—¼Ü‚¥Î|¡ÎzžÌy Í|žÏ€£Ï}¡È‰®ÕÆëdÍö�Òö_¨½`¡³e¬¼}­¹’Õý9999999997u?„CŒG K¢ SµG€×*[³;qÆ_™ÛK{º*9P/Fl.Fm;`¯Ht´W‚¿p‘¹/Y¡ZƘ»ç¥Ãà†§Ôy�Ëw›Éw›Éy�Ë{ŸÈ£Ëy¢Ïz¯ÕZÊòQÏ÷\ÔöWªÈW¡¶b³Éw¯ÈŒÕý999999999@€>†A‰Hœ"Oª!S³2kÏ*cÁ*c½6d«;[{1H5Np2Im)Dx3S‰8^«+]¶p™Ý²ÎæªÈ厳Õt™Ñx�É{žÊ{žÊ{œÍ€žÈx£Êp­×d¸ÞPÈöWËóZÐ÷N­ÎM§Âd±Çb°Ó‡×ü999999999999999999@€<ƒ?‡D˜"OªM­,eÉ'lÏ1f±!TÂ$[´#%1/@[4Xˆ7b© #include #include #include #include #ifndef WINADVAPI #if !defined(_ADVAPI32_) #define WINADVAPI DECLSPEC_IMPORT #else #define WINADVAPI #endif #endif extern char serviceLongName[]; extern char serviceName[]; extern char serviceDescription[]; extern int g_ServiceStatus; #ifdef WIN32_GUI extern int app_main(int argc, char ** argv); #else extern int main(int argc, char ** argv); #endif SERVICE_STATUS serviceStatus; SERVICE_STATUS_HANDLE serviceStatusHandle = 0; typedef WINADVAPI BOOL(WINAPI *CSD_T)(SC_HANDLE hService, DWORD dwInfoLevel, LPVOID lpInfo); void Win32_ServiceInstall() { SC_HANDLE serviceControlManager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CREATE_SERVICE); if (!serviceControlManager) { return; } wchar_t path[MAX_PATH + 10] = {}; if (GetModuleFileNameW(nullptr, path, sizeof path / sizeof *path) != 0) { return; } std::wcscat(path, L"--service"); auto utf8_decode = [](const std::string& str) { if (str.empty()) return std::wstring(); int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); std::wstring wstrTo(size_needed, 0); MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed); return wstrTo; }; SC_HANDLE service = CreateServiceW(serviceControlManager, utf8_decode(serviceName).c_str(), utf8_decode(serviceLongName).c_str(), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path, 0, 0, 0, 0, 0); if (service == NULL) { return; } SERVICE_DESCRIPTIONW sdBuf = {}; auto temp = utf8_decode(serviceDescription); std::vector buffer(temp.begin(), temp.end()); sdBuf.lpDescription = buffer.data(); HMODULE advapi32 = GetModuleHandleW(L"advapi32.dll"); if (advapi32 == NULL) { CloseServiceHandle(service); CloseServiceHandle(serviceControlManager); return; } auto ChangeServiceDescription = reinterpret_cast(GetProcAddress(advapi32, "ChangeServiceConfig2W")); if (ChangeServiceDescription == NULL) { CloseServiceHandle(service); CloseServiceHandle(serviceControlManager); return; } ChangeServiceDescription( service, // handle to service SERVICE_CONFIG_DESCRIPTION, // change: description &sdBuf); CloseServiceHandle(service); CloseServiceHandle(serviceControlManager); } void Win32_ServiceUninstall() { SC_HANDLE serviceControlManager = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT); if (serviceControlManager) { auto utf8_decode = [](const std::string& str) { if (str.empty()) return std::wstring(); int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); std::wstring wstrTo(size_needed, 0); MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed); return wstrTo; }; SC_HANDLE service = OpenServiceW(serviceControlManager, utf8_decode(serviceName).c_str(), SERVICE_QUERY_STATUS | DELETE); if (service) { SERVICE_STATUS serviceStatus; if (QueryServiceStatus(service, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) DeleteService(service); } //DeleteService(service); CloseServiceHandle(service); } CloseServiceHandle(serviceControlManager); } } void WINAPI ServiceControlHandler(DWORD controlCode) { switch (controlCode) { case SERVICE_CONTROL_INTERROGATE: break; case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); g_ServiceStatus = 0; return; case SERVICE_CONTROL_PAUSE: g_ServiceStatus = 2; serviceStatus.dwCurrentState = SERVICE_PAUSED; SetServiceStatus(serviceStatusHandle, &serviceStatus); break; case SERVICE_CONTROL_CONTINUE: serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(serviceStatusHandle, &serviceStatus); g_ServiceStatus = 1; break; default: if (controlCode >= 128 && controlCode <= 255) // user defined control code break; else // unrecognized control code break; } SetServiceStatus(serviceStatusHandle, &serviceStatus); } void WINAPI ServiceMain(DWORD argc, char* argv[]) { // initialise service status serviceStatus.dwServiceType = SERVICE_WIN32; serviceStatus.dwCurrentState = SERVICE_START_PENDING; serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; serviceStatus.dwWin32ExitCode = NO_ERROR; serviceStatus.dwServiceSpecificExitCode = NO_ERROR; serviceStatus.dwCheckPoint = 0; serviceStatus.dwWaitHint = 0; auto utf8_decode = [](const std::string& str) { if (str.empty()) return std::wstring(); int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); std::wstring wstrTo(size_needed, 0); MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed); return wstrTo; }; serviceStatusHandle = RegisterServiceCtrlHandlerW(utf8_decode(serviceName).c_str(), ServiceControlHandler); if (serviceStatusHandle) { wchar_t path[MAX_PATH + 1] = {}; unsigned last_slash = 0; GetModuleFileNameW(0, path, sizeof path / sizeof *path); for (std::size_t i = 0; i < std::wcslen(path); i++) { if (path[i] == '\\') last_slash = i; } path[last_slash] = 0; // service is starting serviceStatus.dwCurrentState = SERVICE_START_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); // do initialisation here SetCurrentDirectoryW(path); // running serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(serviceStatusHandle, &serviceStatus); //////////////////////// // service main cycle // //////////////////////// g_ServiceStatus = 1; argc = 1; #ifdef WIN32_GUI app_main(argc, argv); #else main(argc, argv); #endif // service was stopped serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); // do cleanup here // service is now stopped serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(serviceStatusHandle, &serviceStatus); } } void Win32_ServiceRun() { SERVICE_TABLE_ENTRYA serviceTable[] = { { serviceName, ServiceMain }, { 0, 0 } }; StartServiceCtrlDispatcherA(serviceTable); } #endif pvpgn-1.99.7.2.1+dfsg/src/win32/service.h000066400000000000000000000016531462640461400175470ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32 #ifndef WIN32_SERVICE_INCLUDED #define WIN32_SERVICE_INCLUDED void Win32_ServiceInstall(void); void Win32_ServiceUninstall(void); void Win32_ServiceRun(void); #endif /* !WIN32_SERVICE_INCLUDED */ #endif /* WIN32 */ pvpgn-1.99.7.2.1+dfsg/src/win32/windump.cpp000066400000000000000000000040631462640461400201230ustar00rootroot00000000000000/* * Copyright (C) 2018 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32 #include #include void make_minidump(struct _EXCEPTION_POINTERS* e) { auto hDbgHelp = LoadLibraryA("dbghelp"); if (hDbgHelp == nullptr) return; auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump))GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); if (pMiniDumpWriteDump == nullptr) return; char name[MAX_PATH]; { auto nameEnd = name + GetModuleFileNameA(GetModuleHandleA(0), name, MAX_PATH); SYSTEMTIME t; GetSystemTime(&t); wsprintfA(nameEnd - strlen(".exe"), "_%4d%02d%02d_%02d%02d%02d.dmp", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); } auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) return; MINIDUMP_EXCEPTION_INFORMATION exceptionInfo; exceptionInfo.ThreadId = GetCurrentThreadId(); exceptionInfo.ExceptionPointers = e; exceptionInfo.ClientPointers = FALSE; auto dumped = pMiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory), e ? &exceptionInfo : nullptr, nullptr, nullptr); CloseHandle(hFile); return; } LONG WINAPI unhandled_handler(struct _EXCEPTION_POINTERS* e) { make_minidump(e); return EXCEPTION_CONTINUE_SEARCH; } #endif pvpgn-1.99.7.2.1+dfsg/src/win32/windump.h000066400000000000000000000015651462640461400175740ustar00rootroot00000000000000/* * Copyright (C) 2018 HarpyWar (harpywar@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32 #include LONG WINAPI unhandled_handler(struct _EXCEPTION_POINTERS* e); #endif pvpgn-1.99.7.2.1+dfsg/src/win32/winmain.cpp000066400000000000000000000557611462640461400201150ustar00rootroot00000000000000/* * Copyright (C) 2001 Erik Latoshek [forester] (laterk@inbox.lv) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32_GUI #pragma warning(disable : 4047) #include "common/setup_before.h" #include "winmain.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "common/addr.h" #include "common/eventlog.h" #include "common/list.h" #include "common/version.h" #include "common/gui_printf.h" #include "bnetd/connection.h" #include "bnetd/account.h" #include "bnetd/account_wrap.h" #include "bnetd/ipban.h" #include "bnetd/message.h" #include "bnetd/server.h" #include "bnetd/cmdline.h" #include "resource.h" #include "console_output.h" #include "common/setup_after.h" #define WM_SHELLNOTIFY (WM_USER+1) extern int app_main(int argc, char **argv); /* bnetd main function in bnetd/main.c */ namespace pvpgn { extern HWND ghwndConsole; namespace bnetd { extern int server_main(int argc, char *argv[]); static void guiThread(void*); static void guiAddText(const char *, COLORREF); static void guiAddText_user(const char *, COLORREF); static void guiDEAD(const std::wstring& msg); static void guiMoveWindow(HWND, RECT*); static void guiClearLogWindow(); static void guiKillTrayIcon(); long PASCAL guiWndProc(HWND, UINT, WPARAM, LPARAM); static void guiOnCommand(HWND, int, HWND, UINT); static void guiOnMenuSelect(HWND, HMENU, int, HMENU, UINT); static int guiOnShellNotify(int, int); static BOOL guiOnCreate(HWND, LPCREATESTRUCT); static void guiOnClose(HWND); static void guiOnSize(HWND, UINT, int, int); static void guiOnPaint(HWND); static void guiOnCaptureChanged(HWND); static BOOL guiOnSetCursor(HWND, HWND, UINT, UINT); static void guiOnMouseMove(HWND, int, int, UINT); static void guiOnLButtonDown(HWND, BOOL, int, int, UINT); static void guiOnLButtonUp(HWND, int, int, UINT); static void guiOnServerConfig(); static void guiOnAbout(HWND); static void guiOnUpdates(); static void guiOnAnnounce(HWND); static void guiOnUserStatusChange(HWND); BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK AnnDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK KickDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam); #define MODE_HDIVIDE 1 #define MODE_VDIVIDE 1 struct gui_struc { HWND hwnd; HMENU hmenuTray; HWND hwndUsers; HWND hwndUserCount; HWND hwndUserEditButton; HWND hwndTree; int y_ratio; int x_ratio; HANDLE event_ready; BOOL main_finished; int mode; char szDefaultStatus[128]; RECT rectHDivider, rectVDivider, rectConsole, rectUsers, rectConsoleEdge, rectUsersEdge; WPARAM wParam; LPARAM lParam; }; static struct gui_struc gui; char selected_item[255] = {}; int fprintf(FILE *stream, const char *format, ...) { va_list args; va_start(args, format); if (stream == stderr || stream == stdout) { char buf[1024] = {}; std::vsnprintf(buf, sizeof buf, format, args); gui_lvprintf(eventlog_level_error, "{}", buf); return 1; } else return vfprintf(stream, format, args); } static void guiThread(void *param) { HMODULE hRichEd = LoadLibraryW(L"RichEd20.dll"); if (hRichEd == nullptr) guiDEAD(L"Could not load RichEd20.dll"); WNDCLASSEXW wc = {}; wc.cbSize = sizeof(WNDCLASSEXW); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = static_cast(guiWndProc); wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = static_cast(param); wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_ICON1)); wc.hCursor = LoadCursorW(nullptr, IDC_ARROW); wc.hbrBackground = nullptr; wc.lpszMenuName = MAKEINTRESOURCEW(IDR_MENU); wc.lpszClassName = L"BnetdWndClass"; wc.hIconSm = nullptr; if (!RegisterClassExW(&wc)) { FreeLibrary(hRichEd); guiDEAD(L"cant register WNDCLASS"); } gui.hwnd = CreateWindowExW( 0, wc.lpszClassName, L"Player -vs- Player Gaming Network Server", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, static_cast(param), nullptr); if (!gui.hwnd) { FreeLibrary(hRichEd); guiDEAD(L"cant create window"); } ShowWindow(gui.hwnd, SW_SHOW); SetEvent(gui.event_ready); MSG msg = {}; while (GetMessageW(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } FreeLibrary(hRichEd); } long PASCAL guiWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { gui.wParam = wParam; gui.lParam = lParam; switch (message) { HANDLE_MSG(hwnd, WM_CREATE, guiOnCreate); HANDLE_MSG(hwnd, WM_COMMAND, guiOnCommand); HANDLE_MSG(hwnd, WM_MENUSELECT, guiOnMenuSelect); HANDLE_MSG(hwnd, WM_SIZE, guiOnSize); HANDLE_MSG(hwnd, WM_CLOSE, guiOnClose); HANDLE_MSG(hwnd, WM_PAINT, guiOnPaint); HANDLE_MSG(hwnd, WM_SETCURSOR, guiOnSetCursor); HANDLE_MSG(hwnd, WM_LBUTTONDOWN, guiOnLButtonDown); HANDLE_MSG(hwnd, WM_LBUTTONUP, guiOnLButtonUp); HANDLE_MSG(hwnd, WM_MOUSEMOVE, guiOnMouseMove); case WM_CAPTURECHANGED: guiOnCaptureChanged(reinterpret_cast(lParam)); return 0; case WM_SHELLNOTIFY: return guiOnShellNotify(wParam, lParam); } return DefWindowProcW(hwnd, message, wParam, lParam); } static BOOL guiOnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) { ghwndConsole = CreateWindowExW( 0, RICHEDIT_CLASS, nullptr, WS_CHILD | WS_VISIBLE | ES_READONLY | ES_MULTILINE | WS_VSCROLL | WS_HSCROLL | ES_NOHIDESEL, 0, 0, 0, 0, hwnd, 0, 0, nullptr); if (!ghwndConsole) return FALSE; gui.hwndUsers = CreateWindowExW( WS_EX_CLIENTEDGE, L"LISTBOX", nullptr, WS_CHILD | WS_VISIBLE | LBS_STANDARD | LBS_NOINTEGRALHEIGHT, 0, 0, 0, 0, hwnd, 0, 0, nullptr); if (!gui.hwndUsers) return FALSE; //amadeo: temp. button for useredit until rightcklick is working.... gui.hwndUserEditButton = CreateWindowExW( 0L, L"button", L"Edit User Status", WS_CHILD | WS_VISIBLE | ES_LEFT, 0, 0, 0, 0, hwnd, reinterpret_cast(881), 0, nullptr); if (!gui.hwndUserEditButton) return FALSE; gui.hwndUserCount = CreateWindowExW( WS_EX_CLIENTEDGE, L"edit", L" 0 user(s) online:", WS_CHILD | WS_VISIBLE | ES_CENTER | ES_READONLY, 0, 0, 0, 0, hwnd, 0, 0, nullptr); if (!gui.hwndUserCount) return FALSE; SendMessageW(gui.hwndUserCount, WM_SETFONT, reinterpret_cast(GetStockObject(DEFAULT_GUI_FONT)), 0); SendMessageW(gui.hwndUsers, WM_SETFONT, reinterpret_cast(GetStockObject(DEFAULT_GUI_FONT)), 0); SendMessageW(gui.hwndUserEditButton, WM_SETFONT, reinterpret_cast(GetStockObject(DEFAULT_GUI_FONT)), 0); BringWindowToTop(gui.hwndUsers); std::snprintf(gui.szDefaultStatus, sizeof(gui.szDefaultStatus), "%s", "Void"); gui.y_ratio = (100 << 10) / 100; gui.x_ratio = (0 << 10) / 100; return TRUE; } static void guiOnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDM_EXIT: guiOnClose(hwnd); break; case IDM_SAVE: server_save_wraper(); break; case IDM_RESTART_LUA: server_restart_wraper(restart_mode_lua); break; case IDM_RESTART: server_restart_wraper(restart_mode_all); break; case IDM_SHUTDOWN: server_quit_wraper(); break; case IDM_CLEAR: guiClearLogWindow(); break; case IDM_RESTORE: guiOnShellNotify(IDI_TRAY, WM_LBUTTONDBLCLK); break; case IDM_USERLIST: guiOnUpdateUserList(); break; case IDM_SERVERCONFIG: guiOnServerConfig(); break; case IDM_ABOUT: guiOnAbout(hwnd); break; case ID_HELP_CHECKFORUPDATES: guiOnUpdates(); break; case IDM_ANN: guiOnAnnounce(hwnd); break; case ID_USERACTIONS_KICKUSER: case 881: guiOnUserStatusChange(hwnd); break; } } static void guiOnMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags) { // char str[256]; //by greini /*if( item == 0 || flags == -1) SetWindowText( gui.hwndStatus, gui.szDefaultStatus ); else { LoadString( GetWindowInstance(hwnd), item, str, sizeof(str) ); if( str[0] ) SetWindowText( gui.hwndStatus, str ); }*/ } static int guiOnShellNotify(int uID, int uMessage) { if (uID == IDI_TRAY) { if (uMessage == WM_LBUTTONDBLCLK) { if (!IsWindowVisible(gui.hwnd)) ShowWindow(gui.hwnd, SW_RESTORE); SetForegroundWindow(gui.hwnd); } else if (uMessage == WM_RBUTTONDOWN) { POINT cp; GetCursorPos(&cp); SetForegroundWindow(gui.hwnd); TrackPopupMenu(gui.hmenuTray, TPM_LEFTALIGN | TPM_LEFTBUTTON, cp.x, cp.y, 0, gui.hwnd, NULL); } } return 0; } static void guiOnPaint(HWND hwnd) { PAINTSTRUCT ps; HDC dc; dc = BeginPaint(hwnd, &ps); DrawEdge(dc, &gui.rectHDivider, BDR_SUNKEN, BF_MIDDLE); DrawEdge(dc, &gui.rectConsoleEdge, BDR_SUNKEN, BF_RECT); DrawEdge(dc, &gui.rectUsersEdge, BDR_SUNKEN, BF_RECT); EndPaint(hwnd, &ps); UpdateWindow(ghwndConsole); UpdateWindow(gui.hwndUsers); UpdateWindow(gui.hwndUserCount); UpdateWindow(gui.hwndUserEditButton); } static void guiOnClose(HWND hwnd) { guiKillTrayIcon(); if (!gui.main_finished) { eventlog(eventlog_level_debug, __FUNCTION__, "GUI wants server dead..."); std::exit(0); } else { eventlog(eventlog_level_debug, __FUNCTION__, "GUI wants to exit..."); eventlog_close(); SetEvent(gui.event_ready); } } static void guiOnSize(HWND hwnd, UINT state, int cx, int cy) { if (state == SIZE_MINIMIZED) { NOTIFYICONDATAW dta = {}; dta.cbSize = sizeof(NOTIFYICONDATAW); dta.hWnd = hwnd; dta.uID = IDI_TRAY; dta.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; dta.uCallbackMessage = WM_SHELLNOTIFY; dta.hIcon = LoadIconW(GetWindowInstance(hwnd), MAKEINTRESOURCE(IDI_ICON1)); std::swprintf(dta.szTip, sizeof dta.szTip / sizeof *dta.szTip, L"%ls %ls", PVPGN_SOFTWAREW, PVPGN_VERSIONW); Shell_NotifyIconW(NIM_ADD, &dta); ShowWindow(hwnd, SW_HIDE); return; } if (state == SIZE_RESTORED) { NOTIFYICONDATAW dta = {}; dta.hWnd = hwnd; dta.uID = IDI_TRAY; Shell_NotifyIconW(NIM_DELETE, &dta); } int cy_status = 0; int cy_edge = GetSystemMetrics(SM_CYEDGE); int cx_edge = GetSystemMetrics(SM_CXEDGE); int cy_frame = (cy_edge << 1) + GetSystemMetrics(SM_CYBORDER) + 1; int cy_console = ((cy - cy_status - cy_frame - cy_edge * 2) * gui.y_ratio) >> 10; gui.rectConsoleEdge.left = 0; gui.rectConsoleEdge.right = cx - 140; gui.rectConsoleEdge.top = 0; gui.rectConsoleEdge.bottom = cy - cy_status; gui.rectConsole.left = cx_edge; gui.rectConsole.right = cx - 140 - cx_edge; gui.rectConsole.top = cy_edge; gui.rectConsole.bottom = cy - cy_status; gui.rectUsersEdge.left = cx - 140; gui.rectUsersEdge.top = 18; gui.rectUsersEdge.right = cx; gui.rectUsersEdge.bottom = cy - cy_status - 10; gui.rectUsers.left = cx - 138; gui.rectUsers.right = cx; gui.rectUsers.top = 18 + cy_edge; gui.rectUsers.bottom = cy - cy_status - 20; guiMoveWindow(ghwndConsole, &gui.rectConsole); guiMoveWindow(gui.hwndUsers, &gui.rectUsers); MoveWindow(gui.hwndUserCount, cx - 140, 0, 140, 18, TRUE); MoveWindow(gui.hwndUserEditButton, cx - 140, cy - cy_status - 20, 140, 20, TRUE); } static BOOL guiOnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg) { POINT p; if (hwnd == hwndCursor && codeHitTest == HTCLIENT) { GetCursorPos(&p); ScreenToClient(hwnd, &p); if (PtInRect(&gui.rectHDivider, p)) SetCursor(LoadCursorW(0, IDC_SIZENS)); return TRUE; } return FORWARD_WM_SETCURSOR(hwnd, hwndCursor, codeHitTest, msg, DefWindowProcW); } static void guiOnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { POINT p; p.x = x; p.y = y; if (PtInRect(&gui.rectHDivider, p)) { SetCapture(hwnd); gui.mode |= MODE_HDIVIDE; } } static void guiOnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags) { ReleaseCapture(); gui.mode &= ~(MODE_HDIVIDE | MODE_VDIVIDE); } static void guiOnCaptureChanged(HWND hwndNewCapture) { gui.mode &= ~(MODE_HDIVIDE | MODE_VDIVIDE); } static void guiOnMouseMove(HWND hwnd, int x, int y, UINT keyFlags) { int offset, cy_console, cy_users; RECT r; if (gui.mode & MODE_HDIVIDE) { offset = y - gui.rectHDivider.top; if (!offset) return; cy_console = gui.rectConsole.bottom - gui.rectConsole.top; cy_users = gui.rectUsers.bottom - gui.rectUsers.top; if (cy_console + offset <= 0) offset = -cy_console; else if (cy_users - offset <= 0) offset = cy_users; cy_console += offset; cy_users -= offset; if (cy_console + cy_users == 0) return; gui.y_ratio = (cy_console << 10) / (cy_console + cy_users); GetClientRect(hwnd, &r); guiOnSize(hwnd, 0, r.right, r.bottom); InvalidateRect(hwnd, NULL, FALSE); } } static void guiOnUpdates() { ShellExecuteW(nullptr, L"open", L"http://pvpgn.pro/", nullptr, nullptr, SW_SHOW); } static void guiOnAnnounce(HWND hwnd) { DialogBoxW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(IDD_ANN), hwnd, static_cast(AnnDlgProc)); } static void guiOnUserStatusChange(HWND hwnd) { int index; index = SendMessageW(gui.hwndUsers, LB_GETCURSEL, 0, 0); SendMessageA(gui.hwndUsers, LB_GETTEXT, index, reinterpret_cast(selected_item)); DialogBoxW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(IDD_KICKUSER), hwnd, static_cast(KickDlgProc)); SendMessageW(gui.hwndUsers, LB_SETCURSEL, -1, 0); } static void guiOnAbout(HWND hwnd) { DialogBoxW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(IDD_ABOUT), hwnd, static_cast(AboutDlgProc)); } static void guiOnServerConfig() { ShellExecuteW(nullptr, nullptr, L"conf\\bnetd.conf", nullptr, nullptr, SW_SHOW); } extern void guiOnUpdateUserList() { t_connection * c; t_elem const * curr; t_account * acc; SendMessageW(gui.hwndUsers, LB_RESETCONTENT, 0, 0); LIST_TRAVERSE_CONST(connlist(), curr) { if (!(c = (t_connection *)elem_get_data(curr))) continue; if (!(acc = conn_get_account(c))) continue; SendMessageA(gui.hwndUsers, LB_ADDSTRING, 0, reinterpret_cast(account_get_name(acc))); } std::wstring user_count(std::to_wstring(connlist_login_get_length()) + L" user(s) online:"); SendMessageW(gui.hwndUserCount, WM_SETTEXT, 0, reinterpret_cast(user_count.c_str())); } static void guiAddText(const char *str, COLORREF clr) { int text_length = SendMessageW(ghwndConsole, WM_GETTEXTLENGTH, 0, 0); if (text_length > 30000) { CHARRANGE ds = {}; ds.cpMin = 0; ds.cpMax = text_length - 30000; SendMessageW(ghwndConsole, EM_EXSETSEL, 0, reinterpret_cast(&ds)); SendMessageW(ghwndConsole, EM_REPLACESEL, FALSE, 0); } CHARRANGE cr = {}; cr.cpMin = text_length; cr.cpMax = text_length; SendMessageW(ghwndConsole, EM_EXSETSEL, 0, reinterpret_cast(&cr)); CHARFORMATW fmt = {}; fmt.cbSize = sizeof(CHARFORMATW); fmt.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_BOLD | CFM_ITALIC | CFM_STRIKEOUT | CFM_UNDERLINE; fmt.yHeight = 160; fmt.dwEffects = 0; fmt.crTextColor = clr; std::swprintf(fmt.szFaceName, sizeof fmt.szFaceName / sizeof *fmt.szFaceName, L"%ls", L"Courier New"); SendMessageW(ghwndConsole, EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&fmt)); SendMessageA(ghwndConsole, EM_REPLACESEL, FALSE, reinterpret_cast(str)); } static void guiDEAD(const std::wstring& message) { wchar_t* error_message = nullptr; FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&error_message), 0, nullptr); wchar_t* nl = std::wcschr(error_message, '\r'); if (nl) *nl = '0'; MessageBoxW(0, std::wstring(message + L"\nGetLastError() = " + error_message).c_str(), L"guiDEAD", MB_ICONSTOP | MB_OK); LocalFree(error_message); std::exit(1); } static void guiMoveWindow(HWND hwnd, RECT* r) { MoveWindow(hwnd, r->left, r->top, r->right - r->left, r->bottom - r->top, TRUE); } static void guiClearLogWindow() { SendMessageW(ghwndConsole, WM_SETTEXT, 0, 0); } static void guiKillTrayIcon() { NOTIFYICONDATAW dta = {}; dta.cbSize = sizeof(NOTIFYICONDATAW); dta.hWnd = gui.hwnd; dta.uID = IDI_TRAY; dta.uFlags = 0; Shell_NotifyIconW(NIM_DELETE, &dta); } BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: EndDialog(hwnd, IDOK); break; } break; default: return FALSE; } return TRUE; } BOOL CALLBACK AnnDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: { int len = GetWindowTextLengthA(GetDlgItem(hwnd, IDC_EDIT1)); if (len > 0) { std::string buff(len, '\0'); GetDlgItemTextA(hwnd, IDC_EDIT1, &buff[0], buff.capacity()); t_message* const message = message_create(message_type_error, nullptr, buff.c_str()); if (message) { message_send_all(message); message_destroy(message); } SetDlgItemTextW(hwnd, IDC_EDIT1, L""); } else { MessageBoxW(hwnd, L"You didn't enter anything!", L"Warning", MB_OK); } break; } } break; case WM_CLOSE: EndDialog(hwnd, IDOK); break; default: return FALSE; } return TRUE; } BOOL CALLBACK KickDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_INITDIALOG: if (selected_item[0] != 0) { SetDlgItemTextA(hwnd, IDC_EDITKICK, selected_item); } return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_KICK_EXECUTE: { GetDlgItemTextA(hwnd, IDC_EDITKICK, selected_item, sizeof selected_item); t_connection* const conngui = connlist_find_connection_by_accountname(selected_item); t_account* const accountgui = accountlist_find_account(selected_item); if (conngui == nullptr) { MessageBoxA(hwnd, std::string(std::string(selected_item) + " could not be found in Userlist!").c_str(), "Error", MB_OK); } else { HWND hButton = GetDlgItem(hwnd, IDC_CHECKBAN); HWND hButton1 = GetDlgItem(hwnd, IDC_CHECKKICK); HWND hButton2 = GetDlgItem(hwnd, IDC_CHECKADMIN); HWND hButton3 = GetDlgItem(hwnd, IDC_CHECKMOD); HWND hButton4 = GetDlgItem(hwnd, IDC_CHECKANN); BOOL messageq = FALSE; BOOL kickq = FALSE; if (SendMessageW(hButton2, BM_GETCHECK, 0, 0) == BST_CHECKED) { account_set_admin(accountgui); account_set_command_groups(accountgui, 255); messageq = TRUE; } if (SendMessageW(hButton3, BM_GETCHECK, 0, 0) == BST_CHECKED) { account_set_auth_operator(accountgui, nullptr, 1); messageq = TRUE; } if (SendMessageW(hButton4, BM_GETCHECK, 0, 0) == BST_CHECKED) { account_set_strattr(accountgui, "BNET\\auth\\announce", "true"); messageq = TRUE; } if (SendMessageW(hButton, BM_GETCHECK, 0, 0) == BST_CHECKED) { char temp[64]; std::snprintf(temp, sizeof temp, "%s", addr_num_to_addr_str(conn_get_addr(conngui), 0)); char ipadr[110]; unsigned int i_GUI; for (i_GUI = 0; temp[i_GUI] != ':'; i_GUI++) ipadr[i_GUI] = temp[i_GUI]; ipadr[i_GUI] = 0; std::strcpy(temp, " a "); std::strcat(temp, ipadr); handle_ipban_command(nullptr, temp); temp[0] = 0; std::strcpy(temp, " has been added to IpBanList"); std::strcat(ipadr, temp); if (messageq == TRUE) { std::strcat(ipadr, " and UserStatus changed"); MessageBoxA(hwnd, ipadr, "ipBan & StatusChange", MB_OK); messageq = FALSE; kickq = FALSE; } else MessageBoxA(hwnd, ipadr, "ipBan", MB_OK); } if (SendMessageW(hButton1, BM_GETCHECK, 0, 0) == BST_CHECKED) { conn_set_state(conngui, conn_state_destroy); kickq = TRUE; } if ((messageq == TRUE) && (kickq == TRUE)) { std::strcat(selected_item, "has been kicked and Status has changed"); MessageBoxA(hwnd, selected_item, "UserKick & StatusChange", MB_OK); } if ((kickq == TRUE) && (messageq == FALSE)) { std::strcat(selected_item, " has been kicked from the server"); MessageBoxA(hwnd, selected_item, "UserKick", MB_OK); } if ((kickq == FALSE) && (messageq == TRUE)) { std::strcat(selected_item, "'s Status has been changed"); MessageBoxA(hwnd, selected_item, "StatusChange", MB_OK); } selected_item[0] = 0; } break; } } break; case WM_CLOSE: EndDialog(hwnd, IDC_EDITKICK); break; default: return FALSE; } return TRUE; } } } using namespace pvpgn; using namespace pvpgn::bnetd; int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE reserved, LPSTR lpCmdLine, int nCmdShow) { Console console; if (cmdline_load(__argc, __argv) != 1) { return -1; } if (cmdline_get_console()) { console.RedirectIOToConsole(); return app_main(__argc, __argv); } pvpgn::bnetd::gui.main_finished = FALSE; pvpgn::bnetd::gui.event_ready = CreateEventW(nullptr, FALSE, FALSE, nullptr); _beginthread(pvpgn::bnetd::guiThread, 0, (void*)hInstance); WaitForSingleObject(pvpgn::bnetd::gui.event_ready, INFINITE); auto result = app_main(__argc, __argv); pvpgn::bnetd::gui.main_finished = TRUE; eventlog(pvpgn::eventlog_level_debug, __FUNCTION__, "server exited ( return : {} )", result); WaitForSingleObject(pvpgn::bnetd::gui.event_ready, INFINITE); return 0; } #endif pvpgn-1.99.7.2.1+dfsg/src/win32/winmain.h000066400000000000000000000016741462640461400175540ustar00rootroot00000000000000/* * Copyright (C) 2001 Erik Latoshek [forester] (laterk@inbox.lv) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32_GUI #ifndef __WINMAIN_H__ #define __WINMAIN_H__ namespace pvpgn { namespace bnetd { extern void guiOnUpdateUserList(void); } } #endif #endif pvpgn-1.99.7.2.1+dfsg/version-history.txt000066400000000000000000000407651462640461400201410ustar00rootroot00000000000000PvPGN Version History: Changelog for SVN TRUNK (since 1.8.5) ========================================= -- The list of changes mentioned in here is most probably incomplete. For more details also take a peek view into conversion-changelog. [CMAKE] -- added cmake build system support [WOL] -- completed support for all WOL clients (read IGNOREME.TXT) -- disable account creation via irc/wol when new_accounts=false -- added support to allow/deny WOL clients in bnetd.conf -- added support for battle clans -- added various protocol related fixes -- added apiregister support -- added autoupdate support for WOLv2 clients -- added variables into bnetd.conf for wol FTP update server specification -- fixed voice flag not shown for clients in channel -- fixed bug in RedAlert2/Yuri when game owner changes maximum players -- added QuickMatch support for RA2/YURI -- added Ladders support for Tiberian Sun, Firestorm, Red Alert 2 and Yuri's Revenge clients [WAR3] -- added support for SRP3 based account creation/login -- added support for passchange for SRP3 based accounts [MISC] -- added /muteacct and /unmuteacct commands for admins -- added /clan whisper subcommand -- added /clan create/invite/disband subcommands -- added clan_min_invites to bnetd.conf for miminum accepted invitations need in clan-creating process (default is 2) -- added support for multilingual motd -- added /ipscan command for admins ChangeLog for 1.8.5 (since 1.8.4) ========================================= [VCHECK] -- added STAR/SEXP IX86 1.16 and 1.16.1 vcheck entries [WAR3] -- fixed bug when saved games could not be loaded -- fixed bug when clanmember can promote/demote any member of his clan by sending proper rankupdate packet -- fixed bug when a hosting bot was able to create a custom game which caused clients to disconect [MISC] -- updated IX86 autoupdate according to STAR/SEXP 1.16.1 ChangeLog for 1.8.4 (since 1.8.3) ========================================= [WAR3] -- fixed possible server crash on many ill-formated packets [SQL] -- fixed MySQL reconnection issues for MySQL >= 5.0.19 [IRC/WOL] -- fixed bug when client does not got proper information when account is already in use [MISC] -- fixed bug when Voiced client was not possible to talk in moderated channels ChangeLog for 1.8.3 (since 1.8.2) ========================================= [WOL] -- added Dune 2000 Support -- fixed malformed time format in startgame message -- fixed log warnings/errors rising in login process [MISC] -- fixed malformed output by /cg list command [VCHECK] -- added WAR3/W3XP IX86 1.21b vcheck entries -- added STAR/SEXP IX86 1.51.2 vcheck entries -- rewrited all STAR/SEXP/W2BN/DRTL IX86 vcheck entries to match new ver-ix86-1.mpq file (this fixes connecting problems on Windows Vista systems) ChangeLog for 1.8.2 (since 1.8.1) ========================================= [WOL] -- fixed possible server crash on raw list command [SC/BW] -- fixed client crash when was showed Friends without any item [VCHECK] -- added SC/BW IX86 and XMAC 1.15.1 version check entries ChangeLog for 1.8.1 (since 1.8.0) ========================================= [WOL] -- fixed bug trying to join unexistent games -- fixed bug overflowing the max players game limit -- fixed bug of not showing Yuri's revenge channels -- added address translation support to servserv sent addresses -- added support for usage of /commands [WAR3] -- fixed possible server crash on ill-formated clan invitation reply packet -- properly display tourney wins/losses in account profile [MISC] -- fixed d2dbs accepting d2gs connections from any ip -- fixed IRC /notice disc bug -- fixed warning and reactivated reconnecting when using mysql>=5.0.13 -- fixed SC:BW string representation from "StarCraft: Broodwars" to "StarCraft: Brood War" (to better match battle.net behaviour) -- fixed possible server crash caused by /ipban command -- enforce (stricter) limit for maximum channel name length -- fixed problems caused by login via uid -- added new "account_force_username" option to bnetd.conf -- fixed malformed tracker packets on 64bit machines [VCHECK] -- added WAR3/W3XP IX86 1.21 vcheck entries -- added sc/bw IX86 and XMAC 1.15 version check entries ChangeLog for 1.8.0 (since 1.8.0rc2) ========================================= [VCHECK] -- added sc/bw 1.13f/1.14 version check entries -- added WAR3/W3XP IX86 1.20d/1.20e vcheck entries [D2] -- cleaned up some mess about dealing with D2 1.08 auth reply packets which we don't need to support according to our minimum D2 version required -- fixed joining of D2 channels by non-D2 clients [WOL] -- fixed a bug where players couldn't join newly created channels [MISC] -- fixed logging of invalid strings for client tags -- fixed /ladderinfo command, fix usage of uninitilized variables -- fixed corruption of account data when using files storage and when there where already broken UID accounts -- fixed the clan max members to be 100 not 255 -- fixed loading of all accounts in sql2 (ex. when doing a ladder rebuild) -- reverted dynamic SQL lib support that was creating problems -- properly fixed "sql" ("sql2" is not affected) friend list bugs (reverted the old fix which was creating new problems); might still be problems if using old versions of sqlite3 (that dont support ` quoting) or using ODBC -- fixed connection loss problems with mysql>=5.0.12 (mysql versions starting with 5.0.3 but lower than 5.0.12 will still have the issue, upgrade mysql!) Changelog for 1.8.0rc2 (since 1.8.0rc1) ========================================= [SQL] -- fixed friendslist-related (my)SQL problems [VERSIONCHECK] -- added W3 1.20c entries [MISC] -- updated bnmaps.conf with the latest Battle.net maps ChangeLog for 1.8.0rc1 (since 1.7.9) ======================================= [SQL] -- inserted additional indices for BNET table in sql2 layout -- added runtime loading of shared library files (DLLs) [SC/BW/W2BNE] -- fixed problem with parsing gameresults with unexpected order of players [WAR3] -- fixed "clan membership lost after invitation" bug. [IRC] -- fixed PING problem occouring after about 25 days uptime [VERSIONCHECK] -- added SC/BW 1.13e entries (XMAC & PMAC) -- added D2/D2XP 1.11b entries -- added W3 1.20b entries [MISC] -- enforce limit for topic message max length ChangeLog for 1.7.9 (since 1.7.8) ======================================= [D2CS/D2DBS] -- added pid files to the confs -- fixed a bug where server could not be (un)installed as a win32 service -- added check and modified the default motd to not have a too big motd that was crashing the clients just after character selection [VERSIONCHECK] -- added SC/BW 1.13c entries -- added SC/BW 1.13d entries -- added SC/BW 1.13e entries -- added W3 1.20a entries [SQL] -- reorganized optimized and made more safe the existent codes -- added a new sql layout called "sql2" in config [MISC] -- fixed security issue where an op of a channel was able to set the topic of any other channel -- added more information to "/finger" display (if issued by an admin) -- added a new command, "/tos" to display the Terms Of Service on request ChangeLog for 1.7.8 (since 1.7.7) ======================================= [D2CS/D2DBS] -- usage of the new conf API [SQL] -- fixed a bug where case sensitive string lookup SQL backends whould not see old accounts when searched with different case; this is a major problem and if you are running any non-MySQL SQL backend you MUST upgrade; make sure you read UPDATE too as it contains very important information for this bugfix [WOL] -- initial support (HURRAY!!); almost complete Tiberian Sun (TA) support, basic Red Alert 2 (RA2) and Yuri's Revenge (YR) support [DRTL] -- fixed problem with joining games of level 50 characters [BNPCAP] -- 64 bit arch compatibility changes [BNCHAT] -- added --arch parameter to specify architecture -- added --lang parameter to specify "gamelang" field of COUNTRYINFO packet [MISC] -- changed that if "servaddrs" option in bnetd.conf is commented server should not listen on bnet addresses (now that we have WOL b.net is not a priority anymore) -- optimized some "compat" codes -- updated more places where people get directed to IRC with the other support channels (should reduce the "n00b" infestation that PvPGN IRC suffers of) -- added versioncheck for D2 LOD 1.11 and D2 classic 1.11 -- updated versioncheck with latest client versions (War3 1.18a, SC/BW 1.13 and 1.13b) -- updated bnmaps.conf with latest maps used on b.net ChangeLog for 1.7.7 (since 1.7.6) =================================== [STORAGE] -- fixed account corruption bug happening when trying to register new accounts with existent accounts usernames and using plain/cdb file storage Changelog for 1.7.6 (since 1.7.5) =================================== [MISC] -- renamed old config variable 'servername' to 'hostname' in bnetd.conf -- added 'servername' config variable in bnetd.conf for server identification -- expanded bnmotd.conf to use 'servername' -- made the hardcoded WAR3 motd use 'servername' -- fixed friends join/leave whispers to come from 'servername' -- fixed bug where d2cs was not able to set realmname even when 'allow_d2cs_setname' was set to 'true' in bnetd.conf [STORAGE] -- updated sql_DB_layout.conf and the code handling it -- SQL storage types now use a unique index for 'acct_username' (speedup) -- added sqlite3 storage support -- added ODBC storage support [DRTL] -- fixed handling of gametypes (character levels) Changelog for 1.7.5 (since 1.7.4) =================================== [BNCHAT] -- fixed win32 specific problem (can't read while writing) [D2CS] -- reduce enforced maxchar value down to 18 (fixing possible problems when having many chars with long charnames) [WAR3] -- wins required for specific chat icons now also configurable via anongame_infos.conf (previously only affected profile icons) -- discard games with no winners at all [SC/BW/W2BNE] -- fixed parsing of ladder_games option in bnetd.conf Changelog for 1.7.4 (since 1.7.3) =================================== [SC/BW/W2BNE] -- write a message to creators/joiners of ladder games of alternative types (those specified with "ladder_games" in bnetd.conf) -- adder "ladder_prefix" to allow only matching game names to be counted as ladder games for the alternative ladder game types -- fixed ELO calculation for top-vs-bottom team ladder games [D2DBS] -- changed codes to use the common network API for send/recv on sockets and error checking thus hopefully fixing bugs of not ignoring properly some errors [BNETD] -- made difficulty of a closed D2 game known to bnetd -- various clan related bugfixes -- enforced max password length (was creating problems in some cases with very long passwords) [MISC] -- lots of optimizations (lowers CPU footprint) -- improved win32 winsock error reporting -- fixed many clan code related crashes -- optimized plain file account loading Changelog for 1.7.3 (since 1.7.2) =================================== [D2CS/D2DBS] -- fixed problem with d2cs and d2dbs not exiting when exit signal is received and exit_time has expired [FDWATCH] -- fixed a bug which created problems with some backends because of reused sockets not being cleaned properly [STAR/SEXP/W2BN] -- fixed problem with topvbot "team game"-results being discarded -- fixed possible overflow with malformed gamereport packets [D2DV/D2XP] -- added workarround for D2 classic client side bug (open bnet games being listed as closed bnet games) [MISC] -- made topics.conf being reloaded on SIGHUP -- made '/games all' display clienttag of a game -- fixed problem with language specific channels with limited user count Changelog for 1.7.2 (since 1.7.1) =================================== [WIN32 GUI] -- fixed problem with running d2cs & d2dbs as services (gui versions) [D2CS] -- The backup copy of the charsave file and charinfo file are now deleted when client deletes one of thier charactars [MISC] -- fixed problem with language specific channels -- added UNIX build configure options to disable bnetd/d2cs/d2dbs builds -- added "sync_on_logoff" bnetd.conf directive to enable saving of account data on user's logoff -- fixed crash with short/broken cdb user files -- fixed trying to load invalid usernames (like "") ChangeLog for 1.7.1 (since 1.7.0) =================================== [FDWATCH] -- redesigned and rewritten most of the fdwatch codes (the new codes are now called fdwatch2) to better support OS which have sparse values for sockets (ex. this should help busy WIN32 systems) [STAR/SEXP/W2BN] -- fixed problem with topvbot game-results being discarded [DEV-C++] -- fixed compilation problems [D2XP/D2DV] -- added "Diablo II Level 100 Workaround" for mods with a max level greater than 99. Option in d2dbs.conf to activate. ChangeLog for 1.7.0 (since 1.6.2) =================================== [D2CS] -- made ordering of chars on char-selection screen configurable -- fixed "d2cs not trying to reconnect" issue -- made sure maxchar limit for an account is enforced -- added WIN32 GUI feature [D2DBS] -- fixed "deleted a ladder char and created one with the same name breaks the ladder" problem. If your ladder is broken, delete it. If your players want their chars to stay in ladder they shouldn't delete them cause new chars with same name will remove the old entry -- added WIN32 GUI feature [STAR/SEXP/W2BN]: -- changed handling of gameresults to be democratic -- fixed problem with win/games ladder [STAR/SEXP] : fixed "v1.11 can't play ladder games"-problem [IRC]: major cleanup of handle_irc.c -- hiding IP on channel join/leave to non admins when hide_addr=true -- now on IRC channel topics are supported (at least their output) -- PvPGN can only handle being in one channel at once, so the server now makes sure you leave the channel you were previously in. -- added /NOTICE, /ISON and /WHOIS handling -- now the real MOTD displays when connecting via IRC -- allow changing /nick before login -- allow another try on a wrong /msg nickserv identify -- added passfail count to IRC connections to prevent bruteforce logins [D2] -- added the option to supply norm, night, hell to /games d2xp command for finding a game of a specific difficulty. [WAR3]: -- droped <1.13 support, added 1.15 support (CG,PG,AT, clan profile view) -- added code to detect the right w3routeip if is set to 0.0.0.0 -- redone major parts of the AT code (old AT teams will be lost) [LADDERS]: -- added .dat/.xml ladder output for STAR/SEXP/W2BN rating based ladder -- moved textual ladder files from ladderdir to statusdir [PREFS] -- fixed handling of comments (previously it skipped the whole line no matter where '#' was) [FDWATCH] -- removed useless template usage because events field is r/o for pollfd structs [MISC]: -- updated autoupdate with latest clients versions -- fixed "message_cache_lookup: unsupported connection class" errors when broadcasting message (ex /ann) and having any connection different than irc/telnet/bot/bnet (ex d2cs etc) -- added client, version and country info to the /finger command -- added clan and clan rank info to the /finger command -- added option to bnmotd.txt to display info about new mail -- adjusted /whois command to be more like on battle.net -- massivly renamed configuration files so each has an extension -- added scripts/pvpgn_hash.inc.php so everyone who needs has a php- implementation of the commonly used passhash algorithm -- fixed problem with topaz-chat (and maybe other clients, too) not propperly displaying tmpOP on initial channel join -- adjusted friends/watch game/server join/leave message to be more like b.net (changed message type from info to whisper) -- fixed channel behaviour to now really allow a common channel for all clients (different shortname, same special name) -- changed all 4 bytes tags (clienttags, archtags, etc) to use internally integers for comparations instead of strings -- changed almost every place where sending username to send the logged in username (if differs in case) than the account username (this fixes some bugs like modifying some account data when logged in with different case and also gets us to closer Battle.net behaivour) -- added /moderate command to moderate/unmoderate a channel